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

OpenGL骨骼动画3D模型光照失效:如何传递正确法线?

骨骼动画模型的法线计算与光照修复问题

问题描述

按照骨骼动画教程实现后,模型动画正常,但运动时光照完全失效(显示黑色),静止时光照正常。核心问题出在顶点着色器中未对法线应用骨骼变换,同时世界空间位置也未更新。

错误根源分析

当前顶点着色器存在三个关键错误:

  1. 直接将原始法线norm传递给片元着色器,完全忽略骨骼动画对顶点的变换,导致运动时法线方向与实际模型表面方向完全不符,光照计算的点积结果为负或0,最终显示黑色。
  2. FragPos使用原始顶点位置pos,而非骨骼变换后的世界空间位置,导致光照计算中的视角方向、光源方向全部错误。
  3. 代码中计算了localNormal但未进行权重累加,该值完全未被使用。

修正方案

需要对法线和世界空间位置都应用骨骼的权重变换,具体步骤如下:

  1. 初始化累加变量存储变换后的法线,和顶点位置一样按骨骼权重累加。
  2. 对每个影响骨骼,使用骨骼矩阵的逆转置矩阵变换法线(确保法线在缩放变换后仍保持垂直于表面),再乘以对应权重累加。
  3. 将骨骼变换后的顶点位置转换到世界空间,作为FragPos传递给片元着色器。
  4. 若模型矩阵包含非均匀缩放,需对变换后的法线额外应用模型矩阵的逆转置。

修正后的顶点着色器代码

#version 330 core

layout(location = 0) in vec3 pos;
layout(location = 1) in vec3 norm;
layout(location = 2) in vec2 tex;
layout(location = 5) in ivec4 boneIds;
layout(location = 6) in vec4 weights;
    
uniform mat4 projection;
uniform mat4 view;
uniform mat4 model;
    
const int MAX_BONES = 100;
const int MAX_BONE_INFLUENCE = 4;
uniform mat4 finalBonesMatrices[MAX_BONES];
    
out vec2 TexCoords;
out vec3 FragPos;
out vec3 Normal;
    
void main()
{
    vec4 totalPosition = vec4(0.0f);
    vec3 totalNormal = vec3(0.0f);
    
    for(int i = 0 ; i < MAX_BONE_INFLUENCE ; i++)
    {
        if(boneIds[i] == -1)
            continue;
        if(boneIds[i] >= MAX_BONES)
        {
            totalPosition = vec4(pos, 1.0f);
            totalNormal = norm;
            break;
        }
        // 计算骨骼变换后的顶点位置并累加权重
        mat4 boneMatrix = finalBonesMatrices[boneIds[i]];
        totalPosition += boneMatrix * vec4(pos, 1.0f) * weights[i];
        // 计算骨骼变换后的法线并累加权重:使用逆转置矩阵确保法线正确变换
        mat3 normalMatrix = transpose(inverse(mat3(boneMatrix)));
        totalNormal += normalMatrix * norm * weights[i];
    }
    
    // 计算世界空间位置
    vec4 worldPos = model * totalPosition;
    FragPos = worldPos.xyz;
    
    // 应用模型矩阵的法线变换(如果model有非均匀缩放,必须用逆转置)
    mat3 modelNormalMatrix = transpose(inverse(mat3(model)));
    Normal = modelNormalMatrix * totalNormal;
    
    mat4 viewModel = view * model;
    gl_Position = projection * viewModel * totalPosition;
    TexCoords = tex;
}

额外优化建议

  • 为提升性能,可在CPU端预计算每个骨骼的法线变换矩阵(即骨骼矩阵逆转置的3x3部分),直接传递给GPU,避免在顶点着色器中重复计算transpose(inverse)。
  • 片元着色器中已对Normal做归一化,顶点着色器无需提前归一化,插值后再归一化能得到更准确的法线方向。

内容的提问来源于stack exchange,提问作者Juroza

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 19:35:26