12

I'm trying to draw a subregion of a texture in OpenGL by specifying the coordinates I want. What's happening though is that, depending on the size of the image, it seems there's a slight offset in the origin of where it selects the texture coordinates. The offset amount seems to be less than the size of a pixel & the output is is blurred combination of neighboring pixels.

Here's an idea of what I'm describing. In this case I'd want to select the 6x5 green/white region but what OpenGL is rendering includes a slight pink tint to the top & left pixels. Offset Illustration

What the output would look like:

Resulting Output Texture

I can fix it by adding an offset to the texture coordinates before passing them to glTexCoordPointer but the problem is that I have no way to calculate what the offset is and it seems different for different textures.

Pseudocode:

float uFactor = regionWidth / textureWidth;   // For the example: 0.6f
float vFactor = regionHeight / textureHeight; // For the example: 0.5f

data[0].t[0] = 0.0f * uFactor;
data[0].t[1] = 0.0f * vFactor;
data[1].t[0] = 1.0f * uFactor;
data[1].t[1] = 0.0f * vFactor;
data[2].t[0] = 0.0f * uFactor;
data[2].t[1] = 1.0f * vFactor;
data[3].t[0] = 1.0f * uFactor;
data[3].t[1] = 1.0f * vFactor;

glPushMatrix();

// translate/scale/bind operations

glTexCoordPointer(2, GL_FLOAT, 0, data[0].t);
Nick Gotch
  • 9,167
  • 14
  • 70
  • 97

2 Answers2

22

Keep in mind, that OpenGL samples textures at the texel centers. So when using linear filtering (like GL_LINEAR or GL_LINEAR_MIPMAP_LINEAR) the exact texel color is only returned if sampled at the texel center. Thus, when you only want to use a sub-region of a texture, you need to indent your texture coordinates by half a texel (or 0.5/width and 0.5/height). Otherwise the filtering will blend the border of the texture with neigbouting texels outside of your intended region. This causes your slightly pinkish border. If you use up the whole texture this effect is compensated by the GL_CLAMP_TO_EDGE wrapping mode, but when using a subregion, GL does not know where it's edge is and that filtering should not cross it.

So when you got a subregion of the texture in the range [s1,s2]x[t1,t2] (0 <= s,t <= 1), the real valid texCoord interval should be [s1+x,s2-x]x[t1+y,t2-y] with x being 0.5/width and y being 0.5/height (the width and height of the whole texture corresponding to [0,1]x[0,1]).

Therefore try

data[0].t[0] = 0.0f * uFactor + 0.5/textureWidth;
data[0].t[1] = 0.0f * vFactor + 0.5/textureHeight;
data[1].t[0] = 1.0f * uFactor - 0.5/textureWidth;
data[1].t[1] = 0.0f * vFactor + 0.5/textureHeight;
data[2].t[0] = 0.0f * uFactor + 0.5/textureWidth;
data[2].t[1] = 1.0f * vFactor - 0.5/textureHeight;
data[3].t[0] = 1.0f * uFactor - 0.5/textureWidth;
data[3].t[1] = 1.0f * vFactor - 0.5/textureHeight;
Christian Rau
  • 45,360
  • 10
  • 108
  • 185
  • This comes really close, if I add 0.5f to all the coords it fixes some but messes up others. If I add/subtract like you mention, I don't get any of the edge artifacts but there's still some bleeding of pixel data within the image – Nick Gotch May 19 '11 at 02:37
  • These are test renders using two-color shapes like above. Using No Offsets: http://bit.ly/ioKuV7 . Adding half to all Coords: http://bit.ly/mklitW . Adding/Subtracting half as in your example: http://bit.ly/m8O5dd – Nick Gotch May 19 '11 at 14:54
  • 2
    You could try to also align your vertex coordinates to pixel centers, meaning adjusting them by half a pixel (in view coordinates, of course). Otherwise they are a bit off to the texture coordinates, meaning you got an axb rect with a cxd texture. If you want pixel exact texturing, you need a==c and b==d. So do the same adjustment from the texCoords for the positions too. Then the sizes match. – Christian Rau May 19 '11 at 21:34
  • Christian, I'm going to play with these values a little more but I think you're right and the final solution will just take a little more tweaking. Anyway we're close enough & the art team is satisfied so I'm going to accept your answer. Thanks again! – Nick Gotch May 19 '11 at 22:22
4

Propably this has to do with the wrap around. Try this when you create the source image:

glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP )
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP )
Constantinius
  • 34,183
  • 8
  • 77
  • 85
  • Doing this improves the result but the overall image is still off. It seems this worked to clamp the top & left sides but since it doesn't change the (1,1) vertex the texture it's grabbing is still involving some merging of pixel colors. – Nick Gotch May 17 '11 at 00:13