7

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.

genpfault
  • 51,148
  • 11
  • 85
  • 139
user408952
  • 882
  • 9
  • 23
  • Fixed-function or programmable pipeline? – genpfault Oct 25 '11 at 20:10
  • Obvious questions first, are you taking those borders into account when generating the UVs? Seems like they'd be good to get rid of, just duplicate the tile's edge pixels out. – ssube Oct 25 '11 at 20:11
  • @genpfault It's fixed function on the iphone. – user408952 Oct 25 '11 at 20:29
  • @peachykeen yes, the first 64x64 tile is positioned at 1,1 so the UVs will go from (1/1024,1/1024) to ((1+64)/1024, (1+64)/1024), if the whole texture is 1024. Do you think that duplicating the border pixels instead of adding transparency will work better? – user408952 Oct 25 '11 at 20:32
  • My guess is it's related to filtering, pixel->voxel mapping, or something along those lines. If so, duplicating them will hide the issue, if no other fix is available. Before you resort to that, though, try to make sure your uv coords and filtering line up properly. – ssube Oct 25 '11 at 20:51

4 Answers4

6

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.

Hannesh
  • 7,256
  • 7
  • 46
  • 80
  • I did this, but added a copy of the rightmost column to the right side, instead of the transparent border. I did this because the tiles don't wrap (e.g. the middle, top one in the tile image in the post. That has sand to the left and stone to the right). This makes it look kinda ok, at least with the iphone 4 resolution. – user408952 Oct 26 '11 at 19:02
  • Thank you so much! My models are looking a LOT better now :-) – Lea Hayes Apr 16 '12 at 16:39
  • So what about the corners of the texutres, what should they be? – paulm Apr 01 '14 at 21:40
  • The pixel from the corner diagonally opposite – Hannesh Apr 03 '14 at 15:43
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.

Xavier
  • 8,828
  • 13
  • 64
  • 98
  • Can u explain in more detail? May be u can provide some manuals? thanx – Sergey Kopanev Apr 28 '12 at 08:29
  • I think he means what I did. In my case I have a 256x256 png which is a 16x16 grid of 16x16 pixel textures. Say you're determining texture coordinates for the top left texture, from pixel 0,0 to 16,16 (or really 15,15). Instead of using s,t coordinates 0,0 and 1/16,1/16, you use, say 0.001,0.001 to 1/16-0.001,1/16-0.001. Make sense? – Grumdrig Sep 11 '12 at 19:34
  • Yes this is exactly what I meant. – Xavier Sep 13 '12 at 16:29
0

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

genpfault
  • 51,148
  • 11
  • 85
  • 139
  • Yes, but that will produce the same results as snapping the tiles to the pixel grid, i.e. jerky movement at slow speeds. – user408952 Oct 25 '11 at 20:32
0

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);
nminhtai
  • 130
  • 6