Three.js 模型闪烁(z-fighting)主因是深度缓冲精度不足,导致共面或近距面深度值冲突;可通过调小相机 near 值、启用 antialias、微调 polygonOffset、合并几何体及建模时统一单位并避免面重叠等方法缓解。

为什么 Three.js 中的模型会闪烁(z-fighting)
模型表面出现快速明暗跳变、边缘抖动或贴图错位,多数是深度缓冲精度不足导致的 z-fighting。本质是两个几何面在相机视角下深度值过于接近,GPU 无法可靠判断谁在前谁在后,于是帧与帧之间随机切换渲染顺序。
常见诱因包括:
– 模型导入时单位不一致(比如 Blender 导出为米,但 Three.js 场景按厘米缩放)
– 使用 MeshStandardMaterial 但未启用 depthWrite: true
– 多个 mesh 共享同一位置(如贴花、法线贴图辅助层、双面材质叠加)
– 相机 near 值设得过大(例如 0.1 但场景最小距离仅 0.001)
Three.js 渲染器和相机的关键参数调整
不改模型、不重拓扑,也能大幅缓解闪烁——关键是让深度缓冲更“够用”:
- 把
WebGLRenderer的antialias设为true(默认 false),能软化深度采样边缘 - 缩小相机
near值:若模型最近点距相机约0.5单位,near: 0.01比0.1更稳妥 - 增大相机
far值不是万能解;反而会压缩深度缓冲有效区间,应尽量设为略大于最远物体的距离(例如far: 1000而非10000) - 启用
renderer.setDepthTest(true)(默认开启,但自定义渲染循环中可能被误关)
模型层面的快速修复手段
对已导出的 glTF/GLB 模型,无需回退建模软件,可通过 Three.js 运行时干预:
- 给易闪烁的 mesh 添加微小偏移:使用
mesh.position.z += 0.0001或mesh.translateZ(0.0001) - 禁用背面深度写入:对半透明或贴花类 mesh,设
material.depthWrite = false,避免与主模型争深度 - 合并共面几何体:用
BufferGeometryUtils.mergeBufferGeometries([geo1, geo2])减少 draw call 同时也消除面间微小间隙 - 检查材质是否启用了
side: THREE.DoubleSide—— 若非必要,改为THREE.FrontSide可减少深度冲突概率
glTF 导出时就该注意的建模习惯
Blender / Maya / 3ds Max 导出 glTF 前,这些操作比后期调试更省力:
- 统一单位:Blender 中设
Unit Scale = 0.01(对应 cm),导出时勾选Apply Transform - 删除隐藏面、零面积面、重复顶点(Blender:编辑模式 →
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 插值逻辑是否引入了亚像素抖动。











