GLSL着色器:在超过两个纹理之间进行插值

8

我已经在OpenGL中实现了高度图。目前它只是一个正弦/余弦曲线的地形。 目前,我正在白色“冰”和较暗的“石头”纹理之间进行插值。 操作如下:

color = mix(texture2D(ice_layer_tex, texcoord), texture2D(stone_layer_tex, texcoord), (vertex.y + amplitude) / (amplitude * 2))

结果如下:

结果:

从上到下

从下到上

这个方法运行正常,但是如果我想添加更多的纹理,比如草地纹理,使插值顺序为“冰、石头、草地”该怎么办? 我认为没有像mix(sampler2D[], percentages[])这样的函数? 我该如何编写遵循此逻辑的GLSL方法?

3个回答

29

mix() 只是一个方便函数,你可以很容易地自己编写。其定义如下:

mix(v1, v2, a) = v1 * (1 - a) + v2 * a

换句话说,它计算了v1v2的加权平均值,其中有两个权重w1w2,它们是介于0.0和1.0之间的浮点值,满足约束条件w1 + w2 = 1.0

v1 * w1 + v2 * w2
你可以直接将此推广至计算两个以上输入的加权平均数。例如,对于三个输入值 v1v2v3,你需要使用三个权重 w1w2w3,满足约束条件 w1 + w2 + w3 = 1.0,并按以下方式计算加权平均数:
v1 * w1 + v2 * w2 + v3 * w3

对于你的例子,确定你想要为这3个纹理使用的权重,然后使用类似以下的代码:

weightIce = ...;
weightStone = ...;
weightGrass = 1.0 - weightIce - weightStone;
color = texture2D(ice_layer_tex, texcoord) * weightIce +
        texture2D(stone_layer_tex, texcoord) * weightStone +
        texture2D(grass_layer_tex, texcoord) * weightGrass;

嗨,我知道这是一个旧帖子,但你能否请说明如何计算N个值的权重? - Anas iqbal
你说“满足约束条件w1 + w2 = 1.0”,但是GLSL v4.6规范(§8.3,p148)并没有将其作为要求。虽然我不想过于追究细节,但这确实是该函数的出色解释。 - AnOccasionalCashew

3
其他答案已经提供了您所要求的通用mix()函数的解决方案。但是,我建议使用不同的方法,因为您明确写到了“插值顺序(冰、石头、草)”。在这种情况下,您不需要为每个元素设置任意权重,只需混合相邻的元素,比如冰+石头或石头+草,但从不混合冰+草或冰+石头+草。如果是这种情况,您可以简单地使用3D纹理并使用(三)线性过滤。只需将您的2D纹理中的每个纹理作为3D纹理中的一个切片使用即可。前两个纹理坐标可以保持不变,第三个纹理坐标可以直接用于选择两个相邻切片之间的任意混合。由于纹理坐标始终在[0,1]范围内,因此您只需将您的范围映射到该区间即可。第i个切片的“中心”将位于
p=i/num_layers + 1/(2*num_layers)

假设你有三个片段,分别代表冰、石头和草。那么你会得到:
0/3+1/6 = 0.16667       100% ice  
1/3+1/6 = 0.5           100% stone
2/3+1/6 = 0.83333       100% grass

并且在相邻的层之间进行任意线性混合,就像在它们之间。
1/3 = 0.3333            50% ice + 50% stone  
      0.6               70% stone  + 30% grass
...

哎呀,我不知道如何实现或使用3D纹理...懒得Google,你能发一些示例代码吗?^^它不必包含太多解释,但是...是的...:D - T_01
2
@T_01 如果你“太懒得谷歌”,那么你永远不会从Stack Overflow社区获得任何答案或帮助。SO社区期望你首先搜索Google,其次搜索现有的SO答案,最后在SO上提问——在你完成前两个步骤之后。勤奋是这里(以及整个编程领域)的一种美德,而懒惰将无法让你在SO专家或整个编程职业中走得太远。 - Slipp D. Thompson

2
不行,根据GLSL mix() 的文档,只有两个参数之间的插值重载。您是否接受仅在“ice”和“stone”之间进行插值,然后将结果与“grass”纹理混合?
vec4 ice_color   = texture2D(ice_layer_tex,   texcoord);
vec4 stone_color = texture2D(stone_layer_tex, texcoord);
vec4 grass_color = texture2D(grass_layer_tex, texcoord);

vec4 tmp = mix(ice_color, stone_color, pct);
vec4 final_color = mix(tmp, grass_color, pct);

我试过了,但那不是我想要的东西。看看Reto的答案,那很完美 :)无论如何谢谢你 ^^ - T_01

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接