OpenGL漫反射光照异常:效果不一致且部分视角下消失
漫反射光照异常问题
添加简单漫反射光照后出现以下问题:
- 物体表面光照效果不均匀
- 部分视角下漫反射分量完全消失(相机位置无明显影响)
顶点着色器
#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; }
片段着色器
#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); }
法线计算代码(Python)
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)
注:局部顶点和法线随后被追加到全局顶点和法线缓冲区,初始化后推送至OpenGL
VBO创建代码(Python)
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)
环境光、矩阵及顶点处理均正常,仅添加法线和漫反射光照后出现问题。
问题排查与修复
1. 法线方向错误
当前叉乘顺序可能导致法线朝向面的内侧,不符合OpenGL右手坐标系要求。修改法线计算的叉乘顺序:
normal = np.cross(face_vertices[1]-face_vertices[0], face_vertices[2]-face_vertices[0])
确保法线指向面外侧,避免因法线反向导致漫反射分量被max(dot(...), 0.0)过滤为0。
2. 法线变换的空间与类型错误
顶点着色器中输入的法线是3分量,但声明为vec4,且变换时未处理w分量。修改顶点着色器:
layout (location = 1) in vec3 vNormal; layout (location = 2) out vec3 fNormal; // ... fNormal = normalize(vec3(inverse(transpose(MVMatrix)) * vec4(vNormal, 0.0)));
法线是方向向量,w分量设为0可避免平移变换影响。
3. 光照空间不匹配
lightPos是世界空间坐标,但fPos是视图空间坐标(由MVMatrix转换),导致光照方向计算混乱。推荐将fPos改为世界空间坐标:
// 顶点着色器新增uniform uniform mat4 ModelMatrix; uniform mat4 ViewMatrix; void main() { vec4 worldPos = ModelMatrix * vPosition; gl_Position = PMatrix * ViewMatrix * worldPos; fNormal = normalize(vec3(inverse(transpose(ModelMatrix)) * vec4(vNormal, 0.0))); fPos = worldPos; // 输出世界空间位置 }
此时片段着色器中lightPos与fPos处于同一空间,光照计算逻辑正确。
4. 缓冲区属性校验
确保绘制前glEnableVertexAttribArray始终启用对应属性,避免因缓冲区绑定切换导致属性被意外禁用。
内容的提问来源于stack exchange,提问作者User 12692182
相关产品推荐
相关产品推荐

