You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.08 18:35:15