6

I'm trying to implement ambient occlusion on a voxel-based mesh and get these flickering white pixels at the edges of faces:

shimmering pixels

Here is my fragment shader:

#version 120

varying vec4 color;
varying vec4 normal;

void main(void)
{
  float light = normal.w + max(0.15*dot(normal.xyz, vec3(1,1,1)), 0.0);
  gl_FragColor = vec4(color.xyz * light, 1.0);
}

If I remove the light from the gl_FragColor vec4 then the artifact disappears. The light value is calculated from the ambient occlusion value (normal.w) and a specular component. It seems like it's the specular that's causing the problem. Why would corners and edges suddenly flash like this? The white pixels appear to shimmer as the mesh is rotated. But on larger surfaces, the specular highlights appear normal and follow the light source rather than flicker on and off.

Reed G. Law
  • 3,897
  • 1
  • 41
  • 78
  • 1
    Looks a little like T-Junction errors. Does this _only_ happen at vertex boundaries? – Andon M. Coleman Aug 31 '13 at 00:00
  • It appears that way. But I'm not using any textures yet. Are t-junction errors possible if only blending colors? – Reed G. Law Aug 31 '13 at 02:09
  • 2
    Well, T-Junctions occur whenever you have a vertex that lies on, but is not part of another edge. Errors occur when there's not enough sub-pixel precision to properly identify the vertice's relationship to the other edge. They're really common on older hardware (i.e. PS1), less common these days. As for how to solve them, if these are all separate cubes you could try to pre-process them and weld the shared vertices. – Andon M. Coleman Aug 31 '13 at 02:41
  • There are a lot of overlapping vertices with voxel-based meshes. I am using a greedy meshing algorithm to remove much of the redundancy. But there must be some overlap because the ambient occlusion values are stored per vertex and there may be different values for adjacent facets that share a common vertex. There may even be different textures on adjacent blocks. Do you have any suggestions on how to preserve the blending on welded blocks? Do I need to find the optimum triangle mesh for every surface? – Reed G. Law Sep 01 '13 at 02:14

1 Answers1

2

Now that you mention it, if you followed the normal solution for T-Junction elimination, which is to insert vertices at T-Junctions you would basically undo the benefits of greedy meshing. I see this has already been discussed here with no actual solution proposed. Apparently the argument there was that the issue doesn't prop up enough on most hardware to warrant any further investigation.

In your case, it almost seems that the issue does not pop up during the actual rasterization of polygon edges (T-Junction errors), but rather when you try to compute values in your fragment shader that rely on per-vertex interpolation. I see that you are clearing your color buffer to red, so it is even less likely that these are sub-pixel T-Junction errors during rasterization in that case. Upon closer inspection, I notice some discontinuities along your diagonal triangle edges (the normals seem to be flipped for half of your face). Perhaps the issue is actually related to large discrepancies in some input vertex attribute. I'd try to fix-up the lighting to produce smooth results across the entire face first, maybe whatever is causing that is also causing the issues at T-Junctions.

In fact, would it be possible to include your vertex shader in the question? I am curious to see how normal.w is computed.


UPDATE:

I ported your code to OS X (harder than you'd think :P), with the same results:

OpenGL renderer string: NVIDIA GeForce GT 330M OpenGL Engine
OpenGL version string: 2.1 NVIDIA-1.6.36
OpenGL shading language version string: 1.20

OSX Screenshot

After making the following changes to the vertex shader:

//normal = v_normal;
normal = vec4 (normalize (v_normal.xyz), v_normal.w);

and fragment shader:

//float light = normal.w * max (0.15*dot(normal.xyz, vec3 (1,1,1)), 0.0);
float light = normal.w * max (1.5*dot(normal.xyz, vec3 (1,1,1)), 0.0);

The sparklies disappear and this is the result:

enter image description here

Your ambient occlusion term still seems to be reversed for half of your triangulated faces, however.

Community
  • 1
  • 1
Andon M. Coleman
  • 42,359
  • 2
  • 81
  • 106
  • All my code is here: http://github.com/reedlaw/cubes Currently it doesn't properly compute ambient occlusion values as you can see from the screenshot. I'm in the midst of implementing ambient occlusion using a naive meshing algorithm that simply creates one cube for every voxel. I haven't encountered the t-junction artifacts in the new version yet, so you could be right that it's due to discrepancies in vertex attributes. – Reed G. Law Sep 01 '13 at 06:24
  • Here is the specific code that produced the `normal.w` values: https://github.com/reedlaw/cubes/blob/a0cd0620371489a29b7ed53eb38c7c1cb6e2dfd6/meshers/greedy.cpp#L86-L109 As you can see, they are simply static values between 0.0 and 1.0. This was just to test the effect before I implement a proper ambient occlusion algorithm. I have tested the normal direction by exporting meshes in `.obj` format and visualizing normals in Blender. They appear correctly and my shaders work fine with simple ambient lighting. – Reed G. Law Sep 01 '13 at 06:34
  • 1
    @ReedG.Law: Problem solved, send `vec4 (normalize (v_normal.xyz), v_normal.w)` to your fragment shader. And then adjust your specular factor in the fragment shader to get the same level of shininess. But you still have an issue with the direction of your ambient occlusion term on the two triangles that compose each face. – Andon M. Coleman Sep 01 '13 at 07:23
  • Wow, thanks for the update! Feel free to issue a pull request with your OSX port. I'm still working on the ambient occlusion values, but I'm very grateful for the fixes. – Reed G. Law Sep 06 '13 at 04:11