自制OBJ加载器加载Blender导出模型时光照异常,如何正确设置法向量
问题成因
- 格式串读取错误:代码中fscanf读取顶点、uv、法向量和面的格式串末尾错误使用
%fn、%dn,缺少转义反斜杠,实际会将换行符前的字符误判为有效数值的一部分,导致读取到的法向量(以及顶点、uv)数值错误,是光照异常的核心原因。 - 缺少参数匹配校验:读取面数据时未校验fscanf的返回值,若导出的obj文件格式不匹配
v/vt/vn的三面结构(比如未导出法向量、导出的面是四边形等),会读取到垃圾值存入索引数组,后续访问法向量数组时可能出现越界或取到错误值的问题。 - 渲染阶段处理缺失:如果渲染管线中未对法向量做归一化处理、未使用逆转置的模型矩阵变换法向量,也会导致法向量方向/长度错误,引发光照异常。
正确设置obj文件法向量的方法
1. 导出阶段(Blender操作)
- 导出obj文件前,先给模型设置对应着色模式:需要硬边就设置平直着色,需要平滑效果就设置平滑着色
- 导出时在「几何体」选项栏中勾选「法线」,确保导出的obj文件包含
vn开头的法向量数据行
2. 加载阶段(代码修正)
- 修正所有fscanf的格式串错误:将
%f %f %fn改为%f %f %f\n,%d/%d/%d %d/%d/%d %d/%d/%dn改为%d/%d/%d %d/%d/%d %d/%d/%d\n,保证数值读取正确 - 增加格式校验逻辑:读取面时判断fscanf返回值是否等于9,只有匹配成功才将索引存入对应数组,避免垃圾值干扰
- 若obj未自带法向量,可手动计算:对每个三角面,取两条边向量做叉乘得到面法向量;需要平滑法向量时,将共享同一顶点的所有面的法向量求和后做归一化,得到该顶点的平滑法向量
3. 渲染阶段(管线处理)
- 顶点着色器中,法向量需要乘以模型矩阵的逆转置矩阵再传入片元着色器,抵消模型变换(尤其是非均匀缩放)对法向量方向的影响
- 片元着色器中做光照计算前,用
normalize()函数对插值后的法向量做归一化,保证光照计算的准确性
内容的提问来源于stack exchange,提问作者user12419619
相关产品推荐
相关产品推荐

