花了一些時間把EpicChina在2022台北遊戲開發者論壇的演講看完並做了一些筆記。
題目是關於UE5中的渲染技術。
只是看完影片後覺得資訊量太多,腦袋呈現完全爆炸狀態……然後影片結尾聽到講者說這只是很粗淺的介紹後我就崩潰了。
雖然以下筆記很多內容我都還有點消化不良而且可能也有理解錯誤的地方,但不管怎麼樣先分享出來給需要的人。
想更進一步知道細節的推薦大家去看演講,不能只有我崩潰。
https://www.twitch.tv/videos/1533064361 1:54:37秒 開始
1. 在nanite中大小三角形會分別用軟硬體做,大的用硬體做,小的為了避開overshading的問題,他自己用compute shader寫了rasterization。為什麼呢?在三角型在算pixel的顏色時,由於頂點不會剛好在pixel的中間,因此他必須相鄰的4個pixel拉進來,用一個Quad為單位來考慮。當你的三角型很小而且緊密時,某些pixel會一直反覆被拉進去做計算,這情況稱之為Overshading或者是Quad OverDraw。而Nanite為了解決這個問題,因此大的三角型雖然還是靠硬體做,但小的三角型則是自己用軟體做,在Compute Shader中寫rasterization。小的三角型有一些條件需要達成才會被歸類為小三角型。
2. 整套技術的重點在Cluster生成、Culling跟LOD的選擇,為了高效的達成Culling跟LOD的選擇,引擎從4.22開始就把整個render底層換成retained mode,就是說讓GPU去維護整個場景render object的一些狀態。 這邊是GDC2019演講。
3. LOD的生成機制,其實就是先做graph partition演算法做cluster分組,然後不斷的遞迴減半面數,最終生成一個DAG(Directed Acyclic Graph)。Graph partition的分群的給定條件是共享的邊盡可能的少,面積盡可能的均勻。這出來的結果是每下一級LOD都會是最小的變化,也就是說外觀的變化最小。而衡量外觀變化的標準就是使用QEM(Quadric error metric)。
4. QEM這個衡量標準可以保證我們減面後的error會越來越大,從DAG的Root到Leaf Node,記錄在節點中的error值可以幫助我們做LOD的選擇。
5. 每個Cluster的BoundingBox都會再各別對每級的LOD個別生成BVH(BoudingVolumeHierarchy)後,再掛到一個大的BVH root下面,因為這樣gpu的入口會比較一致。每4個Cluster會組成一個cluster group,最終這個nanite模型會是一個cluster的BVH結構。
6. Culling會利用分群後的資訊進行,經過以下步驟:
— a. Instance Culling:用上一個frame的HZB做跟BoundingBox來做剔除,得到一個粗略的可能還可見對象的instance。
— b. Hierarchical culling:利用Cluster group組成的BVH再去Culling掉大部份不必要的cluster
— c. Cluster Culling:最後存活下來的Cluster我們再進去做實際的Culling。
7. Raster binning:根據需不需要頂點動畫以及三角形的大小分成不同的binning餵進不同的pipeline,靜態的nanite mesh基本上進HW Raster passes(硬體)的fixed vertex pipeline ,需要頂點動畫進programmable vertex shader以及太小的三角型進SW Raster Passes(軟體)。
補充: 目前5.0版本Nanite不支援skeletal animation、morph target、material的mask & translucent以及vertex animation,因此這邊提到的programmable vertex shader看起來就是在5.1 Roadmap中提到的新架構,在5.2以後上面提到的這些限制可能都會開始支援。
8. RebuildHZB:利用上個步驟基於上個frame HZB的culling,跟這個frame的HZB,但還沒被處理過的對象,重新再跑一次整個Culling流程,生成這個frame的visibility buffer後,餵進material pass,生成最終的G-Buffer,用於跟傳統的管線結合在一起,作為最後光照render來用。
9. 整體來看流程分為以下三個部份:
–a. Culling:包括LOD的選擇
–b. raster binning:分組以決定怎麼做rasterization的管線
–c. Rasterization:生成visibility buffer,進而給後面的material pass生成G-Buffer用。
10. 這邊比較特別的地方是,不管軟體或硬體的rasterizatoin,最終都還是透過compute shader把結果寫入visibility buffer(利用atomic64保證操作不會有race condition),硬體rasterization只是用了他的rasterization的能力,最終並沒有用硬體的深寫入機制,因為這樣的話就可以同時對二個軟硬體的computer shader做平行處理。
11. Nanite的材質假設每個mesh大部份情況下只用到3個材質,雖然unreal最多支援到64個,但如果你mesh小於3個材質時他會把資訊壓縮進cluster上,超過的話會需要去另外一個global的表上找,效率會比較低。
12. 搭配nanite的技術之後,我們可以使用Virtual Shadow Map的技術,他可以幫我們解決因為shadow map精度不夠而產生的漏光跟shadow acne的現象。
13. shadow acne是指目前的深度指跟shadow map中的值很接近的時候產生的self-shadow artifact,在地形上會呈現條紋狀,要手動調整shadow bias,但這個值調大了之後,角色或物件的影子又會浮空。使用virtual shadow map就不需要手動調整了。
14. Virtual Shadow Map,粗暴的理解,其本質上就是一個精度非常高的Shadow Map。對於所謂的局部光源,使用了16x16K的virtual shadow map,每個都有8級的mipmap。對於點光源,因為他是cube map,所以他有6張這樣的virtual shadow map。對於方向光,他們發現就算是用17級的16k mipmap都不夠,因此他另外用一個叫clipmap的技術。
15. Clipmap跟mipmap的區別是,他不降解析度,而是覆蓋的區域翻倍。
16. virtual texture預設的TileSize是128X128,最遠的那一級會覆蓋將近80公里左右的範圍,我們可以調tile size來調整。
17. 為了提高效率,加載進來的virtual texture會用page的概念來做cache,只是一有光線變化,我們所有的page cache都會被invalid掉,所以在做time of day光線變化效果時,一般會建議不會每個frame去更新。
18. 關於page cache的另外一個策略是把動態跟靜態對象分PhysicalPagePool存,雖然pool size會翻倍,但可以避免因為樹跟草這些有頂點動畫的影子把我們的cache invalid掉。
廣告時間
我有建立unreal discord討論區,歡迎大家加入一起討論: https://discord.gg/FanK6yc
我在Marketplace上有上架不少Plugin,歡迎大家選購: https://www.unrealengine.com/marketplace/en-US/profile/horizon-studio
Leave a Reply