18

I'm working on an iPhone app that uses OpenGL ES 2 for its drawing. I know that typically texture coordinates are defined in the 0-1 range, but ideally I'd like to map them from 0-1023 (the size of my TextureAtlas) for readability's sake. I've seen sample code that defines coordinates in this manner, but haven't been able to suss out what previous calls were made that allowed for this. glMatrixMode(GL_TEXTURE) seems like it might be involved, but I'm not quite sure how to implement it.

My end goal would be to accomplish something like this, where the texture I'd be using within the atlas is in the upper left 48px square:

GLshort texcoords[]={
  48,48,
  0,48,
  48,0,
  0,0,
};
glVertexAttribPointer(ATTRIB_TEXTUREPOSITON, 2, GL_SHORT, 0, 0, texcoords);
glEnableVertexAttribArray(ATTRIB_TEXTUREPOSITON);
genpfault
  • 51,148
  • 11
  • 85
  • 139
Sam
  • 1,504
  • 2
  • 13
  • 19
  • 2
    One more thing... make sure you have `precision highp float;` at the top of your shader. If your texture is larger than 1024x1024, mediump won't give you enough precision to sample every pixel in the texture. – Danny Dulai Jun 24 '15 at 21:09

3 Answers3

51

This has been asked a few times, but I don't have the links at hand, so a quick and rough explanation. Let's say the texture is 8 pixels wide:

 | 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

The digits denote the texture's pixels, the bars the edges of the texture and in case of nearest filtering the border between pixels. You however want to hit the pixels' centers. So you're interested in the texture coordinates

(0/8 + 1/8)/2 = 1 / (2 * 8)

(1/8 + 2/8)/2 = 3 / (2 * 8)

...

(7/8 + 8/8)/2 = 15 / (2 * 8)

Or more generally for pixel i in a N wide texture the proper texture coordinate is

(2i + 1)/(2N)

However if you want to perfectly align your texture with the screen pixels, remember that what you specify as coordinates are not a quad's pixels, but edges, which, depending on projection may align with screen pixel edges, not centers, thus may require other texture coordinates.

datenwolf
  • 159,371
  • 13
  • 185
  • 298
  • In general my question was more about whether or not I can define the textures as pixels, I'm not having trouble getting them to align with pixel-perfect precision using floats ranging 0-1, but if possible I'd like to feed in actual pixel values so that the code is readable (like the example code in my original question) – Sam May 04 '11 at 06:48
  • @Sam: There is the extension GL_ARB_texture_rectangle which also implements per pixel texture access, however it got obsoleted by a fragment shader's capability to access a texture on a per pixel base. – datenwolf May 04 '11 at 07:38
  • @datenwolf any reference links or sample code on how to pull this off with a fragment shader? – Sam May 04 '11 at 07:47
  • @Sam: The GLSL built in functio texelFetch samples a texture at integer texel coordinates: http://www.opengl.org/sdk/docs/manglsl/xhtml/texelFetch.xml – datenwolf May 04 '11 at 11:20
  • 5
    @datenwolf Neither texture rectangles or texelFetch are available in OpenGL ES 2.0. – Dr. Snoopy May 04 '11 at 11:41
  • @Matias: Oh, right you are. Totally missed that the OP is on that plattform. Well then the OP must implement texel access by 0..1 range texture coordinates. – datenwolf May 04 '11 at 12:14
  • So to clarify there's no way to pull this off unless you're using 1.1 instead of 2.0? – Sam May 04 '11 at 18:04
  • This is really weird, you SHOULD have the links at hand. It's hard to believe it's so complicated :) – gyozo kudor Jul 04 '12 at 05:41
  • @gyozokudor: Just Google for "site:stackoverflow.com datenwolf texture pixel exact" or similar. – datenwolf Jul 04 '12 at 09:36
  • Shouldn't it be (2i - 1)/(2N)? – manatttta Nov 21 '16 at 11:15
  • 1
    @manatttta: No. I think you may be confusing that with the formula for rounding-up-integer-division… which looks similar. Just try to derive it yourself and you'll see how that +1 comes to be. – datenwolf Nov 21 '16 at 12:10
15

I waste 1 hour to find intuitive figure, but surprisingly nobody painted. So I did.

Given 4 pixel texture, normalized texture coordinate [0,1], unnormalized texture coordinate [0, width), gl_FragCoord [0, width] are as follows

enter image description here

Reference

  1. p270 in https://www.khronos.org/registry/gles/specs/3.1/es_spec_3.1.pdf
  2. https://www.opengl.org/sdk/docs/man/html/gl_FragCoord.xhtml
  3. https://www.opengl.org/wiki/Sampler_(GLSL)
Huang Dongsung
  • 335
  • 3
  • 5
4

Turns out this is possible in OpenGl ES 2. Here's how I accomplished it:

Fragment Shader:

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);

If anyone has comments on how this might work performance-wise, I would be interested.

Sam
  • 1,504
  • 2
  • 13
  • 19
  • 2
    Well, you don't need to call glGetAttribLocation/glGetUniformLocation every single frame. Save the result in a variable. But the performance difference is negligible I guess. – fabspro May 04 '12 at 10:36
  • 1
    It might be a good idea to do the division on the Obj-C side to get the least possible ops in the Fragment shader, i.e. compute uniforms a and b so that the line defining mult becomes "vec2 mult = a * v_texCoord + b;" – tjltjl Nov 26 '14 at 18:51
  • 1
    @Sam why this would work, your texture coord is (2i -1)/2N? – james Dec 28 '17 at 08:23