带法线贴图的物体旋转时保持光照正确的解决方案
解决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
相关产品推荐
相关产品推荐

