威狐小編:11 月 16 –20 日,中國 Unity 線上技術(shù)大會以直播形式召開,為廣大開發(fā)者帶來了一場有關(guān)前沿技術(shù)和優(yōu)秀案例的線上盛會。在11月17日晚舉行的游戲?qū)?,米哈游的技術(shù)總監(jiān)弋振中現(xiàn)身,為廣大同行帶來關(guān)于《原神》主機(jī)版渲染技術(shù)的精彩分享。
《原神》的主機(jī)版開發(fā)是國內(nèi)首次嘗試,通過一年多的開發(fā),研發(fā)團(tuán)隊積累了很多經(jīng)驗,尤其是關(guān)于寫實(shí)渲染技術(shù)與風(fēng)格化游戲的結(jié)合。隨著次世代主機(jī)的到來,游戲行業(yè)又將面臨了一大波技術(shù)升級,而米哈游的經(jīng)驗對于時間、資源和人才都很缺乏的國內(nèi)主機(jī)生態(tài)來說,十分寶貴。
同時,弋振中也提到:“Unity是一個靈活度很高的引擎,代碼風(fēng)格很簡潔,所以我們能夠更方便地定制化開發(fā)《原神》的渲染管線?!?/p>
弋振中:大家好!歡迎參加這次分享!今天我演講的主題是從手機(jī)走向主機(jī),《原神》主機(jī)版渲染技術(shù)分享。首先我簡單介紹一下《原神》的游戲,《原神》是開放世界的RPG游戲,有獨(dú)特的二次元畫風(fēng),是跨平臺的,而且長期運(yùn)營,長期更新。
接下來我稍微介紹一下自己,我叫弋振中,大概有十多年的主機(jī)游戲開發(fā)經(jīng)驗,畢業(yè)之后加入了Ubisoft Shanghai,2012年去了加州的Zindagi Games,為當(dāng)時還沒有上市的PS4做一款獨(dú)占的游戲。然后去了紐約的Avalanche Studios,參與制作了《正當(dāng)防衛(wèi)》的3和4?;貒暗淖詈笠徽臼窃谖餮艌D的微軟Xbox。2019年初回到上海,加入了米哈游,組建了研發(fā)團(tuán)隊,目前負(fù)責(zé)《原神》的主機(jī)平臺開發(fā)。這是我做過的一些項目。
今天的內(nèi)容安排大致是這個樣子,首先我會介紹一下《原神》主機(jī)平臺的基本情況,然后按照我們開發(fā)的時候改造渲染管線的思路,選擇部分的技術(shù)點(diǎn)進(jìn)行更深入的分享。希望從渲染的角度,讓大家對于我們?nèi)绾螌ⅰ对瘛穾У街鳈C(jī)平臺有大致的了解。最后是一點(diǎn)我個人的開發(fā)體會。
首先,Unity是我們游戲里面使用的引擎。Unity是一個靈活度很高的引擎,代碼風(fēng)格很簡潔,所以我們能夠更方便地定制化開發(fā)《原神》的渲染管線。Unity中國的技術(shù)支持很配合我們,在此對他們表示感謝!
PS4作為游戲的主機(jī),硬件架構(gòu)可以說是為游戲開發(fā)量身打造的,主機(jī)開發(fā)過程當(dāng)中大量的精力是花費(fèi)在如何更好地利用硬件特性上,也積累了不少我們認(rèn)為還可以的技術(shù)實(shí)現(xiàn)。不過因為索尼NDA的原因,今天的分享就不涉及相關(guān)的內(nèi)容,也不涉及底層的優(yōu)化。前面也提到今天的分享,主要是針對渲染管線的,所以也不涉及CPU和其他的模板。
下面是主機(jī)渲染管線的簡介,首先我們有非常強(qiáng)大的引擎團(tuán)隊,在Unity上面為《原神》進(jìn)行了深度的開發(fā)。主機(jī)平臺和手機(jī)平臺采用了不同的渲染管線,但是游戲的基調(diào)是一致的,都是基于PBR的風(fēng)格化渲染。《原神》的主機(jī)平臺開發(fā)起步時間稍晚于手機(jī)平臺,在平臺管線搭建好以后就進(jìn)入了同步開發(fā)的節(jié)奏。資源的制作、功能的開發(fā)多方面都需要兼顧到多個平臺的情況。
基于PBR,是為了讓整個大世界的光影效果保持統(tǒng)一,因為我們的光影都是實(shí)時計算的,有24小時的循環(huán),有動態(tài)的天氣系統(tǒng)。PBR能夠確保不會在不同的光照條件下出現(xiàn)脫離預(yù)期的渲染效果。作為風(fēng)格化的游戲,也需要根據(jù)美術(shù)的需求修改不同的材質(zhì)。
《原神》在PS4上的分辨率是這樣設(shè)置的,PS4 pro上面是原生的4K分辨率,在PS4的base,我們是把1440P作為我們的渲染分辨率,最后是輸出到1080P上面,這樣我們得到的最終后面會更加清晰,也會更加銳利。作為《原神》為主機(jī)開發(fā)的功能,大量使用了compute shader,compute shader有很多很好的特性,而且在支持Async compute管線的平臺上,我們還能夠進(jìn)一步隱藏開銷。
《原神》的風(fēng)格化渲染是非常獨(dú)特的,因此美術(shù)對于圖形功能的要求也和寫實(shí)類游戲不一樣,尤其是光影效果,大家可以看到臟、黑、死、焦、噪這些詞都頻繁地出現(xiàn)了美術(shù)和程序的溝通當(dāng)中。
下面我們提到的所有的技術(shù),都經(jīng)歷了很長的磨合期,有一些甚至還在磨合當(dāng)中。在經(jīng)歷了反復(fù)的打磨和修改,直到美術(shù)對最終效果滿意,程序?qū)ψ罱K實(shí)現(xiàn)方案的性能也滿意的情況下,我們才會大規(guī)模鋪開制作。
接下來再介紹具體的功能之前,我跟大家講一下在PS4平臺剛開始開發(fā)的時候,我們面臨的一個狀況。首先是我們有一個已經(jīng)針對手機(jī)做大量開發(fā)的Unity引擎,這就意味著簡單的切換平臺就想讓游戲能夠在PS4上跑起來,這是不可能的了。很多項目之前做的改動,在實(shí)現(xiàn)的時候也沒有考慮到主機(jī)平臺的特性。再加上各種計劃為PS4開發(fā)的圖形和游戲的功能,這都意味著大量的工作量。
另外還有TRC、索尼賬號等等一系列PS4獨(dú)有的問題需要解決,工作量和工作難度都非常大,然而我們能夠給主機(jī)開發(fā)的資源又很有限。一開始的主機(jī)團(tuán)隊就我一個人,光桿司令。為了全球同步上市,留給我們的開發(fā)時間大概只有一年半,一年半的時間要讓一個平臺從無到有,還要達(dá)到一定的品質(zhì),這期間還要準(zhǔn)備ChinaJoy、TGS等展會,能夠順利地完成這一切,真的是非常感謝《原神》的整個開發(fā)團(tuán)隊的努力,非常的不容易。
下面講一下我們在渲染管線做改動的時候一些思路,因為開發(fā)的時間很緊,所以在選擇技術(shù)改造點(diǎn)的時候,遵循下面幾個原則。
第一個是關(guān)于功能的選擇,我們首先排除掉開發(fā)周期長的,需要過多地前期研究工作的功能,因為我們沒有時間。
然后根據(jù)游戲的美術(shù)風(fēng)格,我們選擇一些對于畫質(zhì)的幫助更大的地方去做提升。另外因為時間不多,所以我們希望新加的功能能夠更多地發(fā)揮作用,所以最好是能夠在相互之間產(chǎn)生互動,這樣會顯得畫面更加系統(tǒng)化,得到的畫質(zhì)提升也會有1+1>2的效果。
下面就是我們做一些技術(shù)點(diǎn)的解析。首先我們從場景的光影方面選擇了幾個技術(shù)點(diǎn),主要是側(cè)重一些方法,會稍微涉及到一點(diǎn)點(diǎn)的優(yōu)化思路。
第一個是關(guān)于方向光的陰影,《原神》的大量游戲時間是在室外,室外方向光的陰影質(zhì)量非常重要。一方面近處的陰影細(xì)節(jié)需要更加細(xì)膩,才顯得畫面更干凈。另外一方面是陰影覆蓋范圍需要足夠大,因為游戲的可視距離非常遠(yuǎn)?!对瘛返年幱胺秶?00米。大家可以看一下這張貼圖,即使在遠(yuǎn)處墻壁上一小片的綠植產(chǎn)生的陰影,在放大之后都能夠看到樹葉的輪廓。這個地方放大之后,能夠看到樹葉輪廓,而且非常穩(wěn)定。整體上來說,我們對于《原神》的方向光陰影的質(zhì)量是比較滿意的。
我們陰影的技術(shù)還算是比較常規(guī),使用了Cascaded shadow map 加上基于Poisson disc的soft shadow,我們游戲沒有使用通常的4級cascades,而是用了8級,這屬于大力出奇跡的方式。大力出奇跡帶來了更好的陰影效果,當(dāng)然也帶來了更多的性能開銷。更多的drawcall會帶來CPU開銷,更多的cascades也會帶來GPU的開銷。我們把質(zhì)量提升上去之后,會想辦法來解決性能問題,那我們怎么去做的呢?
首先在CPU端,我們做了一個shadow cache,8級cascades前4級我們每幀都更新,后面4級是采用輪流更新的方式,確保每8幀所有的cascades都能至少更新一次。每一幀的話,我們只更新5級cascades。
主要的工作量其實(shí)在GPU端,用了8級cascades以后,我們的screen space shadow map的開銷長期是大于2毫秒的,在某些情況之下能夠超過2.5毫秒。GPU比4級cascades的情況下,爆漲了0.5到0.8毫秒。
我們的軟陰影采用的是泊松分布的采樣,而且每個像素會去做一個旋轉(zhuǎn),來消除重復(fù)的pattern,這一整套的操作都是很重量級的。但是我們仔細(xì)想一想,真的需要對每個像素都要做這么多操作嗎?所以我們的優(yōu)化思路是盡量只在必要的地方做軟陰影計算,我們會生成一張Mask貼圖,在貼圖里面標(biāo)出陰影、半影和非影片區(qū)。陰影區(qū)和非陰影區(qū)只需要直接返回0和1就好了,只有在半影區(qū)才會去計算軟陰影,通過這種方式,我們的GPU開銷大致減少了30%左右。甚至比采用4級cascades還要再快一些。
大家可以看一下這張圖,圖里面被紅色標(biāo)注的區(qū)域,就是我們的半影區(qū),這個是需要我們?nèi)プ鲕涥幱疤幚淼膮^(qū)域。其他的區(qū)域,就是在陰影區(qū)域或者是非陰影區(qū)域,我們直接返回0和1就好了。大家可以看出來,絕大部分的像素都可以去掉軟陰影計算這個繁瑣的步驟。
這張神奇的Mask貼圖是怎么生成的呢?這張Mask貼圖的分辨率是屏幕分辨率的1/4×1/4,也就是說一個Mask值對應(yīng)的是一個4×4的block。然后我們對4×4的block里面的每一個像素,來判斷它是不是在陰影中,最后匯總成一個陰影、半影和非陰影的三個狀態(tài),保存到Mask貼圖里。這樣我們能夠得到一個準(zhǔn)確的半影信息,但是它不夠快,所以我們做了進(jìn)一步的優(yōu)化,只選擇4×4這個block里面很少的幾個像素,來判斷是不是在陰影當(dāng)中。
這幾個像素的判斷結(jié)果,就代表了整個block的信息,顯然這樣會出現(xiàn)一些誤差,因為我們是拿幾個少數(shù)幾個像素的結(jié)果來代表整個block,所以我們把這樣計算得到了Mask貼圖做了模糊處理,讓半影的區(qū)域稍微擴(kuò)散出去。整個Mask貼圖的生成,包括模糊處理大概的開銷是在0.3毫秒左右。大家可以看一下對比圖。
優(yōu)化出來的效果非常好,肉眼可以說是看不出任何的區(qū)別。這樣優(yōu)化完之后,我們的GPU開銷時間大概穩(wěn)定在1.3到1.7毫秒。
把陰影搞好以后,下面我們來看看AO(Ambient Occlusion環(huán)境光遮蔽)。大家可以考慮一種情況,就是人物和場景的物體都已經(jīng)處在山或建筑物的陰影當(dāng)中,這個時候人物和物體的投影跟山和建筑的投影是融為一體的。這種情況之下,畫面缺乏對比,人和物體就會顯得浮空。為了解決這個問題,我們在游戲里面采用了多種的AO技術(shù),針對不同的情景生成不同的AO。
首先我們使用了HBAO,這是一個比較常規(guī)的實(shí)現(xiàn),能夠提供一些比較細(xì)節(jié)的AO效果。同時我們對靜態(tài)物體和動態(tài)物體分別采用了AO Volume和Capsule AO這兩種技術(shù)。大家可以看一下這是HBAO開關(guān)的對比圖,效果還是很明顯的。
下面這個是AO Volume的開關(guān)情況,大家可以重點(diǎn)看一下我們在紅圈里面的區(qū)域。椅子對地面產(chǎn)生了柔和的投影,在我們打開AO volume的情況下。
和HBAO相比,AO Volume能夠產(chǎn)生更大范圍的AO。它可以針對類似桌子或者椅子產(chǎn)生大面積AO。因為技術(shù)原理和性能的限制,HBAO是沒辦法產(chǎn)生這種效果的。AO Volume這個時候就體現(xiàn)很好的補(bǔ)充,要實(shí)現(xiàn)AO volume,首先我們是在離線的時候?qū)π枰a(chǎn)生AO volume的物體做一個遮擋信息的計算。這個計算是在物體的本地空間(Local space)去做的,生成的遮擋信息我們保存下來,在運(yùn)行的時候注入到volume texture中去使用。這個技術(shù)在2012年GDC關(guān)于《InFamous 2》的講座上有提到過,大家有興趣可以去看一下。
下面是關(guān)于Capsule AO的對比圖,大家可以重點(diǎn)看一下屏風(fēng)和地面,被我們紅色的圈給圈出來的區(qū)域。大家可以看到相鄰在屏風(fēng)和地面,能夠產(chǎn)生出能夠反映體形和人影的投影。而且如果在游戲中大家去觀察的話,隨之相鄰動作的改變,陰影的形狀也會隨之產(chǎn)生變化。
我們前面提到AO Volume主要是針對靜態(tài)物體的,因為遮擋信息是通過離線計算的方式保存下來。像角色這種帶骨骼動畫的,是不能采用這種方式的,因為形狀是不停地在發(fā)生變化。Capsule AO的做法就是用一些膠囊體包裹住人物的四肢和軀干,這些膠囊體和角色的骨骼動畫綁定進(jìn)行同步更新。然后這些膠囊體會被用來做遮擋計算,計算的時候我們把它分為無方向的環(huán)境遮擋計算,以及帶方向的遮擋信息計算。帶方向的遮擋信息計算采用的方向是主光源方向和法線進(jìn)行混合之后的得到的虛擬遮擋方向。通過這種方式,角色可以同時在周圍的墻和地面等投出多個陰影。
下面是一個關(guān)于AO的優(yōu)化技巧,《原神》的AO都是在1/2×1/2分辨率的RT(Render Texture)上去做計算。為了保證畫面的干凈,我們對AO還做了一個模糊處理(blur)。然后再Upsample一個全分辨率的貼圖上面去。所有的模糊處理和Upsample pass,我們都用了一個Bilateral filter,確保不會有無效的AO滲透到周圍的區(qū)域。
從前面的描述可以看出來,模糊處理和Upsample加起來一共有三個pass,這就意味著AO需要被讀取和寫入多次。而且你如果你了解Bilateral Upsample的話,大家可以知道相鄰的像素之間有很多的計算其實(shí)都是重復(fù)的,所以我們采用的優(yōu)化方式是將所有的計算都放到一個compute pass里面去做。然后通過LDS來保存blur的中間值,通過同時輸出四個像素的方式,來重用相鄰像素的計算。最終我們還可以通過async compute pipe把性能開銷進(jìn)一步降低。
關(guān)于我們的Local Light,我們在游戲里面采用了Clustered deferred lighting。我們支持是視野內(nèi)同時出現(xiàn)最多1024盞燈。大概的做法是我們將屏幕分成64×64像素的tile,然后每一個tile在深度的方向上面繼續(xù)分為16級的clusters。通過這兩個圖,可以大概看出我們能夠支持多少燈。
這張圖是一個游戲里面的截圖,是一個典型的通過Local Light的陰影提升畫面的情況,多個不同的Local Light,它們的照明范圍是交錯存在的,然后角色也投下多個不同的陰影朝不同的方向,畫面就顯得細(xì)節(jié)很豐富。
我們怎么做的呢?我們的Local Light 陰影系統(tǒng)支持接近100盞燈的實(shí)時陰影,理論上我們可以支持更多的,不過這已經(jīng)很夠用了。陰影的分辨率是根據(jù)優(yōu)先級和距離進(jìn)行動態(tài)調(diào)整,最終的陰影是通過烘焙的靜態(tài)場景陰影和實(shí)時生成的動態(tài)場景陰影結(jié)合得到的。游戲里面有很多的Local Light,如果每一個Local Light都去烘焙它的shadow texture的話,會占用的硬盤空間非常大。而且因為是深度貼圖,所以不能夠隨便使用BCn的壓縮,那樣瑕疵會非常風(fēng)險,所以需要一個好的算法來對于烘焙的shadow texture做一個壓縮。這個壓縮需要在精度損失足夠低的同時,還要保持壓縮率足夠高,同時我們的解壓開銷要非常小才行。
我們開發(fā)的這個系統(tǒng)是在離線制作的時候,對于shadow texture做一個壓縮,盡量地去保持精度,運(yùn)行的時候解壓的速度也非???,用compute shader去解壓的情況,1K×1K的shadow texture,我們解壓只需要0.05毫秒,可以說非常非常快。
那壓縮率和壓縮質(zhì)量呢?我們先介紹一下壓縮的算法思路。首先我們對于shadow texture按照一個2×2的block來進(jìn)行編碼,每4個深度值,我們用32bit來保存。如果想要降低精度損失,可以選擇高精度壓縮,這種情況之下每個block的大小變成64bit。編碼的方式有兩種,一種是基于深度平面方程的方式,或者是通過壓縮的浮點(diǎn)數(shù)方式。編碼完成之后,還要進(jìn)一步通過一個quad tree來合并編碼以后的數(shù)據(jù),進(jìn)一步提高壓縮率。quad tree是每個tile要保存一個,而每個tile又包含了16×16個block,大家可以看到下面的三個圖,從左到右分別是沒有壓縮的深度貼圖,中間是我們的平面方程編碼的視圖,最右邊是我們quad tree 0到4級的深度視圖,黑的地方是深度為0的區(qū)域。我們參考了Li Bo在2019年Siggraph上面的講座,大家有興趣可以去看一下。
壓縮比:在一個典型的室內(nèi)場景默認(rèn)精度壓縮比是在20%到30%左右。如果開啟高精度模式壓縮的話,大概默認(rèn)精度壓縮到40%到70%。陰影貼圖的壓縮是非常必要的,可以幫我們?nèi)萘肯陆狄粋€數(shù)量級。
大家可以看一個對比,這是默認(rèn)精度壓縮,能夠看到紅圈里面有一些瑕疵。這個實(shí)際上是我們找到的可以說是最差的一個情況。這是高精度壓縮,基本上看不出任何瑕疵來。這個是默認(rèn)精度壓縮。如果把高精度壓縮的圖和不壓縮的情況做對比的話,其實(shí)肉眼是看不出什么差別,所以沒有放這個圖。
剛才的圖是一個2K×2K的shadow texture,大小如果不壓縮的話是在8MB,默認(rèn)精度壓縮大小變成了274.4KB,壓縮率是29.85%。如果替換成高精度壓縮,就是肉眼看不出差距的壓縮,貼圖大小變成了583.5KB,這種情況的壓縮率還是有14%左右,所以還是相當(dāng)不錯的。
在搞好了Local Light以后,我們接下來為游戲添加了體積霧,體積霧是可以接受Local Light的照明影響,在燈的影響范圍內(nèi)形成一圈光暈,可以極大地提升畫面的體積感。大家可以看到圖里面近處的燈籠周圍會有一圈泛光。包括畫面遠(yuǎn)處的建筑物,因為籠罩在燈光下會使得周圍的體積霧也被照亮,而顯得有一絲的朦朧。
這個圖是一個更有意思的情況。如果我們給Local Light加一個projection texture,也就是我們通過這個貼圖來控制Local Light光照的形狀,就像右邊這樣,體積霧也會產(chǎn)生相應(yīng)的變化。
我們的體積霧的計算是基于物理的。我們也支持通過不同的參數(shù)讓體積霧在大世界里面的不同區(qū)域有不同的表現(xiàn)。體積霧支持Local Light,這個在前面已經(jīng)展示過了。為了讓體積霧更加穩(wěn)定,畫面更加細(xì)膩,我們給體積霧添加了Temporal filter,進(jìn)行了多幀的混合。整體的GPU開銷,也控制的不錯,在PS4 Pro下面大致在1毫秒甚至更少。
大致的實(shí)現(xiàn)是這樣的:首先是基于相機(jī)空間,我們把view frustum分成很多的voxel,這些voxel跟我們前面提到的clustered deferred lighting的clusters是對齊的,這樣方便我們在后面對Local Light做scattering計算的時候進(jìn)行一個加速。
前面提到的體積霧參數(shù)和Local Light的信息,都會被注入到這些voxel里面去,然后我們通過Ray marching的方式去計算體積霧。在這個時候,Local Light的信息就自然而然被考慮進(jìn)去了。
有了體積霧,我們不得不提到God Ray效果,首先大家可以先看看游戲里面God Ray的表現(xiàn)。對于方向光進(jìn)行遮擋就可以產(chǎn)生God Ray的效果,我們的做法是有一個單獨(dú)的pass來生成God Ray,然后是在1/2×1/2分辨率下面。God Ray也是通過Ray marching的方式去生成的,我們會去采樣shadow map,但是最多會采樣5級的cascades。
God Ray生成完之后,我們會提供美術(shù)一些可以調(diào)整的參數(shù),然后將God Ray的結(jié)果疊加到體積霧上面去。它在使用上面,并不是一個物理上正確的東西,但是它的效果是能夠讓美術(shù)滿意的。
了解體積霧的人可能會有一個問題,為什么要單獨(dú)使用一個pass呢?體積霧本身就可以產(chǎn)生God Ray。這個就是一個很好的技術(shù)和美術(shù)磨合的例子,我們有體積霧直接生成的God Ray,在游戲里面實(shí)際效果其實(shí)不能夠讓美術(shù)滿意,原因有兩點(diǎn)。
第一個是分辨率不夠,因為體積霧的分辨率是靠Voxel,而我們的Voxel是不會劃分的特別精細(xì)的。第二是因為體積霧生成的God Ray強(qiáng)度是完全依賴于體積霧的濃度。要想得到很明顯的God Ray,就需要霧的濃度提的非常高。霧的濃度一旦提高了,畫面就會顯得不通透,太臟,這就是回到前面提到的兩組詞里面,這是美術(shù)不能夠接受的。
所以我們是采用了單獨(dú)的pass去生成God Ray,這樣可以得到更銳利、更清晰的效果,美術(shù)調(diào)整也更靈活。美術(shù)想要什么,我們就給他做什么。
下面給大家看看對比圖,大家就能更好地體會到我說的是什么意思。這個God Ray就是通過體積霧生成的,包括整個畫面的表現(xiàn)。這張是我們游戲里面現(xiàn)在使用的方式,就是單獨(dú)的pass去生成,做一個對比。
大家可以看到第一張God Ray不是很明顯,而且畫面霧的濃度非常高。而第二張圖,God Ray會更清晰一些,而且整個畫面是更加干凈、更加通透,這就是美術(shù)想要的效果。
接下來是IBL(Image Based Lighting)系統(tǒng),大家先看一下演示視頻。圖中左邊的是Reflection probe(反射探針),右邊是Ambient probe,隨著24小時的變化,我們的Reflection probe和Ambient probe的內(nèi)容也會跟著變化。
我們先看一下左邊的Reflection probe,Reflection probe是用來給場景提供反射信息的。因為游戲的光影不斷變化,我們是不能夠簡單地為反射探針烘焙一張環(huán)境貼圖作為反射信息之用。所以對于每一個Reflection probe,我們是烘焙了一個mini GBuffer。產(chǎn)生在游戲當(dāng)中,根據(jù)當(dāng)時的光照條件去實(shí)時生成環(huán)境貼圖,美術(shù)可以在游戲里面擺很多個這樣的Reflection probe,只要他們需要。
然后在運(yùn)行的時候,我們會去更新場景的Reflection probe的cubemap。整個過程我們大致分為三步,第一步是Relight,第二步是Convolve和Compress。我們使用Compute Shader去同時處理六個面,然后分幀進(jìn)行,同時只處理一個probe,不停地做循環(huán)。
第一個步驟,就是Relight步驟,大家可以通過圖能看出來,就是一個簡單的把當(dāng)前的光照環(huán)境用來照亮mini GBuffer,得到環(huán)境貼圖的過程。然后生成的環(huán)境貼圖,需要經(jīng)過Convolve這一步,得到mipmap的正確信息。最后這個貼圖需要再通過一個Compute Shader的做法,壓縮成BC6H的格式,然后送到渲染管線里面去使用。大致是這么三步的過程。
下面是我們的Ambient probe。Ambient probe也是實(shí)時生成的。我們在做完Relight以后,Reflection probe是包含了當(dāng)前的整個光照信息,我們可以從中提取出當(dāng)前的Ambient的信息,并且把它轉(zhuǎn)化成一個3階的SH(Spherical Harmonic)系數(shù)保存下來。
這個提取的過程,在我們把Reflection probe處理完成以后會自動進(jìn)行,也是同時使用Compute Shader來處理六個面。
這么看下來,我們整個系統(tǒng)算是完成了,但實(shí)際上里面有很多地方是可以改進(jìn)的。第一個是Relight是沒有陰影的,因為單靠mini GBuffer我們是沒有辦法在Relight pass生成陰影,這樣會導(dǎo)致一個很大的問題。就是在Relight完成得到的環(huán)境貼圖是漏光的,本來應(yīng)該處于陰影當(dāng)中的地面也會變得非常明亮。
通過這樣的環(huán)境貼圖算出的環(huán)境光(ambient)也會出現(xiàn)有問題的情況,那怎么解決呢?我們的做法是,我們把24小時的shadow都烘焙下來,就是隔一段時間我們烘焙一下,把shadow轉(zhuǎn)化成一個shadow SH保存起來。在運(yùn)行的時候簡單通過當(dāng)前的時間對shadow SH進(jìn)行插值,用來壓暗Relight以后的結(jié)果。
這樣得到的效果是出乎意料的好,而且我們需要保存的數(shù)據(jù)非常的少。因為shadow SH很糊,所以我們做插值也沒有什么大的問題。
同樣的方式,我們還可以把Local Light的信息也保存下來,作為Local Light的SH在Relight的時候也加上去,這樣可以得到非常好、非常廉價的一個Local Light 反彈的效果。大家可以看一下對比,這張圖是沒有添加shadow SH,這張是添加的,大家可以看到?jīng)]添加的情況之下,屋檐和地面都莫名其妙的亮,添加之后就能夠看出來是在陰影當(dāng)中了,所以效果是很明顯的。
下面是我們把Local Light SH加進(jìn)去的情況對比。這是沒有添加的,大家注意看一下畫面右上角那片屋檐下面暗的區(qū)域。
我們現(xiàn)在已經(jīng)解決了漏光的問題,并且添加了Local Light SH。接下來是一個室內(nèi)室外光照環(huán)境不一致帶來的問題,因為室內(nèi)和室外的光照環(huán)境往往是很不一樣的。如果不加區(qū)分的話,室內(nèi)外的環(huán)境光(ambient)混在一起,得到的效果就很容易讓人家覺得不對勁。
我們是把Reflection probe分成室內(nèi)、室外兩種,然后美術(shù)通過擺放一個室內(nèi)環(huán)境用的網(wǎng)格(interior mesh)來標(biāo)記受室內(nèi)光影響的像素。Ambient probe也會相應(yīng)地為室內(nèi)、室外生成不同的環(huán)境光。
下面看一下對比,這是沒有開的情況,如果不區(qū)分的話,室內(nèi)跟室外一樣都會受天光的影響而變得很藍(lán),然后做了室內(nèi)(interior)標(biāo)記,室內(nèi)的像素就能夠正確地反應(yīng)出室內(nèi)的光照條件,會顯得更黃一些。
而且我們還做了一個過度的處理,就是在門口這個區(qū)域當(dāng)室內(nèi)光照和室外光照環(huán)境切換的時候,不會出現(xiàn)一個因為明顯的光照差異不一樣而產(chǎn)生的硬邊的效果。
下面這個就是室內(nèi)環(huán)境用的網(wǎng)格(interior mesh)生成的Mask標(biāo)記圖,紅色區(qū)域就是室內(nèi)的區(qū)域,大家可以看一下對照關(guān)系。除了通過Reflection probe得到的反射,我們還有Screen space reflection來提供實(shí)時的反射信息。SSR在PS4 Pro上面的GPU開銷大概是在1.5毫秒左右,我們對SSR也加了一個Temporal filter,通過當(dāng)前幀的SSR信息和歷史信息混合起來,來提高SSR計算結(jié)果的穩(wěn)定性,讓畫面也更平滑一些。
為了得到更多的反射信息,我們?yōu)镾SR生成了Hi-Z的buffer,我們可以讓每條射線通過Hi-Z最多能夠跟蹤的距離達(dá)到整個屏幕。
下面是一個SSR(Screen space reflection)效果開關(guān)的對比圖,在比較光滑的地板上效果尤其明顯。從前面的截圖大家也能夠看到,在沒有SSR的情況之下,我們還有Reflection probe,它也是可以提供場景的反射信息。我們是使用了一個Deferred reflection pass來計算Reflection和Ambient信息。在計算Reflection的同時,我們把AO信息也考慮進(jìn)去,這樣可以有效地降低漏光。
接下來是我們的最后一個技術(shù)點(diǎn),HDR Display。這里面的HDR Display包含了兩個方面,一個方面是指亮度,需要使用PQ ST2084的EOTF,最高是能夠讓畫面亮度達(dá)到10000 nits。另外一方面就是在色彩空間這邊,我們需要支持Rec.2020色彩空間,Rec.2020色彩空間和現(xiàn)在普遍的電視機(jī)使用的Rec.709相比,它可以顯示的色彩范圍要大得多。大家可以看看在CIE 1931色度圖里面的覆蓋范圍對比。Rec.2020色彩空間覆蓋范圍大概能達(dá)到75.8%,相比之下Rec.709只能在35.9%的樣子。
在這里我們稱使用了ST2084和Rec.2020色彩空間的渲染管線為HDR管線,而使用的Rec.709色彩空間的非HDR管線,我們把他們叫做SDR管線。關(guān)于HDR Display很多基礎(chǔ)的信息,有很多人都已經(jīng)講過在這里面就不細(xì)講了。下面主要講一下,為了讓這個技術(shù)放到《原神》里面去,我們做了哪些調(diào)整。
這張圖是《原神》的SDR和HDR的管線對比圖,和SDR管線相比,HDR管線沒有了tone mapping,color grading變成了HDR的color grading。而代替tone mapping的是RRT+ODT(reference rendering transform + output display transform)的組合,這就是很多人熟悉的ACES調(diào)色。
另外,UI在HDR下面,也是單獨(dú)畫到一張RT的。然后再跟場景做一個合并,這是因為UI的亮度處理方式跟場景是不太一樣的。在圖上大家看到RRT+ODT是灰掉的,我們待會會再來細(xì)講這個事情。
《原神》從1.2開始,會在PS4上面支持HDR10的模式,然后替換SDR的Color grading是我們的HDR Color grading,美術(shù)會在Davinci這種軟件上面去做HDR校色。然后通過我們的腳本輸出HDR的Look-Up-Table(LUT)。
在運(yùn)行的時候,白平衡HDR的Color grading,還有我們的Color expansion這些操作,都會在一個compute pass里面,輸出到一張Color的Look-Up-Table里面去。因為這個Look-Up-Table很小,所以盡管前面提到的這些都是需要大量的計算操作,但實(shí)際上開銷是很小的,大概不到0.05毫秒。
前面管線圖的RRT+ODT部分是灰掉的,雖然這個是一個主流的調(diào)色方式,但是我們并沒有采用它,因為和我們的游戲風(fēng)格不太搭。所以盡管主流,我們還是放棄了。
為了保證在低亮度范圍內(nèi)的畫面和SDR版本的游戲一致,我們將HDR的渲染畫面和tone mapping處理之后的畫面做了一個基于亮度的混合,然后在亮度不高的地方,就盡量保證了filmic tonemapping的關(guān)于toe部分的處理。
在低亮度范圍內(nèi)一致性的問題,這里面涉及到OOTF的事情。前面我們提到在HDR上面是用了BT1886作為一個EOTF曲線,然后在設(shè)備處理游戲輸出的時候會用到,相應(yīng)的游戲在輸出信號給電視機(jī)的時候也會增加一條曲線,這個就是OETF。然后在HDR的管線里面,實(shí)際上就是一條gamma曲線。
但是這里面存在一個問題,就是1886的gamma是2.4,但是SDR的OETF是一個分段函數(shù),大致上可以看作是gamma 2.2,這就出現(xiàn)了一個問題,也就是在PPT上寫的問題。OETF處理完的顏色,經(jīng)過EOTF,它得到的并不是它本身,就產(chǎn)生了一個誤差。
但是在HDR下面,因為我們OETF和EOTF是被很好的定義了的情況,所以他們是互逆的,于是這個顏色在經(jīng)過這兩個處理之后能夠得到原來的顏色,所以在HDR下面沒有這么一個問題。
但是大家已經(jīng)習(xí)慣了在HDR下面有這么一個誤差的畫面,所以為了模擬這個誤差,我們是在HDR管線里面添加了OOTF,把差異給補(bǔ)上去了。
做完這些,是不是HDR就徹底沒問題了呢?并不是。這還有一個大坑,叫做Hue Shift。什么解釋一下什么是Hue Shift?
舉個例子,大家可以看一下游戲里面火焰制作的示意圖,我們通過一個灰度圖,就是下面像饅頭一樣的東西,加一個噪聲貼圖,然后進(jìn)行擾動,得到了火焰擾動的紋理,然后我們用橘色去染色。最后我們把火焰的整體亮度往上提,這個時候就得到大家可以看到的見證奇跡的時刻,在SDR下面因為有tone mapping的原因,tone mapping在亮部是有一條曲線的,會讓亮度增加逐漸變慢,于是橘色的R通道跟G通道的差異本來是很大,但是隨著亮度的增加,tone mapping曲線介入了,R通道的增長就變慢,G通道就逐漸趕了上來,于是就產(chǎn)生了Hue Shift,畫面漸漸開始發(fā)黃,于是就得到了后面看到的SDR下面火焰的效果。
但是這個是美術(shù)想要的一個效果,只不過他是通過這種神奇的方式得到的。但是問題來了:因為在HDR下面,我們是沒有tone mapping的,Hue Shift是不會發(fā)生的。它叫做Hue Preserving。R通道跟G通道的比例關(guān)系是得到一個保持的,所以能夠得到亮度非常高的橘色。但是在視覺上,不會讓人家覺得很明亮,因為沒有黃色。
那怎么去修改呢?一種常用的方法,也是很多大作都用的方法,叫做黑體輻射。這是一個基于物理的算法,美術(shù)去指定溫度,根據(jù)溫度計算出應(yīng)該是什么顏色。通過這種方式,我們是需要修改美術(shù)的資源。不過我們沒有采用這種方式,為什么?一方面是因為HDR 這個功能是在很后期才加入的,我們不能夠讓美術(shù)去大量修改已有的資源。
另外游戲并不是寫實(shí)類的游戲,我們的火可以是各種顏色,而且還有很多其他的特效是隨著元素反應(yīng)去轉(zhuǎn)換顏色的。所以我們自己搞了一個方法,我們在shader里面去模擬了Hue Shift,并且把模擬放到了color grading pass里面去,合并到Look-Up-Table的計算中。
這樣的好處是,首先我們是不需要修改任何的效果,得到的效果非常滿意,我們不僅僅是讓火焰特效在HDR下面的效果跟SDR幾乎一致。而且因為通過引入tone mapping的方式模擬了Hue Shift,所以我們前面提到的在非HDR的亮度部分的畫面一致性的問題,也被順手解決掉了,不需要跟tone mapping處理之后的畫面做混合。而且這個操作,也是在生成Look-Up-Table的時候去做的,所以它的性能增加是可以忽略不計的。
技術(shù)介紹我們就到這里介紹了,下面是我一些個人的總結(jié)和感想。
首先是全球玩家對《原神》主機(jī)版的接受程度之高是出乎我們的預(yù)料,非常誠惶誠恐,也非常的感慨。從零開始,我把PS4版做起來,看著它逐漸地變化,就像看著自己的孩子逐漸長大一樣。隨著PS版越做越完善,很多的小伙伴就逐漸加入了開發(fā)隊伍中,于是PS4版變成了大家的孩子,大家努力讓它變得更好,最后就懷著忐忑的心情送出來跟全球玩家見面。沒想到現(xiàn)在這么受歡迎,所以是一件非常有成就感的事情。而且很開心,能夠和這么好的團(tuán)隊一起做這個項目,后面我們也會繼續(xù)努,不斷地改進(jìn),也不斷地去優(yōu)化,不斷地出更好的內(nèi)容給大家。
就像前面提到的,《原神》的主機(jī)版開發(fā)是第一次嘗試,時間、資源和人才都很缺乏。通過一年多的開發(fā),我們積累了很多的經(jīng)驗,尤其是如何把寫實(shí)的渲染技術(shù)跟風(fēng)格化游戲結(jié)合的經(jīng)驗,非常的寶貴!隨著新的主機(jī)平臺的到來,我們又面臨了一大波的技術(shù)升級。不過和在美國的時候相比,國內(nèi)主機(jī)開發(fā)的從業(yè)人員太少了,希望大家能夠多多交流,和我們探討一下技術(shù),交流一下開發(fā)經(jīng)驗。
我這次的分享就結(jié)束了,感謝大家!