I followed a tutorial to add simple diffuse lighting, but the lighting is very much broken:
On top of being inconsistent, all the diffuse component completely disappears at some camera angles (camera position seems to have no effect on this)
The vertex shader:
#version 450 core
layout (location = 0) in vec4 vPosition;
layout (location = 1) in vec4 vNormal;
layout (location = 2) out vec4 fNormal;
layout (location = 3) out vec4 fPos;
uniform mat4 MVMatrix;
uniform mat4 PMatrix;
void main()
{
gl_Position = PMatrix * (MVMatrix * vPosition);
fNormal = normalize(inverse(transpose(MVMatrix))*vNormal);
fPos = MVMatrix * vPosition;
}
Fragment shader:
#version 450 core
layout (location = 0) out vec4 fColor;
layout (location = 2) in vec4 fNormal;
layout (location = 3) in vec4 fPos;
uniform vec4 objColour;
void main()
{
vec3 lightColour = vec3(0.5, 0.0, 0.8);
vec3 lightPos = vec3(10, 20, 30);
float ambientStrength = 0.4;
vec3 ambient = ambientStrength * lightColour;
vec3 diffLightDir = normalize(lightPos - vec3(fPos));
float diff = max(dot(vec3(fNormal), diffLightDir), 0.0);
vec3 diffuse = diff * lightColour;
vec3 rgb = (ambient + diffuse) * objColour.rgb;
fColor = vec4(rgb, objColour.a);
}
Normal calculation (Due to the pythonnic nature, I did not follow a tutorial and this is probably the issue)
self.vertices = np.array([], dtype=np.float32)
self.normals = np.array([], dtype=np.float32)
data = Wavefront(r"C:\Users\cwinm\AppData\Local\Programs\Python\Python311\holder.obj", collect_faces=True)
all_vertices = data.vertices
for mesh in data.mesh_list:
for face in mesh.faces:
face_vertices = np.array([all_vertices[face[i]] for i in range(3)])
normal = np.cross(face_vertices[0]-face_vertices[1], face_vertices[2] - face_vertices[1])
normal /= np.linalg.norm(normal)
self.vertices = np.append(self.vertices, face_vertices)
for i in range(3): self.normals = np.append(self.normals, normal)
self.index = index_getter(len(self.vertices))
self.vertices.resize((len(self.vertices)//3, 3))
self.vertices = np.array(self.vertices * [0.5, 0.5, 0.5], dtype=np.float32)
(The local vertices and normals are then appended to a global vertex and normal buffer, which is pushed to OpenGL after initialisation)
VBO creation (also probably a problem)
vPositionLoc = glGetAttribLocation(self.program, "vPosition")
vNormalLoc = glGetAttribLocation(self.program, "vNormal")
self.Buffers[self.PositionBuffer] = glGenBuffers(1)
glBindBuffer(GL_ARRAY_BUFFER, self.Buffers[self.PositionBuffer])
glBufferStorage(GL_ARRAY_BUFFER, self.vertices.nbytes, self.vertices, 0)
glVertexAttribPointer(vPositionLoc, 3, GL_FLOAT, False, 0, None)
glEnableVertexAttribArray(vPositionLoc)
self.Buffers[self.NormalBuffer] = glGenBuffers(1)
glBindBuffer(GL_ARRAY_BUFFER, self.Buffers[self.NormalBuffer])
glBufferStorage(GL_ARRAY_BUFFER, self.normals.nbytes, self.normals, 0)
glVertexAttribPointer(vNormalLoc, 3, GL_FLOAT, False, 0, None)
glEnableVertexAttribArray(vNormalLoc)
Ambient lighting, the matrices, and the vertex processing is all functional, things only broke when I added normals and (attempted) diffuse lighting
Problem here is that normal here has type np.float64
.
normal = np.cross(face_vertices[0]-face_vertices[1], face_vertices[2] - face_vertices[1])
normal /= np.linalg.norm(normal)
And self.normals
in the following code are np.float64
but not np.float32
as you expect.
To fix it, you can do something like that:
normal = np.cross(face_vertices[0]-face_vertices[1], face_vertices[2] - face_vertices[1])
normal /= np.linalg.norm(normal)
flt_normal = np.array(normal, dtype=np.float32)
self.vertices = np.append(self.vertices, face_vertices)
for i in range(3): self.normals = np.append(self.normals, flt_normal)
With regards to shaders, you indeed need to use mat3x3
for vec3
normal transformation. Additionally, transpose
is equal to inverse
for rotation matrices, so inverse(transpose(Rot)) = Rot
. So it looks redundant, you can rewrite it as:
fNormal.xyz = mat3x3(MVMatrix) * normalize(vNormal.xyz);
And don't forget to check normal direction in the fragment shader. It looks like it should be:
float diff = max(dot(-vec3(fNormal), diffLightDir), 0.0);