OpenGL纹理坐标在像素空间中的含义

18

我正在开发一个使用OpenGL ES 2进行绘制的iPhone应用程序。通常情况下,纹理坐标被定义在0-1范围内,但出于可读性的考虑,我希望将它们从0-1023(我的TextureAtlas大小)进行映射。

我看过一些定义了这种坐标的示例代码,但没能找出之前调用了哪些方法来实现这个目的。可能涉及到glMatrixMode(GL_TEXTURE),但我不确定如何实现。

我的最终目标是像这样实现,其中我将在atlas中使用的纹理位于左上角的48px正方形中:

GLshort texcoords[]={
  48,48,
  0,48,
  48,0,
  0,0,
};
glVertexAttribPointer(ATTRIB_TEXTUREPOSITON, 2, GL_SHORT, 0, 0, texcoords);
glEnableVertexAttribArray(ATTRIB_TEXTUREPOSITON);

2
还有一件事……确保在着色器的顶部有 precision highp float;。 如果您的纹理大于1024x1024,则mediump无法为您提供足够的精度来对纹理中的每个像素进行采样。 - Danny Dulai
3个回答

51

这个问题被问过几次,但我手头没有链接,所以给一个快速粗略的解释。假设纹理宽度为8像素:

 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
 ^   ^   ^   ^   ^   ^   ^   ^   ^
0.0  |   |   |   |   |   |   |  1.0
 |   |   |   |   |   |   |   |   |
0/8 1/8 2/8 3/8 4/8 5/8 6/8 7/8 8/8
数字表示纹理的像素,条形线表示纹理的边缘,在最近过滤情况下是像素之间的边界。然而您希望命中像素的中心。因此,您需要关注纹理坐标。
对于宽度为N的纹理中的像素i,其适当的纹理坐标为(2i+1)/(2N)。
但是,如果您想要完全将纹理与屏幕像素对齐,请记住,您指定的坐标并不是四边形的像素,而是边缘,这取决于投影可能会与屏幕像素的边缘对齐,而不是中心,因此可能需要其他纹理坐标。

一般来说,我的问题更多地是关于是否可以将纹理定义为像素。我并没有在使用范围为0-1的浮点数时出现对齐像素完美精度的问题,但如果可能的话,我希望提供实际像素值,以便代码可读性更强(就像我原来问题中的示例代码)。 - Sam
5
在OpenGL ES 2.0中,既没有提供纹理矩形也没有提供texelFetch函数。 - Dr. Snoopy
@Matias:哦,你说得对。完全忘记了OP在那个平台上。那么,OP必须通过0..1范围的纹理坐标来实现texel访问。 - datenwolf
所以澄清一下,除非您使用1.1而不是2.0,否则无法实现这个操作? - Sam
1
@manatttta:不,我觉得你可能把它和向上取整的整数除法公式搞混了……虽然它们看起来相似。试着自己推导一下,你就会明白为什么要加上那个+1了。 - datenwolf
显示剩余6条评论

15

1
这很有用,因为texelFetch在GLSL 3.0中得到支持。 - zhy
11
看起来这张图片有一个错别字,Norm tex coord 应该是 0.125 到 0.875 - Denis Golovkin

4
原来在OpenGl ES 2中可以实现这个功能。下面是我完成它的方法:
片段着色器:
precision mediump float; 
varying vec2 v_texCoord;
uniform sampler2D textureSample;
uniform float texScale;
void main()
{
    vec2 mult=(2.0*v_texCoord - 1.0)/(2.0*texScale);
    gl_FragColor = texture2D(textureSample,mult);
}

Obj-C:

GLshort texCoords[]={
    512,512,
    0,512,
    512,0,
    0,0,
};
GLfloat textureWidth = 512.0; //texture size, assumed square, power of 2
texCoordLoc = glGetAttribLocation ( program, "a_texCoord");
GLuint textureScale = glGetUniformLocation ( program, "texScale");
glUniform1f(textureScale, textureWidth);
glVertexAttribPointer ( texCoordLoc, 2, GL_SHORT, GL_FALSE, 0, texCoords);

如果有人对这个在性能方面的运作方式有评论,我会很感兴趣。


2
好的,你不需要在每一帧都调用glGetAttribLocation/glGetUniformLocation。将结果保存在一个变量中即可。但我猜性能差异微乎其微。 - fabspro
1
在片段着色器中进行除法运算可能是个好主意,因为这样可以尽量减少操作次数。也就是说,可以计算出统一变量a和b,使得定义mult的线路变成“vec2 mult = a * v_texCoord + b;”。建议在Obj-C端进行此操作。 - tjltjl
1
@Sam,为什么这个会起作用,你的纹理坐标是(2i-1)/2N? - james

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