塞爾達風之杖渲染解析

遊資網發表於2019-12-24
塞爾達風之杖渲染解析




塞爾達風之杖渲染解析


塞爾達風之杖渲染解析


通過線框繪製模式可以看出“霧”是由幾個緩慢移動的自旋透明片構成(始終繫結在攝像機上),很尋常的做法。去掉紋理過濾後會更容易看出來。
塞爾達風之杖渲染解析




塞爾達風之杖渲染解析


塞爾達風之杖渲染解析


雲實際實際上是Billboard片,出現時有縮放和透明度變化做過渡。

但是這樣做只能顯示雲的側面貼圖,無法處理雲在頭頂的情況。所以遊戲裡視角通常是不能朝上看的。

但在第一人稱視角時必須允許朝上看,所以它做了以下處理:

  • 所有Billboard片的方向並不是固定朝上,而是朝向頭頂中心點。朝上看的時候,所有云都指向天空中心,這樣就不會出現視野旋轉而云不轉的情況。
  • 但云在接近天空中心的時候朝向不對的問題依然不能解決,而且通過中心的時候還會旋轉180度。所以它選擇讓雲接近中心時執行消隱邏輯,任何時候頭頂區域註定沒有云存在(即使是暴雨天氣)。

塞爾達風之杖渲染解析


塞爾達風之杖渲染解析




塞爾達風之杖渲染解析


塞爾達風之杖渲染解析



眾所周知,點光源的渲染成本遠高於平行光。

風之杖的所有點光源都能照亮場景,並體現出點光源的球形範圍特徵,更重要的是,它並沒有燈光數量上限(第二張圖兩個燈光區域疊加了)。

雖然點光源說起來也就是逐畫素計算和燈光的距離,算一次向量自點乘,但在場景光源較多的時候,剔除掉範圍外的光源也需要很多計算——而且實際上是做不到的,因為塞爾達每個Renderer都很大,只有不同Renderer才能選擇不同的光源組,然後又不可能用延遲渲染或者Tile-base按螢幕範圍拆分。

風之杖用的是一個很hack的做法。它的每個光源都是一個多邊形的球體。


塞爾達風之杖渲染解析


然後設成Cull Front(正面剔除),以及ZTest Creater(被遮擋時才顯示),就成了這樣:

塞爾達風之杖渲染解析


所以它的光照投影才不是個正圓,而是個微妙的Low Poly風格多邊形。這並不是故意而為之,單純是不想給這個光照球太多頂點。這樣做並不用專門剔除光照,也不會有任何額外繪製,但表現力就到此為止了,最多也就是用多個不同大小的光照球疊加來制照一些“光照邊緣”,因為在光照球的shader裡其實是不知道牆面和球的交點在哪的。

塞爾達風之杖渲染解析

3個不同大小的光照球疊加


但是如果有不透明物體的深度快取,就能在螢幕上求出其交點,換算回世界座標並和光照中心求距離,並算出正確的點光源光照來。而這其實就是延遲渲染中對光照部分的處理,所以現在看也不完全是個hack的技術了。

此外,人物的光照和投影為求效率也使用的是平行光。對於多光源的處理,它採取的是接近後“捕獲”的做法,在公共區域用的則是最後一個接觸的光源,在光源切換的時候是直接對兩個光源的位置做插值,以避免切換時的突變。(請仔細觀察人物投影方向和光照方向變化的瞬間)

塞爾達風之杖渲染解析


塞爾達風之杖渲染解析


塞爾達風之杖渲染解析




罐子和金幣等物體的影子採用了常見的透明圓片的設計(角度由下方碰撞箱的法線決定),所以可以看到這個明顯的顯示錯誤:

塞爾達風之杖渲染解析


但是從上面看的時候,超出平臺的陰影卻能被隱藏。

塞爾達風之杖渲染解析


由下圖隱藏了一部分的陰影可以判定,這個陰影實際上是通過Offest將自身的深度檢測向後位移了一段距離,然後設定為ZTest Greater(被遮擋)才允許顯示。這是個相當“實用”的貼花技巧。

塞爾達風之杖渲染解析


至於動態陰影部分,

塞爾達風之杖渲染解析


塞爾達風之杖渲染解析


塞爾達風之杖渲染解析


塞爾達風之杖渲染解析


個人認為是每個人物單獨生成ShadowMap,然後從腳底附近的簡化Mesh碰撞箱篩選三角形動態建立Receiver(依據是與人的距離,法線與光照方向的夾角,所以從崖上往下的陰影無法在懸崖上顯示出來)。

因為是每個人物單獨生成ShadowMap,每張貼圖都可以比較小,記憶體佔用較小。但就算只渲染一個人,這張貼圖依然精度極低,鋸齒不明顯是對邊緣半透部分step的結果。

塞爾達風之杖渲染解析


最終鋸齒部分近似為斜線


這個遊戲還有個特殊的設計:一旦人物懸空,或者在梯子和掛邊的時候,陰影就會固定到人物正下方。個人覺得這是為了表示人物的落點位置,單純是為了遊戲性考慮。但這樣的設計確實也方便了Receiver的篩選,遠處的物體將永遠不可能被篩選到,而且也避免了ShadowMap精度低,拉長後過於明顯的紋理走樣問題。

