early-z、z-culling、hi-z、z-perpass是什麼

鏈接:https://juejin.im/post/5e9d6708f265da47ce6cc812

 

之前一直被這幾個和深度緩存(z-buffer)相關的概念搞得神魂顛倒。今天在翻閱《Real-Time Rendering》時碰巧碰巧看到了這部分的講解。硬着頭皮看了看,姑且算是講幾個概念分清楚了。以我的記性估計下週就全忘了,所以打算順手記下來。

這四種技術本質上都是解決傳統渲染管線中的同一個問題——過度繪製(OverDraw) 。一個經典的渲染管線通常會依次經歷頂點階段光柵化片元階段逐像素處理。其中片元階段會進行復雜的光照計算,是整個管線的性能瓶頸。而在逐像素階段會對計算出來的片元值進行各種測試以判斷這個片元會不會最終顯示到屏幕上。這就帶來了一個矛盾:明明我在片元階段花費了最大的力氣計算出結果,但馬上的逐像素階段就可能將這個結果直接捨棄。而事實上逐像素階段的深度測試(z-test)會捨棄大量片元,對於較爲複雜的場景甚至會丟棄80%之多。如果我們能在片元階段之前進行深度測試提前丟棄掉那些不需要繪製到屏幕上的片元,那麼就可以減少大量片元計算提升效率。early-z、z-cull、hi-z、z-perpass都是爲了解決這個問題而產生的不同技術。

1.early-z

early-z的解決方式非常簡單,就是直接修改傳統渲染管線,在光柵化和片元階段中間,加入一個early-z階段。這個階段進行的操作和原本逐像素處理階段的z-test(爲了與early-z區別,這個階段也會被成爲late-z)操作完全一樣,現代的gpu已經都開始包含這樣的硬件設計。但是early-z有以下兩個主要的缺點:

* 一旦進行了手動寫入深度值、開啓alpha test或者丟棄像素等操作,那麼gpu就會關閉early-z直到下次clear z-buffer後纔會重新開啓(不過現在的gpu也在逐漸優化,使其更智能開關early-z)。之所以gpu會選擇關閉early-z是因爲上述那些操作可能會在片元階段與late-z階段之間修改深度緩存中的深度值,導致提前的early-z的結果並不正確。我們也可以在fragment shader中使用layout(early_fragment_tests)來強制打開early-z。

* early-z的優化效果並不穩定,最理想條件下所有繪製順序都是由近及遠,那麼early-z可以完全避免過度繪製。但是相反的狀態下,則會起不到任何效果。所以有些時候爲了完全發揮early-z的功效,我們會在每幀繪製時對場景的物體按照到攝像機的距離由遠及近進行排序。這個操作會在cpu端進行,當場景複雜到一定程度,頻繁的排序將會佔用cpu的大量計算資源。

 

2.z-culling

z-culling和early-z一樣都是gpu硬件層面的優化,所以之前我一直混淆兩者是同一種東西。兩者最明顯的區別是early-z是以pixel quad爲單位(既以4個像素爲一組,因爲深度緩存內的數據是按Z字形排列的)逐個像素進行比較,而z-culling是以tile(比如16*16像素)爲單位進行整體比較。這裏又涉及到tile的概念,雖然我看到的資料中並沒有提到,但是我認爲這裏的tile和tile based rendering(TBR)中的tile是同一概念。也就是說這種技術應該只應用於使用TBR架構的移動端gpu中。其主要方式取得當前tile所對應的的深度緩衝區中的Zmax和Zmin,如果該tile當前深度的最小值<Zmax,則說明整個tile都不可見將整個tile全部丟棄。如果該tile當前深度的最大值>Znim,則說明整個tile都處於最前面,保留整個tile,並因此可以省去該tile對應片元在late-z階段對深度緩衝區的讀取操作,直接寫入就可以。對於其它情況,則交給後續的深度處理進行更細緻的判斷。由於z-culling通常用於TBR架構gpu,所以它也和TPR架構一樣保持了對gpu帶寬的敏感性。因此不同於early-z,z-culling並不會對深度緩存進行寫入,也不會對深度緩存進行直接讀取。它所需要的比對數據(Zmax和Zmin)都會儲存在on-chip緩存中的某個固定區域,特點即是容量小但速度快。由於z-culling對深度緩存是隻讀的,因此不會因爲手動寫入深度值、開啓alpha test或者丟棄像素等操作對其有影響,這剛好解決了early-z的第一個缺點。總結來說,z-culling利用TBR架構進行了非常粗粒度的提前深度測試,但不會帶來額外的對於深度緩存進行讀寫消耗,因此也比z-early具有剛多的適用範圍。

