GLSL着色器:在两个以上纹理之间插值

我在OpenGL中实现了一个高度图。 现在它只是一个正弦/余弦弯曲的地形。 此刻,我在白色“冰”和深色“石头”纹理之间进行插值。 这样做是这样的:

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

结果:

从顶部

从底部

它工作正常,但如果我想添加更多纹理(例如草纹理),插值顺序为“冰,石,草”,我该怎么办? 我认为,没有像mix(sampler2D[], percentages[])这样的函数? 我怎么能按照这个逻辑写一个GLSL方法?

mix()实际上只是一个方便的function,可以轻松自己编写。 定义是:

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

或者换句话说,它计算v1v2的加权平均值,两个权重w1w2是浮点值,介于0.0和1.0之间,符合约束w1 + w2 = 1.0

 v1 * w1 + v2 * w2 

您可以直接对此进行概括,以计算超过2个输入的加权平均值。 例如,对于3个输入v1v2v3 ,您将使用满足约束w1 + w2 + w3 = 1.0 3个权重w1w2v3 ,并将加权平均值计算为:

 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; 

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

 p=i/num_layers + 1/(2*num_layers) 

假设你有冰块,石块和草的3片。 所以你得到了

 0/3+1/6 = 0.16667 100% ice 1/3+1/6 = 0.5 100% stone 2/3+1/6 = 0.83333 100% grass 

和相邻层之间的arbirtrary线性混合,就像在

 1/3 = 0.3333 50% ice + 50% stone 0.6 70% stone + 30% grass ... 

不,根据mix()的GLSL文档,两个参数之间的插值只有重载。

你可以接受插入“冰”和“石头”然后将结果与“草”纹理混合吗?

 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);