塞爾達風之杖渲染解析






塞爾達風之杖渲染解析


相比大片紋理產生的overdraw,大量粒子生成的多邊形更加合算。雨實際上是由大量狹窄雨點三角面渲染而成的。

但由於雨點的下落速度一致,並不需要每個雨點單獨作為粒子,將十幾個雨點繫結成一個整體,隨機水平位置生成向下位移即可。

塞爾達風之杖渲染解析


除了雨點本身外,還需要生成落在地面濺起的水花。用物理檢測實現是不可能的。通過反覆觀察水花生成的“錯誤”,風之杖的水花應該只是簡單地在低於視角的範圍隨機生成(高於視角看到的是Mesh背面,即使生成了也看不到),但是用的是ZTest Greater,也就是隻有當水花被建築遮擋的時候才會顯示。這樣水花就不會出現在空中。

但是當地面高低差較大的時候,由於水花範圍沒有那麼廣,無法落在建築下方,就無法顯示出來。

此外,這也會錯誤地導致牆面上出現水花,但因為牆壁的水花和視線幾乎平行,是一個極扁的圓,錯誤生成了也不是很明顯。

氣流扭曲

塞爾達風之杖渲染解析


風之杖中廣泛使用了這個效果。做法很常規:在繪製完其他全部物體後,Grab Pass,最後繪製扭曲部分(不用Grab Pass直接取上一幀影象會取樣扭曲效果本身,導致越偏越遠)

塞爾達風之杖渲染解析


塞爾達風之杖渲染解析


扭曲效果的本體是一個普通的半透圓片貼圖粒子,除了透明部分外,色彩部分直接用的Grab Pass的貼圖再加一個噪波貼圖位移產生扭曲。並沒有什麼可說的。

塞爾達風之杖渲染解析


塞爾達風之杖渲染解析


但是在一些特殊情況下,“全屏”+“高速”,它則用了一個更近似的方案

塞爾達風之杖渲染解析


GrabPass還是依舊,但這個效果並沒有用噪波紋理,甚至沒有用透明紋理,而是瘋狂甩出了大量高速移動的帶Grab貼圖的不透明粒子。

塞爾達風之杖渲染解析

偏移攝像機後



取一幀檢視

塞爾達風之杖渲染解析


它只是稍微偏移了下采樣的座標,然後靠不透明片隨機疊加,單幀的效果其實有一定破綻,但在連續畫面中是看不出來的。而因為不透明物體重疊會正常pixel kill,效能不差。

非要這樣做而不是用正常的後處理,應該主要還是為了體現出氣流運動的方向,用噪波的效果未必比這個好。運算量也確實要少一些,就像上圖的情況,其實有一部分螢幕根本沒繪製,而後處理至少要繪製一屏。省掉的噪波紋理那方面就更不用說了。

岩漿

塞爾達風之杖渲染解析


這個岩漿效果的特點是:有實際高度變化,且足夠隨機(並且符合岩漿的特徵)

有經驗的人很快能看出,岩漿的顏色=水面高度(取世界座標Z作為依據lerp),不需要紋理。剩下的問題就是如何生成這樣的一個岩漿高度場。

正弦波疊出來的都比較接近波浪,而岩漿池的波形特點則是“沸騰”,簡而言之是個混沌系統,這是簡單的週期波模擬不出來的。雖然FFT理論上可能能模擬出來(但這就扯遠了)

實際上是這樣的:

塞爾達風之杖渲染解析


看不明白就再放一個比較“稀疏”的例子

塞爾達風之杖渲染解析


塞爾達風之杖渲染解析


說白了,還是隨機“粒子”。

每個粒子是個兩層的8邊形,中間凸起。

塞爾達風之杖渲染解析


隨機位置生成粒子後,從水平面下凸起來,然後再沉回去,作一個週期,然後移動到下一個隨機地點再次出現。

粒子依然是不透明物體以減少OverDraw,因為顏色是由高度決定的並不需要半透過度,產生和消失都在“水面”以下,也不需要半透漸隱。

疊加得越多,效果越自然。

頂點數看上去很誇張,但也只是看上去而已(線段長,交叉多)。實際上一個粒子也就16個頂點,而一個湖有50個粒子效果就已經不錯了,隨便一個水面需要的頂點數其實很輕鬆就能超過它。

效率問題反而在OverDraw上,雖然是不透明物體,依然還是有概率重複計算的。按Z排序後能緩解,但邊界處依然存在。

不過基本沒啥問題的,畢竟風之杖只是個NGC遊戲,這個能跑還有啥不能跑。

很容易聯想到,是否普通的水體,或者海是否也能用類似的方法實現……

理論上是可以的,只是需要處理法線連續,所以必須先把高度場渲染到紋理處理,然後再對映到網格上。

神海4用到的Wave Particles聽說就是類似的技術。

水體

水的部分內容較多,分成多個小節描述

(一)邊緣

水是透明的,人對水的感知更多依附於水與其他物體的交介面。

