GPU友好矩阵(坐标精度优化)

背景:大坐标情况下,实例化模型抖动问题处理:

背景:为什么 instancedMesh + 大坐标会抖?

你应该已经踩过这个坑了 👇

  • world 坐标:(100000.123456, 0, 0)

  • GPU 用的是 32-bit float

  • float 在 10⁵ 量级时:

    • 最小可区分单位 ≈ 0.01 ~ 0.02

  • 结果就是:

    • 摄像机动一点

    • MVP 矩阵每帧算出来的结果不稳定

    • 顶点在像素级来回跳 → 抖动

instancedMesh 更惨,因为:

  • instanceMatrix 直接进 shader

  • 没有 per-object 的“局部坐标保护”

 

一句话先给结论

optimizeMatrixPrecision(worldMatrix)
👉 通过“人为降低世界矩阵中 position 的精度”,减少大坐标传入 GPU 后产生的浮点误差,从而缓解 instancedMesh / 大场景下的抖动问题。

它不是让精度更高,而是:
让 CPU 和 GPU 在“同一套低精度规则下工作”

 

GPU友好矩阵处理:

  optimizeMatrixPrecision(worldMatrix) {                                                  
    // 1. 提取位置分量                                                                    
    worldMatrix.decompose(pos, quat, scale);                                              
                                                                                          
    // 2. 判断是否为大坐标                                                                
    if (Math.abs(pos.x) > 10000 || ...) {                                                 
      // 3. 舍入到固定精度                                                                
      pos.x = Math.round(pos.x / 0.001) * 0.001;                                          
    }                                                                                     
                                                                                          
    // 4. 重新组合矩阵                                                                    
    return compose(pos, quat, scale);                                                     
  }                                                                                       

 

2️⃣ 判断是否为「大坐标」

 
if (Math.abs(pos.x) > 10000 || ...) {

为什么是 10000?

这是一个经验阈值

坐标量级 float 精度
1 ~ 100 非常精确
1000 开始丢小数
10000 明显抖动
100000 灾难

👉 超过这个值,再保留很多小数 毫无意义

3️⃣ 舍入到固定精度(关键步骤)

 
pos.x = Math.round(pos.x / 0.001) * 0.001;

这一步非常重要,你要从“反直觉”理解它:

❌ 不是为了提高精度

✅ 是为了 “稳定精度”


举个非常直观的例子

原始 position(每帧略有差异)

100000.123456
100000.123463
100000.123451

GPU float 实际能表示的可能是:

100000.12
100000.14
100000.11

👉 每帧映射到不同 float → 抖


经过你这一步“量化”之后

100000.123 → 100000.123
100000.123 → 100000.123
100000.123 → 100000.123

👉 CPU 先统一规则
👉 GPU 每帧拿到的是同一个 float

这个 0.001 是什么?

这是你在定义:

“我允许这个世界里最小的空间单位是多少”

  • 0.001 → 1 毫米

  • 0.01 → 1 厘米(更稳)

  • 0.1 → 10 厘米(超稳)

📌 越粗糙 → 越稳定

这个 API 实际上解决了什么?

✅ 能解决的

  • instancedMesh 在大坐标下:

    • 顶点抖动

    • 贴图 jitter

    • 线条闪烁

  • 摄像机轻微移动时模型晃动

  • instanceMatrix 每帧浮点不一致

具体实例:

/**
   * 对矩阵进行大坐标精度优化
   * 通过固定精度舍入来减少 Float32 精度损失
   * @param {THREE.Matrix4} worldMatrix - 世界坐标矩阵
   * @returns {THREE.Matrix4} 精度优化后的矩阵
   */
  optimizeMatrixPrecision(worldMatrix) {
    const pos = new THREE.Vector3();
    const quat = new THREE.Quaternion();
    const scale = new THREE.Vector3();
    worldMatrix.decompose(pos, quat, scale);

    // 只对大坐标进行精度舍入
    const needsPrecisionOpt =
      Math.abs(pos.x) > this.precisionScale ||
      Math.abs(pos.y) > this.precisionScale ||
      Math.abs(pos.z) > this.precisionScale;

    if (needsPrecisionOpt) {
      // 舍入到固定精度,避免累积误差
      pos.x = Math.round(pos.x / this.precisionStep) * this.precisionStep;
      pos.y = Math.round(pos.y / this.precisionStep) * this.precisionStep;
      pos.z = Math.round(pos.z / this.precisionStep) * this.precisionStep;
    }

    const optimizedMatrix = new THREE.Matrix4();
    optimizedMatrix.compose(pos, quat, scale);
    return optimizedMatrix;
  }

 

posted @ 2026-02-02 11:52  SimoonJia  阅读(0)  评论(0)    收藏  举报