GLSL点积实现漫反射光照时Z轴方向光效异常问题求助
问题根因
你遇到的问题核心是参与点积计算的法向量和光方向处于不同坐标系,其次存在插值后向量未重归一化的精度问题,具体原因如下:
- 你代码中直接使用顶点输入的
aNormal,它是模型本地空间的法向量,而你传入的lightDirection属于世界空间,两个不同坐标系下的向量做 dot 计算没有物理意义。 - 前两种光方向测试刚好正常的原因是你的模型初始姿态下,本地空间与世界空间的X/Y轴完全对齐,所以X/Y轴向的光计算结果碰巧符合预期;而Z轴向光因为坐标系不匹配,点积结果始终小于等于0,被
max(..., 0.0f)截断为0,表现为完全失效;[0, 1, 1]方向光的Z分量在点积时无有效贡献,最终效果和纯Y轴向的[0, 1, 0]完全一致,也完全匹配坐标系不统一的故障特征。 - 额外问题:顶点着色器中归一化的法向量经过光栅器插值后,长度会发生变化,直接在片元着色器使用会导致亮度计算存在误差。
修复方案
这里提供两种可行修复路径,二选一即可:
路径1:将法向量转换到世界空间(常用方案)
- CPU侧计算法线矩阵:法线矩阵为模型矩阵左上角3x3部分的逆矩阵的转置,避免模型带非均匀缩放时法向量变形,计算完成后作为
uniform mat3 normalMatrix传入着色器。 - 修改顶点着色器:
#version 330 core layout (location = 0) in vec3 aPos; layout (location = 1) in vec3 aNormal; uniform mat4 model; uniform mat4 view; uniform mat4 projection; uniform mat3 normalMatrix; // 新增法线矩阵uniform uniform vec3 lightDirection; out vec3 normal; out vec3 lightDir; void main() { normal = normalMatrix * aNormal; // 法向量转换到世界空间,归一化移到片元着色器 lightDir = normalize(lightDirection); // 方向光为全局常量,这里归一化即可 gl_Position = projection * view * model * vec4(aPos, 1.0f); }
- 修改片段着色器,对插值后的法向量重归一化:
#version 330 core in vec3 normal; in vec3 lightDir; out vec4 FragColor; void main() { float grey = max(dot(-lightDir, normalize(normal)), 0.0f); FragColor = vec4(grey, grey, grey, 1.0f); }
路径2:将光方向转换到模型本地空间(性能更优,适合静态方向光)
CPU侧将世界空间的光方向乘以法线矩阵,得到本地空间的光方向作为uniform传入着色器,不需要修改法向量的计算逻辑,仅需在片元着色器对法向量重归一化即可,代码改动量更小。
内容的提问来源于stack exchange,提问作者Debaug
相关产品推荐
相关产品推荐

