Has anyone used a texture atlas and used it in Chrome?

I created a texture atlas and in Chrome there is some white on the edges of the cubes. However in Firefox it displays just fine. When I use colors there are not white edges either in Chrome or Firefox so it appears to be strictly a texturing problem. The texture atlas is 64 x 64 pixels and there are 16 total textures within the texture atlas at a width of 16 each.

Has anyone had a similar issue or know maybe why I am having this problem?

Yes, I experienced the same problem, it seems to be related to the hardware platform you are using. When I tested it in another pc, the problem just disappeared.
My platform with problems: AMD Phenom II x4 945, GTX460, Win7
Fine platform: Intel Core2 E7500, 9500GT, WinXP

Could one of you guys share a screenshot and a copy of the texture map & shader?

The artifacts only appears on Chrome. In firefox it’s fine on all platforms.

How certain are you that your vertices are accurately snapped together - with no ‘T-edges’? One common difference between Chrome and Firefox (on some hardware) is that Chrome will enable antialiasing when Firefox won’t.

If your vertices are not 100% perfectly shared - then you’ll get teeny-tiny gaps (which we’re seeing white background through) that are visible because the antialiasing is rendering the edges just perfectly…but without antialiasing, the gaps would show up less often (but be much more noticeable when they do show up).

That’s my best theory…but it’s hard to guess from such little information.

As a test, turn off antialiasing when you create your rendering context. If that makes Chrome and Firefox look the same - then I’m 99% sure I’m right. If it doesn’t make a difference…then I need to have another think…so show us the texture AND the shader.

vertex shader:

attribute vec3 aVertexPosition;
attribute vec3 aVertexNormal;
attribute vec2 aTextureCoord;
attribute float aInShade;
attribute vec4 aVertexColor;
uniform mat4 uMVMatrix;
uniform mat4 uPMatrix;
uniform mat4 uNMatrix;
uniform vec3 uAmbientColor;
uniform vec3 uLightingDirection;
uniform vec3 uDirectionalColor;
uniform bool uUseLighting;
uniform bool uUseShadows;
uniform bool uUseColor;
varying vec2 vTextureCoord;
varying vec3 vLightWeighting;
varying vec4 vColor;

