threejs- z-fighting 问题
Z-Buffer
在threejs中,使用深度緩沖(Z-Buffer)來(lái)完成場(chǎng)景可見性計(jì)算,即確定場(chǎng)景哪部分可見,哪部分不可見。深度緩沖(Z-Buffer)是一個(gè)二維數(shù)組,其中的每一個(gè)元素對(duì)應(yīng)屏幕上的一個(gè)像素,如果場(chǎng)景中的兩個(gè)模型在同一個(gè)像素生成渲染結(jié)果,那么圖形處理卡就會(huì)比較二者的深度,并且保留距離觀察者較近的物體在該像素點(diǎn)的渲染結(jié)果,這樣就形成了近的模型遮擋遠(yuǎn)的模型的結(jié)果。
上面說(shuō)到,深度緩沖(Z-Buffer)是一個(gè)二維數(shù)組,但是數(shù)組的元素類型卻可以不同,不同的元素類型代表著不同的精度。這和顏色的精度很像,比如GIF圖像最多用8bit保存一個(gè)顏色,也即GIF最多支持256種色彩。以此類推,如果深度緩沖的也用8bit來(lái)保存一個(gè)像素的深度,那就是說(shuō)該深度緩存只有256個(gè)深度級(jí)別。在threejs中只實(shí)現(xiàn)了一種深度緩沖,但是在例子中,又實(shí)現(xiàn)了一個(gè)精度更高的深度緩沖——logarithmicdepthbuffer,可以看示例webgl_camera_logarithmicdepthbuffer
Z-Fighting
當(dāng)場(chǎng)景中的兩個(gè)模型在同一個(gè)像素生成的渲染結(jié)果對(duì)應(yīng)到一個(gè)相同的深度值時(shí),渲染器就不知道該使用哪個(gè)模型的渲染結(jié)果了,或者說(shuō),不知道哪個(gè)面在前,哪個(gè)面在后,于是便開始“胡作非為”,這次讓這個(gè)面在前面,下次讓那個(gè)面在前面,于是模型的重疊部位便不停的閃爍起來(lái)。這便是Z-Fighting問題。
(圖片來(lái)自:Three.js/WebGL: Large spheres appear broken at intersection)
解決 Z-Fighting
要解決Z-Fighting問題,有兩個(gè)思路:
- 讓各模型渲染結(jié)果不要在同一個(gè)像素出現(xiàn)相同深度值
- 人為設(shè)置渲染順序,這樣即使出現(xiàn)相同深度值,也能正確渲染
這里說(shuō)一下第二種方法為什么也能解決Z-Fighting,比如有兩個(gè)模型A和B,A的渲染順序是0,B的渲染順序是1,既是先渲染A,再渲染B,所以,如果A和B在某個(gè)地方出現(xiàn)了相同的深度值,那么后渲染的B會(huì)覆蓋掉先渲染的A。下面是按照這兩個(gè)思路提出的一些解決辦法。
別讓模型靠得那么近
手動(dòng)設(shè)置一定的偏移即可讓這個(gè)問題解決,比如下面兩個(gè)例子:
刻度的z值為0,和尺子處于同一平面,會(huì)出現(xiàn)z-fighting問題,可以看到刻度文字不停閃爍
有z-fighting的例子
刻度得z值設(shè)置3,和尺子分處不同的平面,無(wú)z-fighting問題
無(wú)z-fighting的例子
設(shè)置合適的near和far值
在創(chuàng)建相機(jī)的時(shí)候,會(huì)有near和far兩個(gè)參數(shù),用來(lái)設(shè)置相機(jī)的近平面和遠(yuǎn)平面。這個(gè)兩個(gè)參數(shù)其實(shí)和深度緩沖(Z-Buffer)也密切相關(guān),深度緩沖其實(shí)是非線性的,靠近相機(jī)的地方精度更高。什么意思呢?假如你的深度緩沖只有10個(gè)深度級(jí)別,你的相機(jī)的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 |
(數(shù)據(jù)是杜撰的)
這樣的非線性深度緩存可能會(huì)造成在離相機(jī)較遠(yuǎn)的地方深度等級(jí)的劃分過于粗糙,比如上面的深度等級(jí)9,離相機(jī)的距離從23.34834到99.999的面都屬于同一個(gè)深度級(jí)別,從上面可以,兩個(gè)面對(duì)應(yīng)到同一個(gè)深度級(jí)別就可能會(huì)出現(xiàn)z-fighting,所以,這個(gè)深度緩存出現(xiàn)z-fighting的概率還是挺大的。
一般來(lái)說(shuō),選擇一個(gè)稍微大一點(diǎn)的near值效果會(huì)明顯,比如把near從0.1設(shè)為1。
參考:【Z-Fighting】【Three.js/WebGL: Large spheres appear broken at intersection】
設(shè)置多邊形偏移(polygon offset)
threejs 的 material 定義了三個(gè)多邊形偏移相關(guān)的屬性:
- polygonOffset 是否開啟多邊形偏移
- polygonOffsetFactor 多邊形偏移因子
- polygonOffsetUnits 多邊形偏移單位
當(dāng)發(fā)生兩個(gè)面深度值相同時(shí),設(shè)置了polygonOffset的面便會(huì)向前或向后偏移一小段距離,這樣就能區(qū)分誰(shuí)前誰(shuí)后了。
當(dāng)polygonOffsetFactor和 polygonOffsetUnits的都是正值時(shí),向遠(yuǎn)離相機(jī)的方向偏移,當(dāng)兩者都是負(fù)值時(shí),向靠近相機(jī)的地方偏移。
設(shè)置polygonOffsetFactor和 polygonOffsetUnits是有所講究的:
- 當(dāng)面和近平面(near)、遠(yuǎn)平面(far)幾乎平行的時(shí)候,一個(gè)很小的偏移就足夠,你可以設(shè)置polygonOffsetFactor=0, polygonOffsetUnits=1.0
- 當(dāng)面和近平面(near)、遠(yuǎn)平面(far)有一個(gè)明顯的角度時(shí),這時(shí)候就需要一個(gè)較大的偏移和一個(gè)較小但非零的偏移因子。這是因?yàn)橐珠_兩個(gè)交叉的面要比分開兩個(gè)重合的面要更大的偏移。你可以設(shè)置如polygonOffsetFactor=0.75, polygonOffsetUnits=4.0
這部分內(nèi)容很多都來(lái)自Z fighting & polygon offset,原文講得更好點(diǎn)。
設(shè)置 render order
threejs的Object3D對(duì)象定義了一個(gè)renderOrder屬性,可以指定對(duì)象的渲染順序,按renderOrder從小到大排列,小的先渲染,大的后渲染。
設(shè)置完renderOrder之后,就算兩個(gè)面有同樣的深度,但是因?yàn)橛袖秩卷樞?#xff0c;后渲染的面會(huì)覆蓋掉先渲染的面。也因?yàn)檫@樣,設(shè)置正確的渲染順序很重要。
此外,這種方法更經(jīng)常用在處理元素透明問題上,詳見transparent-objects-in-threejs。
使用 logarithmicDepthBuffer 緩沖
緩沖的級(jí)別越多,沖突的概率相應(yīng)的也就越低,所以,我們可以使用一個(gè)精度更高的z緩沖,來(lái)代替原有的Z緩沖。對(duì)于這個(gè)方法,threejs官網(wǎng)已經(jīng)提供了一個(gè)例子webgl_camera_logarithmicdepthbuffer。不過,官網(wǎng)的例子為了演示效果,寫得比較復(fù)雜,實(shí)際上只需要將logarithmicDepthBuffer參數(shù)設(shè)為true即可:
var renderer = new THREE.WebGLRenderer({ logarithmicDepthBuffer: true });參考文檔
1、解釋如什么是z-fighting及何用polygon offset
Z fighting & polygon offset
2、講到了near far 設(shè)置的問題
(1)Three.js/WebGL: Large spheres appear broken at intersection
(2)Z-Fighting
3、解釋了 depth write的使用
How to use polygonOffset solving Z-fighting poblems
4、講到了解決透明問題的方法,比較全面
Transparent objects in Threejs
總結(jié)
以上是生活随笔為你收集整理的threejs- z-fighting 问题的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: NSGA2学习(1)——与遗传算法的比较
- 下一篇: [pytorch、学习] - 4.5 读