在此博客中,我将向您展示我在Vagabond游戏中积极使用的最喜欢的技术:更换调色板。
调色板交换是对纹理调色板的更改。 在本文中,我们使用着色器实现它。 在过去,这是一种有用的技术,可让您在不浪费内存的情况下增加资源的可变性。 今天,它已用于程序生成中以创建新资源。
影像准备
第一步是准备图像以替换调色板。 在位
图中,每个像素都包含一种颜色,但是我们需要在调色板中包含其颜色索引。 因此,我们将图像结构(相同颜色的区域)与真实颜色分开。
实际上,某些图像格式支持此存储方法。 例如,
PNG格式可以保存索引颜色。 不幸的是,即使图像以索引模式保存,许多图像加载库也会创建颜色数组。 这也适用于我使用的SFML库。 在内部,它使用
stb_image ,它会自动“删除调色板”,即 用相应的调色板颜色替换索引。
因此,为避免此问题,您需要分别存储图像和调色板。 图像以灰色阴影记录,每个像素的灰度级与其调色板中的颜色索引相对应。
这是我们期望收到的示例:
为此,我使用了一个使用
Pillow库的小型Python函数:
import io import numpy as np from PIL import Image def convert_to_indexed_image(image, palette_size):
首先,该功能将图像转换为调色板模式。 然后,她将其重新解释为灰度图像。 然后检索调色板。 没什么复杂的,主要工作由Pillow库完成。
着色器
准备好图像后,我们准备编写一个着色器来替换调色板。 有两种将调色板转移到着色器的策略:可以使用纹理或齐次阵列。 我发现使用齐整数组更容易,因此我使用了它。
这是我的着色器,是用GLSL编写的,但是我认为可以轻松地将其转移到另一种用于创建着色器的语言中:
#version 330 core in vec2 TexCoords; uniform sampler2D Texture; uniform vec4 Palette[32]; out vec4 Color; void main() { Color = Palette[int(texture(Texture, TexCoords).r * 255)]; }
我们仅使用纹理来读取当前像素的红色通道。 红色通道是一个介于0到1之间的浮点值,因此我们将其乘以255并将其转换为
int
以获得从0到255的原始灰度级,该灰度级存储在图像中。 接下来,我们使用它从调色板中获取颜色。
本文开头的动画取自游戏中的屏幕截图,其中,我使用以下调色板来更改角色身体的颜色: