OpenGL texture atlas bleeding

2020-02-26 11:41发布

问题:

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

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

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.

回答1:

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.



回答2:

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.



回答3:

You could always switch your GL_TEXTURE_MIN/MAG_FILTER to GL_NEAREST :)



回答4:

Try using GL_NEAREST for GL_TEXURE_MIN/MAX_FILTER and translate by 0.375f before drawing:

glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(0.375f, 0.375f, 0.0f);


回答5:

I had this exact same problem. I fixed it with this.

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);