vec3 halfLambertDiffuse(void) {
  vec4 transformedLightingDirection = uMVMatrix * vec4(uLightingDirection, 0.0);
  vec4 transformedNormal = uNMatrix * vec4(aVertexNormal, 1.0);
  float directionalLightWeighting = max(sqrt(0.5 * dot(transformedNormal.xyz, transformedLightingDirection.xyz) + 0.5), 0.0);
  //float directionalLightWeighting = max(dot(transformedNormal.xyz, transformedLightingDirection.xyz), 0.6);
  vLightWeighting = uAmbientColor + uDirectionalColor * directionalLightWeighting;
  return vLightWeighting;

void main(void)
  vec4 mvPosition = uMVMatrix * vec4(aVertexPosition, 1.0);
  gl_Position = uPMatrix * mvPosition;
  if (!uUseColor) {
    vTextureCoord = aTextureCoord;
    if (!uUseLighting) {
      vLightWeighting = vec3(1.0, 1.0, 1.0);
    } else {
      //shading with shadows
      if (uUseShadows) {
        if (aInShade == 0.0) {
         vLightWeighting = halfLambertDiffuse();
        } else {
          vLightWeighting = uAmbientColor;
      } else { 
        //shading only
        vLightWeighting = halfLambertDiffuse();
  } else {
    vColor = aVertexColor;
    if (uUseLighting) {
      vLightWeighting = halfLambertDiffuse();

fragment shader:

#ifdef GL_ES
precision highp float;
varying vec2 vTextureCoord;
varying vec3 vLightWeighting;
varying vec4 vColor;
uniform sampler2D uSampler;
uniform bool uUseColor;
uniform bool uUseLighting;
void main(void)
  if (!uUseColor) {
    vec4 textureColor = texture2D(uSampler, vec2(vTextureCoord.s, vTextureCoord.t));
    gl_FragColor = vec4(textureColor.rgb * vLightWeighting, textureColor.a);
    //gl_FragColor = texture2D(uSampler, vec2(vTextureCoord.s, vTextureCoord.t));
  } else {
    if (!uUseLighting) {
      gl_FragColor = vColor;
    } else {
      gl_FragColor = vec4(vColor.rgb * vLightWeighting, vColor.a);

Everything in the game world is made of cubes. Notice the textured cubes have the white edges vs the colored cubes (the character) has no white edges.

Another angle:


There is no bug in driver, browser or WebGL here. Your program is the problem. You’re probably getting away with it on some systems because the results sensitively depend on roundoff error - and antialiassing is almost certainly what’s making it more noticable. However, with the issues I describe below, you’ll eventually see issues on any platform under some camera angles/lighting/whatever.

There are a couple of issues here:

  1. If you draw two cubes bang up against each other (eg on the ground), the top row of pixels of front-facing vertical face of the further cube will potentialy have the exact same Z coordinate as the far edge of the upper face of the nearer cube…so they will “flimmer”/“Z-fight” at some camera angles. You can’t make a voxel game just by drawing a bunch of cubes - you have to eliminate the faces of cubes that are butting up against other cubes.

  2. When you render a texture with LINEAR or MIPMAP_LINEAR modes, then adjacent texels will be linearly blended. Hence you can’t use atlassed textures that are packed together tightly like that. You have to inset the area you use out of each sub-map by at least a few texels and color the “no-man’s land” between the sub-maps in the same color as the adjacent sub-maps. So instead of using UV values like 0.0 to 0.25 for one map and 0.25 to 0.5 for the next - use 0.0+(inset/mapres) to 0.25-(inset/mapres) for one and 0.25+(inset/mapres) to 0.5-(inset/mapres) for the next…where ‘inset’ is 2.0 or so and ‘mapres’ is the resolution of your map in texels. If you’re MIPmapping (which you don’t seem to be doing in your screenshot - but you’ll definitely need to do to avoid horrible moire & aliassing because you’re using perspective) then there is no amount of “inset” that’ll perfectly avoid the problem at lower MIP levels - so you’d also do well to avoid textures of violently differing colors being right next to each other in the atlas…try to keep similar colors together. If you have unused areas in the map…paint them a neutral color that’s similar to the nearby maps.

Because your foreground character seems to be working - I deduce that (2) is your main problem…but I predict that (1) will also come back to bite you. 3D graphics are quite intolerant of this kind of inattention to roundoff and precision issues.

– Steve

Oh - and incidentally, using unnecessary “if” statements inside shaders is a really bad idea - especially on older hardware. Shaders are NOT general purpose CPU’s and you can’t treat GLSL like it was C or C++.

On some older machines, both the ‘then’ and the ‘else’ part consume time, even if they aren’t being executed!!! eg:

if ( a )
do_something () ;
do_something_else () ;

…executes at a speed that is equal to the time it would take to do BOTH “do_something” AND “do_something_else”. Even modern machines will suffer this problem in pixel shaders under some rather common situations!!

Hence, even when you have lighting and shadowing turned off - you’re still paying the price for doing both. With your deeply-nested “if” statements, you’re running every statement in the shader even if just one assignment was all that was needed!

Since it looks like you’re using uniform variables to switch various modes on and off - you’d be better to create different shaders for each combination of modes using #if/#else/#endif to keep your sanity! When you’re rendering, consider sorting your objects according to the shader they need in order to minimize shader reloading.

– Steve

Wow thank you for the helpful advice. I have a lot to digest as I do this as a hobby and have been teaching myself WebGL/OpenGL for the past 6 months outside of work. There are some things regarding the textures I didn’t understand but I want to do some Googling and be less tired before asking.

I’ll probably be posting some more questions tomorrow, after I get out of work, to make sure I understand. Thanks again. I really do owe you one.

But my artifacts appears where is drawn in one draw batch and I don’t even use a texture.

OK guys - this is getting confusing!

Firstly, I should explain that I have been doing 3D graphics since 1980. I’ve seen every kind of problem you could possibly imagine. So I’m not guessing when I’m telling you what’s wrong!

You two guys have totally different problems that just happen to look the same. They are both teeny-tiny roundoff kinds of problem that show up most clearly when antialiassing is enabled - which it typically is in Chrome and typically isn’t in Firefox - and may be visible on some kinds of hardware and not others. In both cases you have made fairly elementary mistakes that I’ve seen 100 times before…but they appear to be different mistakes!

Accusing Chrome is unfair…you’ll both see these problems in Firefox too - but because of the antialiassing in Chrome, the statistical probability of a “crack” showing up is much greater - but the crack itself is much narrower. When Firefox shows the problem, it’ll be MUCH bigger…but it’s also less probable.

To avoid further confusion - let me separate out my responses to each of you!

@oak3d: The “drawn in one batch” thing doesn’t matter a damn. The cause of the symptoms you are reporting are very different from those that xman84 is seeing - the panda/bear image almost certainly has cracks because the vertices aren’t perfectly shared, even though it’s only one draw call - and even if it’s not textured. If you don’t believe me (and to be fair, all I’ve got to go on is one screenshot!) try clearing the screen to some really bright color instead of white. If you clear the screen to green, do the “cracks” turn green? If they do then you must have fine cracks in your geometry that are letting the background color leak through - right? That’s what I’m betting…but you should do the experiment. Do you know what a “T-edge” is? If not, I can explain that. I’m pretty certain that you either have T-edges or unshared vertices - or (much less likely) some other weird bad geometry issue.

@xman84: Your texture atlas issues are at least a part of the problem - as I described before (although possibly not ALL of your problem). We know this for 100% certain because if you look at the ‘cracks’ on the building, some of them are red - and that’s because you have red texture next to the grey/black “stone” texture. All of the cracks in the “grass” are white because the texture tile next to the green one is white. You never see red cracks in the grass because there is no red texture adjacent to the green one. Just to test my theory, look at the sky. There are white cracks on the left/right side of the map and green cracks at the top. Look at your texture atlas and guess what? You have grass next to one side of the blue patch and white on the other side. This isn’t just a theory anymore…we have solid evidence! OpenGL/WebGL/Direct3D all do a trick where they blend the edges of adjacent texels together to avoid texture aliassing. At the very edges of your polygons, the texture UV coordinates are probably EXACTLY on the boundary between the grey/black stone and the bright red sub-map. So when WebGL blends in a little of the adjacent texel, you get some red mixed into the grey for a small fraction of the pixel. When you run in Firefox, the lack of polygon edge antialiassing is hiding this problem from you to some degree - but it WILL show up there under some circumstances…I guarantee it.

@oak3d: Your panda’s body is a “volume of revolution” - right? I bet your code says something like:

 for ( a = 0 ; a < 360 ; a+=10 )  // Do a full circle in 10 degree steps
    a0 = a * 3.14159 / 180.0 ;  // Convert to radians
    x0 = sin ( a0 ) * radius ;
    y0 = cos ( a0 ) * radius ;
    a1 = (a+1) * 3.14159 / 180.0 ;
    x1 = sin ( a1 ) * radius ;
    y1 = cos ( a1 ) * radius ;
    // make a quad from x0,y0 to x1,y1...

If you did anything like that then you are assuming that the sine and cosine of 0pi/180 are PRECISELY equal to those at 36pi/180 - but they aren’t because in floating point math, bigger numbers are stored less precisely than small ones - and those two angles are not precisely two-pi radians apart. Note that it doesn’t matter whether you do this in amazingly high precision math - or even whether you typed pi in to enough decimal places…the nature of computers is that floating point numbers are never 100% precise.

So if I’m right then it’s no coincidence that the join between the first row of vertices and the last is where the crack shows up…and right down the center line of the panda is where it is! (That’s my clue!)

The fix (if my guess is right) is to change the calculation of ‘a1’:

 if ( a == 35 ) a1 = 0 * 3.14159/180.0 ; else a1 = (a+1)*3.14159/180.0 ;

…which ensures that the last column of vertices exactly lines up with the first.

But better still, use an indexed rendering mode and make the index of the final row of triangles re-use the actual vertices from the first row so you’re not relying on math precision at all.

I can’t be 100% sure of this - but I’d bet $10 that if you’re generating the geometry programmatically that you did something kinda like that!

This explains the vertical crack - but it can only explain the horizontal ones if you’re also not sharing the vertices vertically between rows of triangles either. That suggests that you’re sending at least twice as many vertices to the GPU as you need to - and your panda will probably take twice as long to draw as it ideally could.

– Steve


Would it better for me to do something like suggested here? Specifically the third pic from the top.

Which I think is what you suggested earlier with the inset. The picture is nice confirmation that I understand.

Or should I just modify my texture coordinates slightly, say by .05 to get rid of the bleeding? I’ll probably make the changes over the next day or two and report back.


Thank you for your detailed reply. I’ll check out the mesh to see if there’s anything as what you said.

if its not Tjunctions like steve saiz, its most likely filtering
to see if thats the cause use gl.( NEAREST ) texture filtering instead of LINEAR


Yes - I think you understand.

That’s the kind of thing you have to do to the texture map - but you’ll still have to inset your texture coordinates by a couple of pixels from where they are right now. An inset of 0.05 would probably be enough for a texture of around 128x128 resolution. You could make it a bit less than that for larger maps maybe.

The problem is that there is no “correct” amount. If you have the eyepoint far enough back - or if the polygons are sufficiently edge-on, you’ll still get some bleed. But hopefully the texture will be so “muddy” by then that you’ll get away with it.

I do this kind of thing a lot because atlassed textures save the need to have separate draw calls for things with a lot of different maps - and I use shader code to switch sub-maps on the fly to make simple animations and other effects.

Keeping my UV coordinates a couple of pixels away from edge of each sub-map is good enough for most applications…perfection is overrated!

The other trick (if you can) is to try to avoid having maps that are too violently different in color/tone in the same texture…and if you can’t avoid that, at least try to arrange your maps so that the most similarly colored ones are together.

Take a look at this atlas texture from my cowboy bar-fight game at http://tubagames.net for example:

Good luck!

You’re getting as confused as I was! There are two completely separate problems going on in this thread.

oak3d’s problem (with his panda model) could be either Tjunctions (T-edges, whatever) or just roundoff error in the vertex math causing the polygons to not quite join up along their edges (I’m betting on the latter).

xman84’s problem (with his voxel-based world) is a texture color bleed problem - which could be “solved” using gl.NEAREST texture filtering as you suggest. But the quality implications of using totally unfiltered textures in a 3D application are fairly horrifying - definitely not something I’d ever recommend!