Three.js 模型闪烁(z-fighting)主因是深度缓冲精度不足,导致共面或近距面深度值冲突;可通过调小相机 near 值、启用 antialias、微调 polygonOffset、合并几何体及建模时统一单位并避免面重叠等方法缓解。
模型表面出现快速明暗跳变、边缘抖动或贴图错位,多数是深度缓冲精度不足导致的 z-fighting。本质是两个几何面在相机视角下深度值过于接近,GPU 无法可靠判断谁在前谁在后,于是帧与帧之间随机切换渲染顺序。
常见诱因包括:
– 模型导入时单位不一致(比如 Blender 导出为米,但 Three.js 场景按厘米缩放)
– 使用 MeshStandardMaterial 但未启用 depthWrite: true
– 多个 mesh 共享同一位置(如贴花、法线贴图辅助层、双面材质叠加)
– 相机 near 值设得过大(例如 0.1 但场景最小距离仅 0.001)
不改模型、不重拓扑,也能大幅缓解闪烁——关键是让深度缓冲更“够用”:
WebGLRenderer 的 antialias 设为 true(默认 false),能软化深度采样边缘near 值:若模型最近点距相机约 0.5 单位,near: 0.01 比 0.1 更稳妥far 值不是万能解;反而会压缩深度缓冲有效区间,应尽量设为略大于最远物体的距离(例如 far: 1000 而非 10000)renderer.setDepthTest(true)(默认开启,但自定义渲染循环中可能被误关)对已导出的 glTF/GLB 模型,无需回退建模软件,可通过 Three.js 运行时干预:
mesh.position.z += 0.0001 或 mesh.translateZ(0.0001)
material.depthWrite = false,避免与主模型争深度BufferGeometryUtils.mergeBufferGeometries([geo1, geo2]) 减少 draw call 同时也消除面间微小间隙side: THREE.DoubleSide —— 若非必要,改为 THREE.FrontSide 可减少深度冲突概率Blender / Maya / 3ds Max 导出 glTF 前,这些操作比后期调试更省力:
Unit Scale = 0.01(对应 cm),导出时勾选 Apply Transform
Mesh > Clean Up > Delete Loose)0.001 单位Keep Original Materials(若用 glTF-Transform 工具链,可加 --dedupe 参数去重材质)const loader = new GLTFLoader();
loader.load('model.glb', (gltf) => {
gltf.scene.traverse((child) => {
if (child.isMesh && child.material) {
// 对所有 mesh 统一微调 depth bias
child.material.polygonOffset = true;
child.material.polygonOffsetFactor = 1;
child.material.polygonOffsetUnits = 1;
}
});
scene.add(gltf.scene);
});
真正难处理的闪烁往往藏在动态 LOD 切换、骨骼蒙皮插值、或自定义 shader 的深度计算里——那些地方没有通用解,得看具体 gl_FragDepth 或 varying 插值逻辑是否引入了亚像素抖动。