我想做的事情:在iOS上使用OpenGL ES 2.0,在片段着色器中使用有符号16位整数进行GPGPU计算。
我已经验证了我已经正确设置了(输入)纹理缓冲区和(输出)帧缓冲区,并且可以通过使用无符号8位指针别名到缓冲区中传递16位整数,将单个字节渲染到帧缓冲区中,并检索字节并在CPU端重新构建正确的16位值。
如果我调用glClearColor,我可以传递像以下这样的值:
然而,我想传入一个包含不同值的数组,所以我设置了一个缓冲区,并传递一个指向由我的有符号16位整数数组成的指针(基本上是将有符号int16_t字节别名到无符号字节)的指针,而不是使用glClearColor。 然后,我可以将它们呈现到帧缓冲区中,并使用glReadPixels获取正确的值,并将int16_t指针别名回CPU上的该数组。但是,使用与上述相同的逻辑,但访问我的纹理而不是帧缓冲区:
使用我的纹理值,我只能在片段着色器中正确重建0到32767之间的值,而不能重建大于32767的值。我需要能够重建任何2 ^ 16个可能的值,无论是有符号还是无符号。为什么我可以从帧缓冲区重建大于32767的值,但无法从我的纹理缓冲区重建呢?
(编辑后添加:更正 - 似乎我无法通过我的纹理传递、渲染和检索所有16位整数...之前出现的负值是使用glClear()初始化的副作用。我通过纹理传递的负值渲染为黑色,并在CPU上重建为零。处理0到32767之间的值是正确的。那么...是否可能传递带符号的int16值(或大于32767的无符号int16值),并在片段着色器内正确重建它们?)
我已经验证了我已经正确设置了(输入)纹理缓冲区和(输出)帧缓冲区,并且可以通过使用无符号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值),并在片段着色器内正确重建它们?)
myIntArray
更改为highp
- 结果相同。有趣的是,从gl_LastFragData
检索到的lowp
值lastFragColor
确实有效。我故意将这些输入值在两种情况下(gl_LastFragData
和myIntArray
)都拉入了highp
工作变量signed_value中,以便后续操作可以保持精度。2)是的-变量名signed_value
打错了(编辑代码进行修复)-尽管如此,在运行代码中这是/是正确的。发现得好。 - bphi