三角形mesh出错边拓扑结构乱掉导致的,这里大量的建筑模型都出现问题),再重面触发被动fitting! 既然由重面引发的,解决重面就可以Z-Fighting啦。
Z-Buffer
使用深度缓冲(Z-Buffer),Z-Buffer又名 Depth buffer来完成场景可见性计算,即确定场景哪部分可见,哪部分不可见。深度缓冲(Z-Buffer)是一个二维数组,其中的每一个元素对应屏幕上的一个像素,如果场景中的两个模型在同一个像素生成渲染结果,那么图形处理卡就会比较二者的深度,并且保留距离观察者较近的物体在该像素点的渲染结果,这样就形成了近的模型遮挡远的模型的结果。
当我们想要渲染一副二维的图像时,深度就不是单纯地由Y坐标决定了,二维图像上的每个像素点都有不同的深度,所以我们需要一个二维数组,这时我们把它称作ZBuffer。 深度缓冲(Z-Buffer)是一个二维数组,但是数组的元素类型却可以不同,不同的元素类型代表着不同的精度。这和颜色的精度很像,比如GIF图像最多用8bit保存一个颜色,也即GIF最多支持256种色彩。以此类推,如果深度缓冲的也用8bit来保存一个像素的深度,那就是说该深度缓存只有256个深度级别。
Z-Fighting
在场景中出现纹理的闪烁不停地切换纹理等等,就是Z-Fighting导致的问题,可能导致Z-fighting有很多种原因如果是模型重面要重新处理模型,要不找做模型的人重新做,或者改正模型;比如说同一个模型 、同一位置(raster过程中同一个像素) 、颜色不同 叠加到一起(深度缓存无法判断谁前谁后)就会发生这种问题 !但是有的模型叠加到一起就不会有这种问题(以为是好的)~其实就算看到有些面似乎没有出问题,正确的方法也依然是要把重叠面调开,因为Depth Buffer中是无法比较这样的数据的即使使用了有做深度缓存用对数深度缓存 ,对数缓存一样,因为他这个是完全重叠的面;
三维渲染的本质是三维投影光栅化到2D缓存上,深度测试的职责是判断src和dst像素之间的遮挡关系。如果两个前后绘制的对象在某个像素上的深度值很相近,就会产生这种随机的比较结果,这是float类型的精度决定的和osg或者别的什么引擎没有关系,只要还存在光栅化这件事,就必须面对这个问题Z-Fighting。
解决 Z-Fighting
要解决Z-Fighting问题,有两个思路:
让各模型渲染结果不要在同一个像素出现相同深度值
人为设置渲染顺序,这样即使出现相同深度值,也能正确渲染
这里说一下第二种方法为什么也能解决Z-Fighting,比如有两个模型A和B,A的渲染顺序是0,B的渲染顺序是1,既是先渲染A,再渲染B,所以,如果A和B在某个地方出现了相同的深度值,那么后渲染的B会覆盖掉先渲染的A。下面是按照这两个思路提出的一些解决办法
别让模型靠得那么近
手动设置一定的偏移即可让这个问题解决,叫建模人员 或者用3Dmax调整一下距离,分开一点就可以了;
设置合适的near和far值
在创建相机的时候,会有near和far两个参数,用来设置相机的近平面和远平面。**这个两个参数其实和深度缓冲(Z-Buffer)也密切相关,深度缓冲其实是非线性的,靠近相机的地方精度更高。**什么意思呢?假如你的深度缓冲只有10个深度级别,你的相机的near=1,far=100,那么你的深度缓冲可能是这样的:
深度级别
深度范围
0
0~1.0
1
1.0~1.1
3
1.1~1.234
4
1.234~1.325
5
1.325~1.55667
6
1.55667~1.9634
7
1.9634~5.434
8
5.434~23.34834
9
23.34834~99.999`在这里插入代码片`
(数据是杜撰的)
这样的非线性深度缓存可能会造成在离相机较远的地方深度等级的划分过于粗糙,比如上面的深度等级9,离相机的距离从23.34834到99.999的面都属于同一个深度级别,从上面可以,两个面对应到同一个深度级别就可能会出现z-fighting,所以,这个深度缓存出现z-fighting的概率还是挺大的。
一般来说,选择一个稍微大一点的near值效果会明显,比如把near从0.1设为1。
There.js Z-Fighting : Large spheres appear broken at intersection
设置多边形偏移(polygon offset)
应对Z-Fighting这种情况的办法是使用glPolygonOffset给当前绘制对象设置一个深度偏移,函数原型是:
void APIENTRY glPolygonOffset (GLfloat factor, GLfloat units);
设置后深度偏移量的计算公式:
offset = (m * factor) + (r * units)
m : dz/dx (dz/ dy)的比率, 与znear(zfar)平行的面时, m = 0 r 值用于保证每个屏幕不同像素之间产生的最小差值一般可以填 ( 1, 0) 或者( 1, 1), 如 glPolygonOffset(1.0f, 1.0f);offset 为正值时,代表目标绘制时比正常绘制时离视点要远一点为负值时, 代表目标绘制时比正常绘制时离视点近一点
设置正数表示当前的深度更深一些,显示的时候会被前景覆盖,设为负数表示深度较浅,会被绘制到屏幕上去。使用glPolygonOffset之前需要用)开启深度偏移功能。
Polygon Offset来处理它有三种值可以设置,每种针对一个渲染方式: line, point , fill 模式, 如OPenGL下( GL_POINT, GL_LINE and GL_FILL),
对就在的开启函数如下:
glEnable(GL_POLYGON_OFFSET_LINE);
glEnable(GL_POLYGON_OFFSET_POINT);
glEnable(GL_POLYGON_OFFSET_FILL);
使用 logarithmicDepthBuffer 缓冲
缓冲的级别越多,冲突的概率相应的也就越低,所以,我们可以使用一个精度更高的z缓冲,来代替原有的Z缓冲。
比如cesium:
viewer.scene.logarithmicDepthBuffer = true;
比如there.js:
var renderer = new THREE.WebGLRenderer({ logarithmicDepthBuffer: true });
使用Reverse-z:
使用Reverse-z大大降低Z-Fighting出现的概率。
Z-Fighting问题解决(二) - Reverse-z