塞爾達風之杖渲染解析


這個遊戲中大部分水都是一張純藍色的貼圖,然後在邊緣處單獨拉一個Renderer,使用特殊材質,拉好UV。

塞爾達風之杖渲染解析


使用兩張貼圖,4次取樣。同種貼圖UV展開方向是相反的,相加後saturate,這樣出來的紋理看上去並沒有那麼重複。

塞爾達風之杖渲染解析


白沫

塞爾達風之杖渲染解析

深色倒影


(二)海浪拍岸

塞爾達風之杖渲染解析


這裡取了個巧,任何位置的拍岸節奏都是一樣,只有UV x軸的步進速度不同。

海岸分3層,由下至上是:

1.波浪退去時潮溼的部分:

塞爾達風之杖渲染解析


UV並不是位移,而是直接貼邊對y軸縮放,y軸畫素拉伸產生的模糊模擬了溼邊的擴散。

2.有實體的海浪,在圖示範圍正弦擺動,一個完整週期要擺動兩次。

塞爾達風之杖渲染解析


塞爾達風之杖渲染解析


這個波還有個不易察覺的細節:

塞爾達風之杖渲染解析


接近岸邊倒極限的時候會顯示一個水面紋理,二次取樣,相反方向運動



塞爾達風之杖渲染解析


使用的紋理


除了提供細節外,還提供了一點軟邊緣的效果:接近岸邊的部分變亮了,更像沙灘的顏色。

3.從深處來的浪花:看著像海浪,其實只有白沫,看著同時存在兩個波,其實是通過repeat方式複製出來的。

塞爾達風之杖渲染解析


這個浪花一直保持勻速推進。

整個設計巧妙的地方在於,兩個波有一段時間是重合的。而第二種波的頻率比第三種大,它推到最內側之後,還會時間回來,再去接下一個波。重合期間由於一個是勻速,一個是正弦波,所以會錯開一點,使得白沫的變化更多。

正因為這個波只有白沫沒有藍色的海水部分,才能和下層的白沫重合在一起。

不過回退的時候處理不好就會出現這個狀況:

塞爾達風之杖渲染解析


波2回退的時候,波3還沒有完全消失

波3的覆蓋範圍需要比波2稍短一些。波3的白沫需要在波2回退之前滾動出網格外。

(三)海平面交界線

塞爾達風之杖渲染解析


這是不太容易注意到但是非常重要的細節。海水靠近地平面的地方應該變亮,並逐漸過渡到接近天空的顏色,也就是一種類似菲涅爾的現象。

我們肯定不願意在frag上正常算一次viewDir點乘法線。但這整個海只是一個Quad,也沒法交給頂點去算,那怎麼才能讓遠處的顏色發生變化呢?

用遠景霧確實可以,但這還是要算。

塞爾達風之杖渲染解析


調整鏡頭後,我們可以俯瞰整個海的Quad的形狀。可以看到這個Quad並不是純色,並且其影象在任何方向觀察都一樣,所以它很可能是貼了個這樣的貼圖:

塞爾達風之杖渲染解析


放大到足夠大,以接近水平的角度觀察,視覺上就產生了這樣的效果。

塞爾達風之杖渲染解析


塞爾達風之杖渲染解析


(四)波浪

在大陸附近,海面是由一個Quad組成的,這多半是為了效能。為了表現海浪,風之杖就搞了個Billbroad粒子糊弄事。

塞爾達風之杖渲染解析


塞爾達風之杖渲染解析


一眼就能看出來簡單實現方法,卻不得不說效果還成。

但是一旦進入深海,大陸的資源被釋放掉後,就會換成一個正規的海浪模擬。

塞爾達風之杖渲染解析


塞爾達風之杖渲染解析


沒有用尖波(Gerstner),海浪也不大,應該就是兩個不同頻率的正弦波的疊加,在頂點上直接計算。

海浪模擬為了節約計算成本,按說會做LOD,不過移動攝像機後發現LOD層級是一樣的,只是到最遠處變回了最初的Quad海面。



為了方便視域裁剪按說要對海面分塊,規則是近處分塊密,遠處分塊稀疏,這樣才能在滿足裁剪的情況下減少DrawCall。

塞爾達風之杖渲染解析


不過它LOD都沒做,估計就是最大粒度按扇形隨便分了下下吧。

另外這遊戲整個海面是跟著主角一起移動的(意外地破綻不大),不需要動態建立銷燬,其實很容易做分塊。

進入深海後海面也有了紋理

塞爾達風之杖渲染解析


兩次取樣,第二次UV翻倍,而且染成黑色紋理。同時還要做一次隨機擾波增加波紋細節。

塞爾達風之杖渲染解析


越遠紋理則越淡。

(五)船隻尾跡

其實就是個隨時間變寬的MeshTail,但是帶了一個偏移每一段UV的正弦擺動效果(兩個不同頻率的正弦波疊加)

塞爾達風之杖渲染解析


塞爾達風之杖渲染解析


貼圖:

塞爾達風之杖渲染解析



作者:flashyiyi
專欄地址:https://zhuanlan.zhihu.com/p/34960962

相關文章