Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

OpenGL texture atlas bleeding

I'm trying to draw a basic 2d ground mesh made up of smaller tiles from a texture atlas (note the 1 pixel transparent border):

enter image description here

I render the tiles as texture quads using the following code:

glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, m_texture);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);

glVertexPointer(2, GL_SHORT, 0, &m_coords[0]);
glTexCoordPointer(2, GL_FLOAT, 0, &m_uvs[0]);

glDrawArrays(GL_TRIANGLES, 0, m_coords.size() / 2);

glBindTexture(GL_TEXTURE_2D, 0);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisable(GL_TEXTURE_2D);

The positions are obviously integer coordinates. The UV coordinates for the corners are calculated like this:

u0 = float(x) / float(texWidth);
v0 = float(y) / float(texHeight);
u1 = float(x+w) / float(texWidth);
v1 = float(y+h) / float(texHeight);

Where w and h is the size of the tile without the padding.

It looks great when the modelview transform is snapped to an integer position (right), but when it starts to move I get black thingies between the tiles (left):

enter image description here

From what I understand I should offset the UV coordinates with a half texel to make it work, but if I change the UV calculations to:

u0 = float(x+0.5f) / float(texWidth);
v0 = float(y+0.5f) / float(texHeight);
u1 = float(x+w-0.5f) / float(texWidth);
v1 = float(y+h-0.5f) / float(texHeight);

It still doesn't work. Is this the correct way to do it? Do I need blending for this to work? If I offset the tiles to make sure they're snapped to the pixel grid it works, but that makes it snap when moving slowly. How do people usually solve this?

EDIT

I should ofc have said that it's on the iphone.

like image 567
user408952 Avatar asked Oct 25 '11 19:10

user408952


2 Answers

Your border shouldn't be transparent, but rather the pixels from the opposing side of each subtexture. For example the border on the right hand side of each sub-texture should be a copy of the left-most line of pixels, i.e. the pixels that it would wrap around to.

That is how you "cheat" wrapping for the texture sampler on the borders.

like image 144
Hannesh Avatar answered Nov 07 '22 06:11

Hannesh


I had a similar issue with a texture atlas. I fixed it by insetting the image by 1.0/TEXTURE_ATLAS_PIXELS_PER_SIDE * 1/128.0. The 128 number you need to figure out by experimentation. The upside for me is no one is going to perceive 128th of a pixel being missing. I made this modification to the texture coordinates being sent to the graphics card and not in a shader. I have not tried doing this with texels in the shader, like you have. I've read different information on how to handle texture bleeding but for a texture atlas this was the easiest solution for me. Adding borders to my textures which are tightly packed and follow the power of two rule would cause me to have a lot of whitespace.

This is what worked for me on the iphone.

like image 41
Xavier Avatar answered Nov 07 '22 07:11

Xavier