深度冲突(模型闪烁)
此文章通过一个比较简单的案例,来给大家展示一下实际开发的过程中,你可能会遇到的模型闪烁问题对于模型闪烁的原因简单地说就是深度冲突,对应的英文关键词是 Z-fighting
Mesh 面重合渲染测试
下面代码创建两个重合的矩形平面 Mesh,通过浏览器预览,当你旋转三维场景的时候,你会发现模型渲染的时候产生闪烁
这种现象,主要是两个 Mesh 重合,电脑 GPU 分不清谁在前谁在后,这种现象可以称为深度冲突 Z-fighting
// 两个矩形平面Mesh重合,产生闪烁
// 闪烁原因:两个矩形面位置重合,GPU无法分清谁在前谁在后
const geometry = new THREE.PlaneGeometry(250, 250);
const material = new THREE.MeshLambertMaterial({
color: 0x00ffff,
side: THREE.DoubleSide,
});
const mesh = new THREE.Mesh(geometry, material);
const geometry2 = new THREE.PlaneGeometry(300, 300);
const material2 = new THREE.MeshLambertMaterial({
color: 0xff6666,
side: THREE.DoubleSide,
});
const mesh2 = new THREE.Mesh(geometry2, material2);
将两个矩形 Mesh 拉开距离
适当偏移,可以解决深度冲突,偏移尺寸相对模型尺寸比较小,视觉上两个平面近似还是重合效果
mesh2.position.z = 1;
间隙很小,也会产生深度冲突
当两个面间隙很小,也可能出现深度冲突,从纯理论的角度,你能分清 0 和 0.0000...0000001 的大小,但是实际上,电脑 GPU 精度是有限的
// 当两个面间隙很小,也可能出现深度冲突。
mesh2.position.z = 0.0000000000000000000001;
透视投影相机对距离影响(深度冲突)
第 1 步:设置两个 Mesh 平面的距离相差 0.1,文章对应案例源码你可以看到,没有深度冲突导致的模型闪烁问题
mesh2.position.z = 0;
mesh2.position.z = 0.1;
camera.position.set(292, 223, 185);
第 2 步:改变相机.position 属性,你会发现当相机距离三维模型较远的时候,两个面也可能出现深度冲突,当然你也可以通过相机控件 OrbitControls 缩放功能,改变相机与模型的距离,进行观察
camera.position.set(292 * 5, 223 * 5, 185 * 5);
透视投影相机的投影规律是远小近大,和人眼观察世界一样,模型距离相机越远,模型渲染的效果越小,两个 mesh 之间的间距同样也会变小,当两个 Mesh 和相机距离远到一定程度,两个模型的距离也会无限接近 0
通过 webgl 渲染器设置对数深度缓冲区
两个矩形平面距离比较近,相差 0.1
mesh2.position.z = 0;
mesh2.position.z = 0.1;
camera.position.set(292 * 5, 223 * 5, 185 * 5);
当一个三维场景中有一些面距离比较近,有深度冲突,你可以尝试设置 webgl 渲染器设置对数深度缓冲区 logarithmicDepthBuffer: true 来优化或解决,logarithmicDepthBuffer: true 作用简单来说,就是两个面间距比较小的时候,让 threejs 更容易区分两个面,谁在前,谁在后
// WebGL渲染器设置
const renderer = new THREE.WebGLRenderer({
// 设置对数深度缓冲区,优化深度冲突问题
logarithmicDepthBuffer: true,
});
有一点要注意,当两个面间隙过小,或者重合,你设置 webgl 渲染器对数深度缓冲区也是无效的
mesh2.position.z = 0;
// 当两个面重合,logarithmicDepthBuffer: true无效
mesh2.position.z = 0;
// 当两个面间隙过小,logarithmicDepthBuffer: true无效
mesh2.position.z = 0.00001;
camera.position.set(292 * 5, 223 * 5, 185 * 5);