我的Android 2.3.4 Tegra 2设备上的glsl着色器出现问题。
我想做一些计算,在glsl着色器中的片段着色器中创建纹理坐标,并注意到一个问题并尝试将其缩小,从而制作了一个测试程序。我在屏幕上渲染了宽度为256像素到精确到256像素的纹理,保存帧缓冲并逐像素地检查(与源比较)并且渲染正确。根据opengl规范,当我使用从0.0到1.0的纹理坐标时,它应该采样中心的每个像素(0.5 / 256,1.5 / 256,2.5 / 256等),这似乎是正确的。
然后我将x纹理坐标本身写入纹理并检查它们。结果有点偏,我不知道为什么。而不是得到0..255的渐变,我预计我得到1..128,128..255。这意味着跳过值0并且值128出现两次,这意味着它们不是完全线性的。如果值以任何方式舍入,那么对我来说不是那么明显。我还检查了纹理坐标的子像素值,它们是我所期望的。它们的值总是0.5 / 256,应该是半个像素,并确保纹理在像素内正确采样。
同样,用这些坐标渲染的纹理正是我想要的,但坐标本身并不是我所期望的。这对我来说是一个问题,因为我想用计算的纹理坐标进行渲染,虽然我以一种应该正确的方式计算它们,但结果纹理是错误的。我需要这些坐标对像素级别是正确的,我不知道它们为什么会关闭。
有谁知道导致此行为的原因以及我如何纠正?
这是正确渲染纹理的片段着色器:
precision mediump float;
varying vec2 vtex;
uniform sampler2D samp0;
void main() {
gl_FragColor = texture2D(samp0, vtex);
};
这是片段着色器,它创建了一个渐变,其纹理坐标为1..128,128..255,而不是我预期的0..255:
precision mediump float;
varying vec2 vtex;
uniform sampler2D samp0;
void main() {
gl_FragColor = vec4(vtex.x, 0, 0, 1.0);
};
这是片段着色器,它为纹理坐标的子像素值创建一个颜色,并提供一个纹理,其中所有值都是我所期望的,并且应该是像素的中心:
precision mediump float;
varying vec2 vtex;
uniform sampler2D samp0;
void main() {
gl_FragColor = vec4(fract(vtex.x * 256.0), 0, 0, 1.0);
};
其余测试程序完全相同。我只更改着色器。这些是一些重要的代码片段:
String vertexshader =
"attribute vec4 a_pos; \n" + // in position
"attribute vec2 a_tex; \n" + // in Texture coordinates
"varying vec2 vtex; \n" + // out Texture coordinates
"void main(void) { \n" +
" gl_Position = vec4(a_pos); \n" +
" vtex = a_tex; \n" +
"}";
// Initialization
GLES20.glUseProgram(shader);
// Vertex
vdata.position(0);
GLES20.glVertexAttribPointer(handlepos, 2, GLES20.GL_FLOAT, false, 4*4, vdata);
GLES20.glEnableVertexAttribArray(handlepos);
vdata.position(2);
GLES20.glVertexAttribPointer(handletex, 2, GLES20.GL_FLOAT, false, 4*4, vdata);
GLES20.glEnableVertexAttribArray(handletex);
// Texture
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, maplookupid[1]);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST );
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_NEAREST );
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_REPEAT );
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_REPEAT );
GLES20.glUniform1i(handlesampler0, 0);
GLES20.glDisable(GLES20.GL_DITHER);
// Rendering
GLES20.glViewport(0, 0, screenx, screeny);
//
vdata.put(0*4+0, 0.0f * (2.0f/screenx));
vdata.put(0*4+1, 256.0f * (2.0f/screeny));
vdata.put(0*4+2, 0.0f * (1.0f/256.0f));
vdata.put(0*4+3, 256.0f * (1.0f/256.0f));
//
vdata.put(1*4+0, 0.0f * (2.0f/screenx));
vdata.put(1*4+1, 0.0f * (2.0f/screeny));
vdata.put(1*4+2, 0.0f * (1.0f/256.0f));
vdata.put(1*4+3, 0.0f * (1.0f/256.0f));
//
vdata.put(2*4+0, 256.0f * (2.0f/screenx));
vdata.put(2*4+1, 256.0f * (2.0f/screeny));
vdata.put(2*4+2, 256.0f * (1.0f/256.0f));
vdata.put(2*4+3, 256.0f * (1.0f/256.0f));
//
vdata.put(3*4+0, 256.0f * (2.0f/screenx));
vdata.put(3*4+1, 0.0f * (2.0f/screeny));
vdata.put(3*4+2, 256.0f * (1.0f/256.0f));
vdata.put(3*4+3, 0.0f * (1.0f/256.0f));
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
答案 0 :(得分:1)
看起来你正在遇到四舍五入的问题。由于您显然使用的是8位颜色缓冲区,因此通过将颜色乘以255将颜色转换为像素值。因此,纹理值0.5 / 256将乘以255,以便为像素值生成0.498,这在理想情况下是理想的(GPU上的无限精度计算和舍入到最近)将转换为0字节值。
事实上,从你获得1..128,128..255(128是重复值)的事实来看,它看起来像是将转换向上舍入(因为(127.5 / 256)* 255是127.002和(128.5 / 256) )* 255是127.998 - 那两个像素转换为128)。如果这是正在发生的事情,我希望你的第三个测试程序在每个像素中产生128,而不是127(因为0.5 * 255 == 127.5)
结果是你无法从GPU中获得极其精确的计算 - 有许多舍入错误的来源。
答案 1 :(得分:0)
您可能想要更改以下代码行:
precision mediump float;
为:
precision highp float;
片段着色器代码中的。