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

带法线贴图的物体旋转时保持光照正确的解决方案

解决PixiJS中旋转精灵后法线贴图光照异常的问题

嘿,我完全懂你遇到的这个坑——旋转带法线贴图的精灵后,光照方向完全反了,比如转180度本该亮右侧结果亮了左侧对吧?这问题本质是法线贴图的光照计算没跟上精灵的旋转变换,咱们一步步来解决:

问题根源

你用的那个示例里,法线贴图的光照计算是基于精灵的局部UV空间的,完全没考虑精灵自身的旋转、缩放这些变换。简单说:精灵转了180度,但法线贴图里的法线向量还是保持原来的方向,相当于“精灵身子转了,皮肤的法线还没转”,光照自然就乱了。

核心解决思路

要让法线方向跟着精灵的变换走,我们需要在片元着色器(Fragment Shader)里,把法线向量乘以精灵变换矩阵的逆转置矩阵(这是图形学里处理法线变换的标准操作,避免缩放导致的法线方向失真)。

具体实现步骤

1. 修改自定义法线着色器

首先,你需要给原示例的着色器新增一个uniform变量,用来传递法线变换矩阵,然后在计算光照前把法线向量做变换:

// 顶点着色器(保持原逻辑,传递UV即可)
attribute vec2 aVertexPosition;
attribute vec2 aTextureCoord;

uniform mat3 projectionMatrix;

varying vec2 vTextureCoord;

void main() {
    gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);
    vTextureCoord = aTextureCoord;
}
// 片元着色器(关键修改)
varying vec2 vTextureCoord;
uniform sampler2D uSampler;
uniform sampler2D uNormalSampler;
uniform vec3 uLightDirection;
uniform mat3 uNormalMatrix; // 新增:法线变换矩阵

void main() {
    // 取基础颜色和法线贴图数据
    vec4 baseColor = texture2D(uSampler, vTextureCoord);
    vec3 normal = texture2D(uNormalSampler, vTextureCoord).rgb * 2.0 - 1.0;
    
    // 把法线从局部空间转换到和精灵匹配的空间
    normal = normalize(uNormalMatrix * normal);
    
    // 计算光照强度(避免负数值导致物体变黑)
    float lightIntensity = max(dot(normal, normalize(uLightDirection)), 0.1);
    
    // 输出最终颜色
    gl_FragColor = vec4(baseColor.rgb * lightIntensity, baseColor.a);
}

2. 在JS代码中计算并传递法线矩阵

每次精灵的旋转、缩放变化后,我们都要计算对应的法线矩阵,然后传递给着色器的uniform:

// 假设你的精灵叫 block1
block1.rotation = Math.PI; // 旋转180度

// 获取精灵的局部变换矩阵
const transform = block1.localTransform;

// 计算变换矩阵的行列式(用于求逆)
const determinant = transform.a * transform.d - transform.b * transform.c;

// 构建法线变换矩阵(逆矩阵的转置)
const normalMatrix = new PIXI.Matrix(
    transform.d / determinant, -transform.b / determinant,
    -transform.c / determinant, transform.a / determinant,
    0, 0
);

// 转换成着色器需要的mat3数组格式
const mat3Array = [
    normalMatrix.a, normalMatrix.b, 0,
    normalMatrix.c, normalMatrix.d, 0,
    0, 0, 1
];

// 把矩阵传递给精灵材质的uniform
block1.material.uniforms.uNormalMatrix = mat3Array;

3. 优化:自动监听变换更新

如果精灵会频繁旋转/缩放,你可以监听精灵的transform变化事件,自动更新法线矩阵:

block1.on('transformupdated', () => {
    const transform = block1.localTransform;
    const determinant = transform.a * transform.d - transform.b * transform.c;
    const normalMatrix = new PIXI.Matrix(
        transform.d / determinant, -transform.b / determinant,
        -transform.c / determinant, transform.a / determinant,
        0, 0
    );
    block1.material.uniforms.uNormalMatrix = [
        normalMatrix.a, normalMatrix.b, 0,
        normalMatrix.c, normalMatrix.d, 0,
        0, 0, 1
    ];
});

为什么要用逆转置矩阵?

简单说:普通的模型变换矩阵会同时处理位置、旋转、缩放,但法线是向量,不是点,缩放会让法线方向失真。用逆矩阵的转置可以保证法线在变换后依然垂直于表面,光照计算才会准确。

这样修改后,你再旋转精灵,法线贴图的光照就会跟着精灵的朝向正确变化了!

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 04:28:04