Skip to content

fix(renderer): normal matrix on singular model matrices contaminates lighting #3010

Description

@GuoLei1990

背景

Renderer._updateTransformShaderData 用 `Matrix.invert` + `transpose` 算 `renderer_NormalMat`:

```ts
// packages/core/src/Renderer.ts:402-404
Matrix.multiply(context.viewMatrix, worldMatrix, mvMatrix);
Matrix.invert(worldMatrix, normalMatrix);
normalMatrix.transpose();
```

当 `worldMatrix` singular(任意一个 scale 分量为 0,比如动画"压扁消失"过渡帧 `scale = (1, 0, 1)`),`Matrix.invert` 的早返保护启动:

```ts
// packages/math/src/Matrix.ts:449-452
let det = ...;
if (!det) {
return null; // ← 不写 out
}
```

`return null` 时 out(`normalMatrix`)不被写入,调用方 line 403 不检查返回值,下一行 `normalMatrix.transpose()` 在保留值上 transpose 一次,最终上传到 GPU。

问题

上传的 `normalMatrix` 是:

  • 第一帧 singular:identity(构造时初始值).transpose() = identity
  • 后续帧 singular:上一帧已 transpose 过的值再 transpose 一次

两种情况都是有效浮点数,不传染 NaN、不崩屏,但法线方向与几何不一致

场景 上传的 normalMatrix 视觉表现
第一帧 singular identity 法线沿对象局部坐标系,未跟随 scale=0 压扁
持续 singular 上一帧 transpose 链结果 法线方向"卡住"在某个旧状态
从非 singular → singular 上一帧正确法线 法线"延迟反应",光照值在过渡帧偏离

严重度

  • 不阻塞渲染(上传的是有效浮点数,没 NaN)
  • 视觉影响在 singular 帧(压扁动画过渡帧)—— 此时物体几何体积接近 0,视觉感知较弱
  • 严重度 P2 (cosmetic),不是 P0

修复方向(与 instance 路径对齐)

instance 路径(GPU shader 端)已在 PR #2957 commit `0805f2d93` 改成 cofactor 形式,代数等价 `det(M) · transpose(inverse(M))`,无除法、对 singular NaN-safe、性能更好。

CPU 路径也应该改为 cofactor 形式:

```ts
// 提议在 Matrix3x3 / Matrix 添加 API,大致形态:
static normalMatrix3FromAffine(affine: Matrix, out: Matrix3x3): void {
const e = affine.elements;
// 取 affine 的 mat3 部分,做 cofactor:
// c0 = cross(m[1], m[2])
// c1 = cross(m[2], m[0])
// c2 = cross(m[0], m[1])
// sign(det) 用三重积修正
...
}
```

Renderer.ts 改:

```ts
// 替换 line 403-404
Matrix3x3.normalMatrix3FromAffine(worldMatrix, this._normalMatrix3);
shaderData.setMatrix3x3(Renderer._normalMatrixProperty, this._normalMatrix3);
```

收益

  • 正确性:第一帧 singular / 持续 singular 都给精确退化结果,跟几何对齐,不依赖"上一帧值还在"
  • 性能:cross + dot 不需要 mat4 inverse 的完整 cofactor 展开 + 除法。每帧每 renderer 省 1 次除法 + ~47 ops
  • 一致性:CPU/GPU 路径行为完全对齐(修完 PR feat(core): GPU instancing auto-batching #2957 后,instance 路径反而比 CPU 路径更准确,这是反常状态)

行业对照

  • Filament: `getWorldFromModelNormalMatrix` 走 cofactor
  • Unreal: `MaterialTemplate.ush` 走 cofactor
  • Three.js: `Matrix3.getNormalMatrix` 走 cofactor (PR #19620+)

Out of scope

  • 本 issue 只描述 CPU 路径,instance 路径已在 PR feat(core): GPU instancing auto-batching #2957 修复
  • 是否同时把 `renderer_MVPMat` / `renderer_MVMat` 的 CPU 端也做对应优化,作为单独 issue
  • math 库新增 API 的命名 / 签名 / 文档需要 reviewer 评审

关联

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions