Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

In my game there is a Minecraft-like terrain made out of cubes. I generate a vertex buffer from the voxel data and use a texture atlas for looks of different blocks:

texture atlas for voxel based terrain

The problem is that the texture of distant cubes interpolates with adjacent tiles in the texture atlas. That results in lines of wrong colors between cubes (you may need to view the screenshot below at its full size to see the graphical flaws):

screenshot of terrain with stripes of the wrong color

For now I use these interpolation settings but I tried every combination and even GL_NEAREST without mipmapping doesn't provide better results.

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

I also tried added an offset in the texture coordinates to pick a slightly smaller area of the tile but since the unwanted effect depends on the distance to the camera this cannot solve the problem completely. At far distances the stripes occur anyway.

How can I solve this texture bleeding? Since using a texture atlas is popular technique there might be a common approach. Sadly, for some reasons explained in the comments, I cannot change to different textures or texture arrays.

share|improve this question
4  
The problem lies with mipmapping - your texture coordinates may work well for the biggest mip level, but as things get further away, the bordering tiles blend together. You can fight this by not using mipmaps (probably not a good idea), growing your guard zones (area between tiles), growing the guard zones dynamically in a shader based on mip level (tricky), do something strange while generating mipmaps (can't think of anything though).. I've never tackled this problem myself though, but there's stuff to start with.. =) –  Jari Komppa Jan 7 '13 at 16:37
    
As I said sadly turning off mipmapping doesn't solve the problem. Without mipmaps it will either interpolate linear GL_LINEAR which gives a similar result or pick the nearest pixel GL_NEAREST which anyhow also results in stripes between blocks. Last mentioned option decreases but not eliminates the pixel flaw. –  danijar Jan 7 '13 at 16:44
7  
One solution is to use a format that have generated mipmaps, then you could create thees mipmaps your self and correct the error. Another solution is to force a specific mipmap level in your fragmentshader when you are sampling the texture. Another solution is to fill the mipmaps with the first mipmap. In other words, when you are creating your texture, you can just add subimages to that particular image, containing the same data. –  Tordin Jan 7 '13 at 16:50
1  
I had this problem before and it was solved by adding 1-2 pixel padding in your atlas between each different texture. –  Rubber Mallet Jan 8 '13 at 6:01
1  
@Kylotan. The problem is about texture resizing regardless of if it is done by mipmaps, linear interpolation or nearest pixel picking. –  danijar Jan 10 '13 at 20:34
show 14 more comments

4 Answers

One option that'll be a lot easier than fiddling with mipmaps and adding texture coordinate fuzz factors is to use a texture array. Texture arrays are similar to 3d textures, but with no mipmapping in the 3rd dimension, so they're ideal for texture atlases where the "subtextures" are all the same size.

http://www.opengl.org/wiki/Array_Texture

share|improve this answer
    
If they're available, they're a much better solution - you get other features as well, like wrapping textures, that you miss with atlases. –  Jari Komppa Jan 7 '13 at 17:01
    
@JariKomppa. I do not want to use texture arrays because that way I would have extra shaders for the terrain. Since the engine I write should be as general as possible I do not want to make this exception. Is there a way to create one shader that can handle both, texture arrays and single textures? –  danijar Jan 10 '13 at 20:47
4  
@sharethis Dismissing unambiguously superiour technical solutions because you want to be "general" is a recipe for failure. If you write a game, don't write an engine. –  Sam Hocevar Jan 11 '13 at 12:13
    
@SamHocevar. I am writing an engine and my project is about a specific architecture were components are fully decoupled. So the form component shouldn't take care of the terrain component which should only provide standard buffers and a standard texture. –  danijar Jan 11 '13 at 12:30
    
@sharethis OK. I was somehow misled by the "In my game" part of the question. –  Sam Hocevar Jan 11 '13 at 13:23
show 2 more comments

Okay, I think you have two problems going on here.

The first problem is with mipmapping. In general, you don't want to naively mix atlasing with mipmapping, because unless all your subtextures are exactly 1x1 pixel sized, you will experience texture bleeding. Adding padding will simply move the problem to a lower mip level.

As a rule of thumb, for 2D, which is where you usually do atlasing, you don't mipmap; while for 3D, which is where you mipmap, you don't atlas (actually, you skin in such a way that bleeding won't be a problem)

However, what I think is going on with your program right now is that you're probably not using the correct texture coordinates, and because of that your textures are bleeding.

Let's suppose an 8x8 texture. You're probably getting the top-left quarter by using uv coordinates of (0,0) to (0.5, 0.5):

Incorrect mapping

And the problem is that at u-coordinate 0.5, the sampler will interpolate the destination fragment using half of the left texel and half of the right texel. The same will happen at u-coordinate 0, and the same for v-coordinates. This is because you're addressing the borders between texels, and not the centers.

What you should do instead is address the center of each texel. In this example, these are the coordinates you want to use:

Correct mapping

In this case, you will always be sampling the center of each texel and you should not get any bleeding... Unless you use mipmapping, in which case you will always get bleeding starting on the mip level where the scaled subtexture doesn't neatly fit inside the pixel grid.

To generalize, if you want to access a specific texel, the coordinates are calculated as:

function get_texel_coords(x, y, tex_width, tex_height)
    u = (x + 0.5) / tex_width
    v = (y + 0.5) / tex_height
    return u, v
end

This is called "half pixel correction". There's a more detailed explanation in here if you're interested.

share|improve this answer
    
Your explanation sounds very promising. Sadly since I have to video card at the moment, I cannot implement it yet. I will do so if the repaired card arrives. And I will calculate the mipmaps of the atlas on my own without interpolating over the boundaries of tiles then. –  danijar Feb 18 '13 at 14:20
    
@sharethis If you definitely have to atlas, make sure your subtextures have sizes in powers of 2, so you can limit bleeding to the lowest mip levels. If you do so, you don't really need to hand craft your own mipmaps. –  Panda Pajama Feb 18 '13 at 14:30
    
Even PoT sized textures can have bleed artifacts though, because the bilinear filtering can sample across those boundaries. (At least in the implementations I've seen.) As for the half-pixel correction, should that apply in this case? If I wanted to map his rock texture for example, surely that will always be 0.0 to 0.25 along the U and V coordinates? –  Kylotan Feb 18 '13 at 17:31
    
@Kylotan If your texture size is even, and all the subtextures have even sizes and are located at even coordinates, bilinear filtering when halving the texture size will not mix between subtextures. Generalize for powers of two (if you're seeing artifacts, you're probably using bicubic, not bilinear filtering). Regarding half-pixel correction, it is necessary, because 0.25 is not the rightmost texel, but the middle point between both subtextures. To put it into perspective, imagine that you are the rasterizer: what is the color of the texture at (0.00, 0.25)? –  Panda Pajama Feb 19 '13 at 1:59
1  
@sharethis check out this other answer I wrote which may be able to help you gamedev.stackexchange.com/questions/50023/… –  Panda Pajama Mar 9 '13 at 15:50
show 18 more comments

Looks like the issue could be caused by MSAA. See this answer and the linked article:

"when you turn on MSAA, it then becomes possible for the shader to get executed for samples that are inside the pixel area, but outside of the triangle area"

The solution is to use centroid sampling. If you are calculating the wrapping of the texture coordinates in a vertex shader, moving that logic to the fragment shader could also fix the issue.

share|improve this answer
    
As I already wrote in another comment I have no working video card at the moment so I cannot check if that is the actual problem. I will do so as soon as I can. –  danijar Feb 18 '13 at 14:21
    
I disabled hardware antialiasing but the bleeding still remains. I already do the texture looking in the fragment shader. –  danijar Mar 9 '13 at 11:41
add comment
up vote 3 down vote accepted

After struggling a lot with this issue, I finally came up with a solution.

To use both, a texture atlas and mipmapping, I need to perform the downsampling myself, because OpenGL would otherwise interpolate over the boundaries of tiles in the atlas. Moreover I needed to set the right texture parameters, because interpolating between mipmaps would also cause texture bleeding.

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

With these parameters, no bleeding occurs.

There is one thing you have to notice when providing custom mipmaps. Since it doesn't make sense to shrink the atlas even if each tile is already 1x1, the maximal mipmap level must be set according to what you provide.

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 7); // pick mipmap level 7 or lower

Thanks for all other answers and very helpful comments! By the way I still do not know how to use linear up scaling, but I guess there is no way.

share|improve this answer
    
good to know you got it solved. You should mark this answer as the correct one instead of mine. –  Panda Pajama Mar 11 '13 at 3:40
    
mipmapping is a technique exclusively for downsampling, which makes no sense for upsampling. The idea is that if you're going to draw a small triangle, it would take a lot of time to sample a large texture just to get a few pixels out of it, so you pre-downsample it and use the appropriate mip level when drawing small triangles. For larger triangles, using anything different from the larger mip level doesn't make sense, so it is an error to do something like glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_FILTER, GL_NEAREST_MIPMAP_LINEAR); –  Panda Pajama Mar 11 '13 at 3:52
    
@PandaPajama. You are right, I corrected my answer. To set GL_TEXTURE_MAX_FILTER to GL_NEAREST_MIPMAP_LINEAR causes bleeding not for the reason of mipmapping, but for the reason of interpolation. So in this case it is equivalent to GL_LINEAR since the lowest mipmap level is used anyway. –  danijar Mar 11 '13 at 12:06
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.