這裏有一個疑問,爲什麼early-z不像z-culling一樣,對深度緩存只讀,來避免收到手動寫入深度值、開啓alpha test或者丟棄像素等操作的影響?其中一個解釋是,在z-culling階段後,那些沒有被優化的片元在late-z階段會讀取深度緩存進行細粒度的測試,完成後再更新寫入新的深度緩存。同時也會更新z-culling會訪問的on-chip緩存。由於z-culling訪問的是on-chip的所以不會帶來額外開銷,所以整體上只有對深度緩存進行一次讀一次寫。而對於early-z來說,如果在early-z階段只讀取深度緩存而不寫入的話,那麼在late-z階段就需要重新讀取然後寫入,以更新深度緩存。這就相當於兩次讀一次寫,帶來了額外的開銷。不過也看到有人說late-z階段對深度緩存的讀寫是無論如何都會進行的,所以此處存疑。

還需要說明的是,z-culling和early-z都可以不依賴於對方單獨存在,當然兩者也可以共存。當兩者共存的時候,會先進行z-culling做粗粒度的篩選,再進行early-z做細粒度的排除。在有些資料中也會把z-culling成爲HiZ(沒錯,就是最後要講的hi-z),這要是不弄混就怪了。

 

3.z-perpass

和上面兩種技術不同,z-perpass是一種軟件技術。它主要是配合early-z使用,來減少開始提到的early的第二個缺點——效果不穩定。其做法是將場景做兩個pass的繪製。第一個pass僅寫入深度,不做任何複雜的片元計算,不輸出任何顏色。第二個pass關閉深度寫入,並將深度比較函數設爲“相等”。我在開篇有提到過度繪製的主要矛盾——經過大量運算的片元,很大概率會在之後被丟棄掉。那麼對於第一個pass由於只寫入深度,不在片元做任何計算,所以即便之後會被丟棄,也並不可惜。也就是說無論場景中的物體以怎樣的順序繪製,我們都可以以很小的代價提前繪製好當前場景的深度緩存。那麼在第二個pass時,early-z就可以用這個深度緩存中的值和當前深度值進行比較,只繪製深度相等的片元,任何其他的片元都可以直接丟棄,因此第二個pass要把深度比較函數設爲“相等”。同時當前的深度緩存已經是完全正確的結果了,因此第二個pass也不需要對深度緩存做任何更新,便可以關閉深度寫入。

z-perpass必須配合early-z才能發揮效果,如果沒有early-z的話,第二個pass的深度測試依舊在片元后,因此所有片元都會在片元階段進行復雜計算。z-perpass的思想和延遲渲染管線(defered render pipeline,下面也會提到)有些相似,差別在於:第一,z-perpass的第一個pass只計算深度,並且結果直接存儲在深度緩存。而延遲渲染會同時計算更多其他的屏幕空間數據,並將這些數據存儲在額外的framebuffer中,需要更大的緩存(也就是GBuffer)。第二,z-perpass的第二個pass依舊需要對全場景的各個物體進行繪製(至少頂點階段是如此),而延遲渲染的第二個pass類似於後處理本質上只繪製了一個屏幕大小的矩形。

4.hi-z

hi-z全名Hierarchical Z,和z-perpass一樣也是一種軟件技術,據說這項技術最早是在《刺客信條:大革命》中使用的。其核心原理是利用上一幀的深度圖和攝像機矩陣,來對當前幀的場景做剔除,對於剔除後的物體進行繪製新的深度圖和GBuffer,然後再用新的深度圖和當前攝像機矩陣再對當前幀的場景做剔除,對剔除後的物體進行繪製更新剛剛的深度圖和GBuffer。之所這種看起來十分複雜的方法能提高效率,是因爲每一幀的繪製都已上一幀的繪製結果爲基礎。我們假設相鄰兩針差距不會特別大,那麼以上一幀的深度圖作爲結果來對當前幀可見的物體進行篩選,可以得到絕大部分。而對於少量兩針不一樣的物體,進行第二次深度繪製,由於第二次繪製的少量不一樣的物體所帶來的的計算量很小,因此可以帶來性能上的提升。這種基於前一幀的迭代式的對場景物體進行剔除,便可以在一定程度上減少過度繪製。不過由於我也沒有實現過這種算法,所以對這種算法實際帶來的效果存疑。

值得注意的是,這裏提到了Gbuffer,那麼說明hi-z技術是基於延遲渲染管線。而延遲渲染管線本身也是在減少各種由其他原因(包括但不限於深度測試這個原因)導致的過渡繪製。其目的就是希望無論擁有多少模型多少光源,整個場景渲染的複雜度都O(1)。延遲渲染管線本身也是個龐大的話題,可能以後會結合Unity剛剛正式更新的Scriptable Render Pipeline也寫點東西。

 

終於把這四種技術講完了,除了這四種在名字容易讓人混淆的技術外,其實還有一些東西沒有提到。對於TBR(或者TBDR)架構的gpu,因爲其提供了提前優化的潛能所以各家硬件廠商也會有自己獨特的針對於過度繪製的優化,比如PowerVR的HSR(Hidden Surface Removal)和Arm的APK(Forward Pixel Kill)。這些技術可以配合early-z等技術來更高效的避免過度繪製。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章