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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/24356075/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-14 11:44:53  来源:igfitidea点击:

GLSL shader: Interpolate between more than two textures

javaopengllwjglinterpolationheightmap

提问by T_01

I've implemented a heightmap in OpenGL. For now it is just a sine/cosine curved terrain. At the moment I am interpolating between the white "ice" and the darker "stone" texture. This is done like this:

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

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

The result:

结果:

from top

自上而下

from bottom

从底部

It works fine, but what could I do if I want to add more textures, for example a grass texture, so that the interpolation order is "ice, stone, grass"? I think, there isn't a function like mix(sampler2D[], percentages[])? How could I write a GLSL method following this logic?

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

采纳答案by Reto Koradi

mix()is really just a convenience function for something you can easily write yourself. The definition is:

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

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

Or putting it differently, it calculates a weighted average of v1and v2, with two weights w1and w2that are float values between 0.0 and 1.0 meeting the constraint w1 + w2 = 1.0:

或将其不同地,它计算的加权平均v1v2,用两个加权w1w2是满足约束0.0和1.0之间的浮点值w1 + w2 = 1.0

v1 * w1 + v2 * w2

You can directly generalize this to calculate a weighted average of more than 2 inputs. For example, for 3 inputs v1, v2and v3, you would use 3 weights w1, w2and v3meeting the constraint w1 + w2 + w3 = 1.0, and calculate the weighted average as:

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

v1 * w1 + v2 * w2 + v3 * w3

For your example, determine the weights you want to use for each of the 3 textures, and then use something like:

对于您的示例,确定要用于 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;

回答by glampert

No, according to the GLSL documentation for mix()there are only overloads for interpolation between two parameters.

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

Would it be acceptable to you to just interpolate "ice" and "stone" then mix the result with the "grass" texture?

您是否可以只插入“冰”和“石头”,然后将结果与“草”纹理混合?

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

回答by derhass

The other answers have already provided solutions for the genralized mix()function you asked for. But I'd recommend using a different approach, since you explicitely wrote about an "interpolation order (ice, stone, grass)". In that case, you don't need arbitrary weights for each element, you only mix neighboring ones, like ice+stone or stone+grass, but never ice+grass or ice+stone+grass. If that is the case, you can simply use 3D textures and use (tri)linear filtering. Just use each of your 2D texture as a slice in the 3D texture. The first two texcoords can stay as they are, and the third can be directly used to select an arbitrary blending between two neighboring slices. Since texcoords are always in the range [0,1], you just have to map your range to that interval. The "center" of the i-th slice will lie at

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

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

Say you have those 3 slices for ice, stone and grass. So you get

假设你有冰、石头和草这 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

and arbirtrary linear blends between neighboring layers just inbetween, like

和相邻层之间的任意线性混合,就像

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