OpenGL shading doesn't work with different projection and view matrices?

I found an online (public-domain) code to do marching cubes, and I am trying to adapt this code to work in my own project. The code uses GLUT and "older-style" OpenGL, whereas I am using GLM and GLEW/GLFW. So far I have been able to adapt the whole thing, except for one thing: the MVP matrices. I am not able to use a perspective projection matrix instead of an orthographic projection matrix, for some reason. (Or, it may be the view matrix that is messing things up, but every view matrix I've tried has had the same problem). The vertices display fine, but the shading is off in a weird way. Here are some pictures:

The matrices here were generated by

glm::mat4 Model2 = glm::mat4(1.0f);
glm::mat4 View2 = glm::lookAt(glm::vec3(a,b,c),glm::vec3(0,0,0),glm::vec3(0,1,0));
glm::mat4 Projection2 = glm::perspective(45.0f, 4.0f/3.0f, 1.0f, 100.0f);
glm::mat4 MVP2 = Projection2*View2*Model2;


, where I varied a, b, and c in the view matrix position in different runs.

(The isosurface here that marching cubes runs on is just x^2+y^2+z^2 = val. The color of all vertices is red [1, 0, 0].)

As you can see, the first three images look normal except there are strange dark spots in a squarish pattern around the edges. The last image doesn't have this problem but has an even stranger one: There is only one light source, but the dark part seems to be reflecting light, even though the whole sphere is actually the same red color pre-shading! (I reduced the ambient color for this one, to better display the shading perversion).

In contrast, when I use the original projection matrix from the marching cubes code (found here: http://paulbourke.net/geometry/polygonise/marchingsource.cpp)

float fHalfWorldSize = sqrt(2)/2;
float fAspect = 1024./768.;
glOrtho(-fHalfWorldSize*fAspect, fHalfWorldSize*fAspect, -fHalfWorldSize,
fHalfWorldSize, -10*fHalfWorldSize, 10*fHalfWorldSize);


The modelview matrix was the identity translated by

glTranslatef(-0.5, -0.5, -0.5);


The MVP was just Projection*Modelview.

So, the way I see it, either there must be something wrong with my shader, or I am not passing in the matrices correctly. I am using the same shaders in both the good and bad runs, though, and just the projection and view matrices were different.

This is how I set up my shader in OpenGL:

    glUseProgram(programID3);
glUniformMatrix4fv(MatrixID3, 1, GL_FALSE, &MVP[0][0]);
glUniformMatrix4fv(ModelMatrixID3, 1, GL_FALSE, &Model[0][0]);
glUniformMatrix4fv(ViewMatrixID3, 1, GL_FALSE, &View[0][0]);
glm::vec3 lightPos = glm::vec3(0,-1,-1);
glUniform3f(LightID3, lightPos.x, lightPos.y, lightPos.z);


where programID3 is where the shaders are loaded, and MVP/Model/View are changed to MVP2/Model2/View2 to use my glm-generated projection, view, model, and MVP matrices.

#version 120

// Input vertex data, different for all executions of this shader.
attribute vec3 vertexPosition_modelspace;
attribute vec3 vertexColor;
attribute vec3 vertexNormal_modelspace;

// Output data ; will be interpolated for each fragment.
varying vec3 Position_worldspace;
varying vec3 Normal_cameraspace;
varying vec3 EyeDirection_cameraspace;
varying vec3 LightDirection_cameraspace;
varying vec3 fragmentColor;

// Values that stay constant for the whole mesh.
uniform mat4 MVP;
uniform mat4 V;
uniform mat4 M;
uniform vec3 LightPosition_worldspace;

void main(){
// Output position of the vertex, in clip space : MVP * position
gl_Position =  MVP * vec4(vertexPosition_modelspace,1);
// Position of the vertex, in worldspace : M * position
Position_worldspace = (M * vec4(vertexPosition_modelspace,1)).xyz;
// Vector that goes from the vertex to the camera, in camera space.
// In camera space, the camera is at the origin (0,0,0).
vec3 vertexPosition_cameraspace = ( V * M * vec4(vertexPosition_modelspace,1)).xyz;
EyeDirection_cameraspace = vec3(0,0,0) - vertexPosition_cameraspace;
// Vector that goes from the vertex to the light, in camera space. M is omitted because it's identity.
vec3 LightPosition_cameraspace = ( V * vec4(LightPosition_worldspace,1)).xyz;
LightDirection_cameraspace = LightPosition_cameraspace + EyeDirection_cameraspace;
// Normal of the the vertex, in camera space
Normal_cameraspace = ( V * M * vec4(vertexNormal_modelspace,0)).xyz; // Only correct if ModelMatrix does not scale the model ! Use its inverse transpose if not.
fragmentColor = vertexColor;
}


#version 120

// Interpolated values from the vertex shaders
varying vec3 fragmentColor;
varying vec3 Position_worldspace;
varying vec3 Normal_cameraspace;
varying vec3 EyeDirection_cameraspace;
varying vec3 LightDirection_cameraspace;

// Values that stay constant for the whole mesh.
uniform mat4 MV;
uniform vec3 LightPosition_worldspace;

void main(){

// Light emission properties
// You probably want to put them as uniforms
vec3 LightColor = vec3(1,1,1);
float LightPower = 30.0f;

// Material properties
vec3 MaterialDiffuseColor = fragmentColor;
vec3 MaterialAmbientColor = vec3(0.71,0.71,0.71) * MaterialDiffuseColor;
vec3 MaterialSpecularColor = vec3(0.1,0.1,0.1);

// Distance to the light
float distance = length( LightPosition_worldspace - Position_worldspace );

// Normal of the computed fragment, in camera space
vec3 n = normalize( Normal_cameraspace );
// Direction of the light (from the fragment to the light)
vec3 l = normalize( LightDirection_cameraspace );
// Cosine of the angle between the normal and the light direction,
// clamped above 0
//  - light is at the vertical of the triangle -> 1
//  - light is perpendicular to the triangle -> 0
//  - light is behind the triangle -> 0
float cosTheta = clamp( dot( n,l ), 0,1 );

// Eye vector (towards the camera)
vec3 E = normalize(EyeDirection_cameraspace);
// Direction in which the triangle reflects the light
vec3 R = reflect(-l,n);
// Cosine of the angle between the Eye vector and the Reflect vector,
// clamped to 0
//  - Looking into the reflection -> 1
//  - Looking elsewhere -> < 1
float cosAlpha = clamp( dot( E,R ), 0,1 );

gl_FragColor.rgb =
// Ambient : simulates indirect lighting
MaterialAmbientColor +
// Diffuse : "color" of the object
MaterialDiffuseColor * LightColor * LightPower * cosTheta / (distance*distance) +
// Specular : reflective highlight, like a mirror
MaterialSpecularColor * LightColor * LightPower * pow(cosAlpha,5) / (distance*distance);

gl_FragColor.a = 1.0;

}


Does anyone have any idea why the same normals work in one case and not the other?

• You need to be careful just getting the xyz of a vec4 in your shaders because if w is not 1, then xyz is not the exact 3D position. – fastinvsqrt Aug 6 '14 at 16:11
• @fastinvsqrt Right, but doesn't multiplying by the MVP matrix remedy that? At any rate, the positions are all correct, it's the shading that is messed up. – kevin james Aug 6 '14 at 18:10
• It would, but you're completely disregarding the w component in your vertex shader (for light and normal calculations too), which could cause your errors. – fastinvsqrt Aug 6 '14 at 19:10
• @fastinvsqrt Interesting. The site I got these shaders from uses them to display things using perspective projection matrices, and they seem to work fine for those, so I'm still confused why they don't work here (I am convinced the normals themselves are correct). Anyway, can you suggest how I should properly use the w component for lighting calculations? – kevin james Aug 6 '14 at 23:06
• If you copied the shaders exactly from the site and they work there, then I actually don't know why they wouldn't work for you here. To correct for a non-one w component, though, you simply divide the entire vector by w before retrieving the xyz components. Also, I re-read your question and using glm::ortho didn't work for you? – fastinvsqrt Aug 6 '14 at 23:41