无法在OpenGL片段着色器中正确检索16位整数

3
我想做的事情:在iOS上使用OpenGL ES 2.0,在片段着色器中使用有符号16位整数进行GPGPU计算。
我已经验证了我已经正确设置了(输入)纹理缓冲区和(输出)帧缓冲区,并且可以通过使用无符号8位指针别名到缓冲区中传递16位整数,将单个字节渲染到帧缓冲区中,并检索字节并在CPU端重新构建正确的16位值。
如果我调用glClearColor,我可以传递像以下这样的值:
glClearColor(0.5, 0.5, 0.5, 0.5);  // endian-agnostic way to initialize buffer with "no data" values (-32640)
glClear(GL_COLOR_BUFFER_BIT);

我正在使用 (0.5, 0.5, 0.5, 0.5) 作为glClearColor的测试值。这应该等同于传递 -32640(有符号)或 32896(无符号)。

在片段着色器中,我可以正确地检索值(作为无符号等效值)。

#extension GL_EXT_shader_framebuffer_fetch : require

varying highp vec2 vTexCoord;
uniform sampler2D myTexture;

void main()
{
    lowp vec4 myIntArray = texture2D( myTexture, vTexCoord);
    // Retrieve value from frame buffer
    lowp vec4 lastFragColor = gl_LastFragData[0];

    // Recover the value (stored in alpha (high byte) and red (low byte)
    highp float signed_value = lastFragColor.a;
    signed_value = signed_value*255.0*256.0 + lastFragColor.r*255.0;

    //  ... Do stuff with the recovered value.
    // Here, I simply verify value is recovered correctly (as an unsigned int equivalent):

    // This draws the pixel green, indicating success
    if (signed_value >= 32896.0) {
        gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0);
    }
    else {
        gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
    }

    // But changing the threshold value very slightly (replacing the lines
    // above with those below) draws the pixel red, correctly
    // indicating the condition is not met, because the value is
    // 32896
    if (signed_value >= 32896.01) {
        gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0);
    }
    else {
        gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
    }
}

然而,我想传入一个包含不同值的数组,所以我设置了一个缓冲区,并传递一个指向由我的有符号16位整数数组成的指针(基本上是将有符号int16_t字节别名到无符号字节)的指针,而不是使用glClearColor。 然后,我可以将它们呈现到帧缓冲区中,并使用glReadPixels获取正确的值,并将int16_t指针别名回CPU上的该数组。但是,使用与上述相同的逻辑,但访问我的纹理而不是帧缓冲区:
highp float signed_value = myIntArray.a;
signed_value = value*255.0*256.0 + myIntArray.r*255.0;

使用我的纹理值,我只能在片段着色器中正确重建0到32767之间的值,而不能重建大于32767的值。我需要能够重建任何2 ^ 16个可能的值,无论是有符号还是无符号。为什么我可以从帧缓冲区重建大于32767的值,但无法从我的纹理缓冲区重建呢?
(编辑后添加:更正 - 似乎我无法通过我的纹理传递、渲染和检索所有16位整数...之前出现的负值是使用glClear()初始化的副作用。我通过纹理传递的负值渲染为黑色,并在CPU上重建为零。处理0到32767之间的值是正确的。那么...是否可能传递带符号的int16值(或大于32767的无符号int16值),并在片段着色器内正确重建它们?)

这可能是由于在计算中混合了浮点精度而导致的奇怪问题。如果您将所有内容声明为highp,它是否有效? - samgak
代码应该是这样的吗?(将值更改为有符号值):signed_value = signed_value255.0256.0 + lastFragColor.r*255.0; - samgak
@samgak。1)好问题...我刚试着将变量myIntArray更改为highp - 结果相同。有趣的是,从gl_LastFragData检索到的lowplastFragColor确实有效。我故意将这些输入值在两种情况下(gl_LastFragDatamyIntArray)都拉入了highp工作变量signed_value中,以便后续操作可以保持精度。2)是的-变量名signed_value打错了(编辑代码进行修复)-尽管如此,在运行代码中这是/是正确的。发现得好。 - bphi
1个回答

1
原来片段着色器代码没问题;我看错地方了。我不知道的是,“客户端”将输入夹紧为非负数。虽然有点惊讶,但谜团终于解开了。

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