为什么我的着色器中的 GLSL 纹理坐标不是线性的?
我的 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);
};
这是创建纹理坐标的片段着色器 :纹理坐标的子像素值的颜色,并按照我的预期提供所有值为 127 的纹理,并且它应该是像素的中心:
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);
I have a problem with my glsl shaders on my Android 2.3.4 Tegra 2 device.
I want to do some calculations to create texture coordinates in the fragment shader in a glsl shader and noticed a problem and tried to narrow it down and therefore made a testprogram. I rendered a texture with a width of 256 pixels to exactly 256 pixels onscreen, save the framebuffer and check it pixel by pixel (compare with source) and it renders correctly. According to the opengl specification when I use the texture coordinates from 0.0 to 1.0 it should sample each pixel in the center (0.5/256, 1.5/256, 2.5/256, etc.), which seems right.
Then I wrote the x texture-coordinates themselves into the texture and checked those as well. The results are somewhat off and I don't know why. Instead of getting a gradient of 0..255 which I expected I got 1..128,128..255. That means the value 0 is skipped and the value 128 appears twice which means they are not perfectly linear. If the value was rounded in any way then not in a way that is obvious to me. I also checked the subpixel values for the texture-coordinates and they are what I expected. Their value is always 0.5/256, which should be half a pixel and ensure that the texture is sampled within the pixel correctly.
Again, the texture rendered with these coordinates is exactly what I want, but the coordinates themselves are not what I expected. This is a problem for me because I want to render with calculated texture coordinates and although I calculate them in a way that should be correct, the resulting texture is wrong. I need these coordinates to be correct to the pixel level and I don't know why they are off.
Does anyone know what is causing this behaviour and how I can correct it?
This is the fragment shader that renders the texture correctly:
precision mediump float;
varying vec2 vtex;
uniform sampler2D samp0;
void main() {
gl_FragColor = texture2D(samp0, vtex);
};
This is the fragment shader that creates a gradient with the texture coordinates that is 1..128,128..255 and not 0..255 as I expected :
precision mediump float;
varying vec2 vtex;
uniform sampler2D samp0;
void main() {
gl_FragColor = vec4(vtex.x, 0, 0, 1.0);
};
This is the fragment shader that creates a color for the subpixel values of the texture coordinates and delivers a texture with all values 127 as I expected, and which should be the center of the pixels:
precision mediump float;
varying vec2 vtex;
uniform sampler2D samp0;
void main() {
gl_FragColor = vec4(fract(vtex.x * 256.0), 0, 0, 1.0);
};
The rest of the test programs are absolutely identical. I only change the shaders. These are some significant code snippets:
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);
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
看来您遇到了舍入问题。由于您显然使用的是 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 进行极其精确的计算——舍入误差的来源有很多。
Looks like you're running into rounding problems. Since you're apparently using an 8-bit color buffer, colors are converted to pixel values by multiplying them by 255. So your texture value 0.5/256 will get multiplied by 255 to produce 0.498 for the pixel value, which in an ideal world (infinite precision computation on the GPU and round to nearest) would convert to a 0 byte value.
In fact, from the fact you get 1..128,128..255 (128 is the repeated value), it looks like its rounding the conversion up (since (127.5/256)*255 is 127.002 and (128.5/256)*255 is 127.998 -- those being the two pixels that convert to 128). If that is what is happening though, I would expect your 3rd test program to produce 128 in every pixel, rather than 127 (since 0.5*255 == 127.5)
The upshot is that that you can't expect extremely precise computation from a GPU -- there are many sources of rounding error.
的以下代码行:更改
为: 。
您可能需要将片段着色器代码中
You may want to change the below line of code:
to:
in your fragment shader code.