0
\$\begingroup\$

I'm trying to use WebGL to render some simple models, and I'm running into issues where pixels near the edge of my model are passing normals to my fragment shader that point away from the camera. This is of course messing with my attempts to use physically-based shading, because BRDFs are only defined in the hemisphere around the normal vector. These normals aren't just slightly pointing away from the screen; if that was the case, I would chalk it up to floating-point error and clamp to acceptable values. But some of my pixels have v-dot-n values of -0.5 or less.

This happens on pretty much any mesh I try to render. I have verified that my vertex normals point away from the mesh. I have backface culling turned on. This happens with both perspective and orthographic projections. I have tested in both Firefox and Chrome. Everything else about my render looks fine.

My vertex shader looks like this:

precision mediump float;

uniform mat4 M;
uniform mat4 V;
uniform mat4 P;

attribute vec3 vertexPos_model;
attribute vec3 vertexNormal_model;

varying vec3 pos_world;
varying vec3 normal_world;

void main() {
    gl_Position = P * V * M * vec4(vertexPos_model, 1.0);
    pos_world = (M * vec4(vertexPos_model, 1.0)).xyz;
    // We can use M here instead of its inverse transpose because it does not scale the model.
    normal_world = mat3(M) * vertexNormal_model;
}

And my fragment shader looks like this:

precision mediump float;

uniform vec3 cameraPos_world;

varying vec3 pos_world;
varying vec3 normal_world;

void main() {
    vec3 n = normalize(normal_world);
    vec3 v = normalize(cameraPos_world - pos_world);
    float vAngleCos = dot(n, v);
    // This will highlight pixels whose view vectors are outside of the hemisphere around n.
    gl_FragColor = vec4(vec3(clamp(-vAngleCos, 0.0, 1.0)), 1.0);
}

Rendering with the fragment shader above gives me results like this:

Normal issues

Apologies if I'm missing something obvious!

\$\endgroup\$
4
\$\begingroup\$

You are seeing interpolation artifacts. The surface normals between front and back faces are interpolated between front and back faces, leaving some of the fragments on visible faces having normals facing away from the camera.

Consider this crude drawing:

Crude Drawing

In this image A and B are your vertex normals while C is an interpolated surface normal between them. C in this case is pointing away from the camera, even though it is part of a front face.

\$\endgroup\$
  • \$\begingroup\$ Hmm. I thought that might be the case but I had a hard time visualizing it. Nice diagram! Is there a good way of handling this without abandoning Phong shading? \$\endgroup\$ – Aaron Rotenberg Dec 20 '13 at 16:24
  • 1
    \$\begingroup\$ Since it affects only a few pixels and only a little I would just clamp the dot product. I doubt anyone will notice the difference. ;) \$\endgroup\$ – kolrabi Dec 20 '13 at 16:33
  • \$\begingroup\$ I was hoping that wouldn't be the answer, because that makes some of my Fresnel effects look visibly weird. But I guess if I put in normal mapping I'm going to have to handle this case somehow anyway. Thanks. \$\endgroup\$ – Aaron Rotenberg Dec 20 '13 at 18:21

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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