3

我有一个 GLSL 着色器,它从输入纹理的一个通道(例如 R)读取,然后写入输出纹理中的同一通道。该频道必须由用户选择。

我现在能想到的就是使用一个 int 制服和大量的 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)

4

2 回答 2

5

有这样一种方法,它就像你在问题中实际写的一样简单。只需使用t[channelId]. 引用GLSL 规范(这来自版本 3.30,第 5.5 节,但也适用于其他版本):

数组下标语法也可以应用于向量以提供数字索引。所以在

vec4 pos;

pos[2]指 pos 的第三个元素,等价于 pos.z。这允许变量索引到向量中,以及访问组件的通用方式。任何整数表达式都可以用作下标。第一个组件的索引为零。使用具有负值或大于或等于向量大小的常量整数表达式读取或写入向量是非法的。使用非常量表达式进行索引时,如果索引为负数或大于或等于向量的大小,则行为未定义。

请注意,对于代码的第一部分,您使用它来访问纹理的特定通道。您还可以使用ARB_texture_swizzle功能。在这种情况下,您只需使用一个固定的通道,例如r,在着色器中访问,然后混合实际纹理通道,以便您想要访问的任何通道变为r.

更新:由于目标平台原来是 webgl,这些建议不可用。但是,一个简单的解决方案是使用vec4制服代替uChannelID所选组件的 1.0 和所有其他组件的 0.0。假设这个变量被称为uChannelSel。您可以data=dot(t, uChannelSel)在第一部分和gl_FragColor=(vec4(1.0)-uChannelSel) * t + uChannelSel*result第二部分使用。

于 2013-09-19T20:27:45.960 回答
1

我相信你知道,着色器中的分支可能很昂贵。但是,听起来它在一次传递中始终是同一个通道(是吗?),因此您可能会保持足够的凝聚力来看到良好的性能。

自从我使用 GLSL 以来已经有一段时间了,但是如果您使用的是较新的版本,也许您可​​以做一些位移(<< 或 >>)魔术?您可以将纹理读入 int 而不是 vec4,然后根据您要读取的通道将其移动多个位。

于 2013-09-19T20:29:47.617 回答