Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Strange square lighting artefacts in OpenGL

Tags:

c

opengl

lighting

I have a program that generates a heightmap and then displays it as a mesh with OpenGL. When I try to add lighting, it ends up with weird square shapes covering the mesh. They are more noticeable in some areas than others, but are always there.

I was using a quad mesh, but nothing changed after switching to a triangle mesh. I've used at least three different methods to calculate the vertex normals, all with the same effect. I was doing the lighting manually with shaders, but nothing changes when using the builtin OpenGL lighting system.

My latest normal-generating code (faces is an array of indices into verts, the vertex array):

int i;
for (i = 0; i < NINDEX; i += 3) {
    vec v[3];
    v[0] = verts[faces[i + 0]];
    v[1] = verts[faces[i + 1]];
    v[2] = verts[faces[i + 2]];

    vec v1 = vec_sub(v[1], v[0]);
    vec v2 = vec_sub(v[2], v[0]);

    vec n = vec_norm(vec_cross(v2, v1));

    norms[faces[i + 0]] = vec_add(norms[faces[i + 0]], n);
    norms[faces[i + 1]] = vec_add(norms[faces[i + 1]], n);
    norms[faces[i + 2]] = vec_add(norms[faces[i + 2]], n);
}

for (i = 0; i < NVERTS; i++) {
    norms[i] = vec_norm(norms[i]);
}

Although that isn't the only code I've used, so I doubt that it is the cause of the problem.

I draw the mesh with:

glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(3, GL_FLOAT, 0, verts);

glEnableClientState(GL_NORMAL_ARRAY);
glNormalPointer(GL_FLOAT, 0, norms);

glDrawElements(GL_TRIANGLES, NINDEX, GL_UNSIGNED_SHORT, faces);

And I'm not currently using any shaders.

What could be causing this?

EDIT: A more comprehensive set of screenshots:

  • Wireframe
  • Flat shading, OpenGL lighting
  • Smooth shading, OpenGL lighting
  • Lighting done in shader

For the last one, the shader code is

Vertex:

varying vec3 lightvec, normal;

void main() {
    vec3 lightpos = vec3(0, 0, 100);
    vec3 v = vec3(gl_ModelViewMatrix * gl_Vertex);
    normal = gl_NormalMatrix * gl_Normal;

    lightvec = normalize(lightpos - v);

    gl_Position = ftransform();
}

Fragment:

varying vec3 lightvec, normal;

void main(void) {
    float l = dot(lightvec, normal);
    gl_FragColor = vec4(l, l, l, 1);
} 
like image 955
sigill Avatar asked Jun 13 '12 06:06

sigill


2 Answers

You need to either normalize the normal in the fragment shader, like so:

varying vec3 lightvec, normal;

void main(void) {
    vec3 normalNormed = normalize(normal);
    float l = dot(lightvec, normalNormed);
    gl_FragColor = vec4(l, l, l, 1);
} 

This can be expensive though. What will also work in this case, with directional lights, is to use vertex lighting. So calculate the light value in the vertex shader

varying float lightItensity;

void main() {
    vec3 lightpos = vec3(0, 0, 100);
    vec3 v = vec3(gl_ModelViewMatrix * gl_Vertex);
    normal = gl_NormalMatrix * gl_Normal;

    lightvec = normalize(lightpos - v);

    lightItensity = dot(normal, lightvec);

    gl_Position = ftransform();
}

and use it in the fragment shader,

varying float light;

void main(void) {
    float l = light;
    gl_FragColor = vec4(l, l, l, 1);
} 

I hope this fixes it, let me know if it doesn't.

EDIT: Heres a small diagram that explains what is most likely happening

enter image description here

EDIT2:

If that doesn't help, add more triangles. Interpolate the values of your heightmap and add some vertices in between.

Alternatively, try changing your tesselation scheme. For example a mesh of equilateral triangles like so could make the artifacts less prominent.

enter image description here

You'll have to do some interpolation on your heightmap.

Otherwise I have no idea.. Good luck!

like image 94
Hannesh Avatar answered Oct 21 '22 05:10

Hannesh


I don't have a definitive answer for the non-shader versions, but I wanted to add that if you're doing per pixel lighting in your fragment shader, you should probably be normalizing the normal and lightvec inside the fragment shader.

If you don't do this they not be unit length (a linear interpolation between two normalized vectors is not necessarily normalized). This could explain some of the artifacts you see in the shader version, as the magnitude of the dot product would vary as a function of the distance from the vertices, which kind of looks like what you're seeing.

EDIT: Another thought, are you doing any non-uniform scaling (different x,y,z) of the mesh when rendering the non-shader version? If you scale it, then you need to either modify the normals by the inverse scale factor, or set glEnable(GL_NORMALIZE). See here for more: http://www.lighthouse3d.com/tutorials/glsl-tutorial/normalization-issues/

like image 43
Tim Avatar answered Oct 21 '22 03:10

Tim