我有一个GLSL着色器,它从输入纹理的一个通道(例如R)读取,然后在输出纹理中写入相同的通道。该频道必须由用户选择。
我现在能想到的只是使用int uniform和大量的if语句:
uniform sampler2D uTexture;
uniform int uChannelId;
varying vec2 vUv;
void main() {
//read in data from texture
vec4 t = texture2D(uTexture, vUv);
float data;
if (uChannelId == 0) {
data = t.r;
} else if (uChannelId == 1) {
data = t.g;
} else if (uChannelId == 2) {
data = t.b;
} else {
data = t.a;
}
//process the data...
float result = data * 2; //for example
//write out
if (uChannelId == 0) {
gl_FragColor = vec4(result, t.g, t.b, t.a);
} else if (uChannelId == 1) {
gl_FragColor = vec4(t.r, result, t.b, t.a);
} else if (uChannelId == 2) {
gl_FragColor = vec4(t.r, t.g, result, t.a);
} else {
gl_FragColor = vec4(t.r, t.g, t.b, result);
}
}
有没有办法像t[uChannelId]
这样的词典访问?
或许我应该有4个不同版本的同一个着色器,每个版本处理一个不同的通道,以便我可以避免所有的if语句?
这样做的最佳方式是什么?
编辑:更具体地说,我正在使用WebGL(Three.js)
答案 0 :(得分:4)
有这样一种方式,它就像你在问题中实际写出来一样简单。只需使用t[channelId]
即可。引用GLSL Spec(这是版本3.30,第5.5节,但也适用于其他版本):
数组下标语法也可以应用于向量以提供数字索引。所以在
vec4 pos;
pos[2]
指的是pos的第三个元素,相当于pos.z.这允许变量索引到a 向量,以及访问组件的通用方法。任何整数表达式都可以用作 标。第一个组件是零指数。使用常量读取或写入向量 具有负值或大于或等于矢量大小的值的积分表达式是非法的。 使用非常量表达式进行索引时,如果索引为负数或更大,则行为未定义 大于或等于矢量的大小。
请注意,对于代码的第一部分,您可以使用它来访问纹理的特定通道。您还可以使用ARB_texture_swizzle功能。在这种情况下,您只需使用一个fxied频道(例如r
)来获取着色器中的访问权限,以及实际纹理频道的混音,以便您想要访问的wahtever频道变为r
。
更新:由于目标平台原来是webgl,因此这些建议无法使用。但是,一个简单的解决方案是使用vec4
制服代替uChannelID
,对于所选组件为1.0,对于所有其他组件为0.0。假设此变量名为uChannelSel
。您可以在第一部分使用data=dot(t, uChannelSel)
,在第二部分使用gl_FragColor=(vec4(1.0)-uChannelSel) * t + uChannelSel*result
。
答案 1 :(得分:1)
我确定你知道,着色器中的分支可能很昂贵。然而,听起来它总是在传球中是同一个频道(是吗?),所以你可以保持足够的凝聚力来看到好的表现。
自从我使用了GLSL之后,它已经很好了,但是如果你使用的是更新版本,也许你可以做一些按位移位(<<>>)魔术?你会将纹理读入int而不是vec4,然后根据你想要读取的通道将它移位一些位。