1

I have a big, 2048x2048 image with 4096 32x32 sprites. That is, a 64x64 table of 32x32 images. I'm using triangles to draw them. Each trig must cut the right piece of the texture. It works alright, except that on the border of the faces, the engine is accessing pixels from neighbor sprites on the sheet, resulting in noise. Notice the pixels below the orc:

notice the noise pixels below the character

They are from the sprite below it on the sheet. I'm using the following calculation to cut the area:

Vertex shader:

// position of each pixel of this face on texture
varying vec2 pix_pos;

// pos_in_sheet holds the position of the sprite on the sheet
// considering it is a 64x64 sheet, it is on the range vec2(0..63, 0..63)
vec2 pos_in_sheet = vec2(floor(mod(sprid,texture_cols)), floor(sprid/texture_cols));

// `vec2 vertex` indicates which of the 4 vertex in the quad this is:
// vec2(-1.0,-1.0)|vec2(-1.0,1.0)|vec2(1.0,-1.0)|vec2(1.0,1.0)

// inside main() I calculate the bounding quad that contains the sprite
pix_pos = vec3(
    ((pos_in_sheet.x+(vertex.x+1.0)/2.0)/64.0), //64 = rows in texture 
    ((pos_in_sheet.y+(-vertex.y+1.0)/2.0)/64.0));

And this is the fragment shader:

varying vec3 pix_pos;
uniform sampler2D sampler;
void main(void){
    vec4 col = texture2D(sampler,vec2(pix_pos.x,pix_pos.y));
    gl_FragColor = vec4(col.x,col.y,col.z,1.0);
}

I've checked my calculation and it seems fine. So what could be causing the fragment shader to access sprites outside its own cutting region?

MaiaVictor
  • 51,090
  • 44
  • 144
  • 286
  • What's `vertex` variable? Btw, your `pix_pos` is somewhere a `vec2` and somewhere `vec3`. – Dragan Okanovic Mar 07 '14 at 10:29
  • 1
    Linear texture filtering causes this. I have written about this [numerous times](http://stackoverflow.com/questions/19611745/opengl-black-lines-in-between-tiles/19612007#19612007). The solution is going to be to either use nearest neighbor filtering (hack) or place border texels around all of your sprites when you pack them (the proper solution). – Andon M. Coleman Mar 07 '14 at 12:38

1 Answers1

2

This can happen due to linear interpolation, because each texture2D() can take 4 pixels from a texture and returns the result based on those 4 pixels and some weights. It's hard to solve this issue trough math code since float operations are hard to handle.

Another thing that can generate this issue are texture mipmaps(if you use them). In general lower mipmap levels are created using some sort of filtering technique that will blend 4 pixels into one. Neighboring pixels that have a high contrast can create such artifacts.

The solution for this is to have a padding between sprites. Typically between 2 and 4 pixels of empty space (in general black rgba<0,0,0,0> ) should solve the problem. So each sprite has 32x32 pixels and in an atlas will take 34x34 pixels. The 2 pixel border around the sprite has to be the color of the closest pixel in the sprite (in your case transparent). This reduces the number of sprites you can fit in the atlas

Raxvan
  • 6,257
  • 2
  • 25
  • 46
  • Padding between sprites, definitely the one I'd go for. – Jean-Simon Brochu Mar 07 '14 at 19:59
  • This really complicates the ATLAS layout, actually. Can't I just disable those kinds of interpolation? – MaiaVictor Mar 09 '14 at 04:23
  • @Viclib probably you can, you have to test by changing the texture filtering to nearest neighbor, i can't tell you how to do this in webgl but in c++ opengl is somthing like: `glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);` – Raxvan Mar 10 '14 at 09:43