祖龍娛樂王遠明:如何用UE4做出3A級材質和天氣系統?

Jimmy、Sam發表於2021-10-08
在近日舉行的北京國際遊戲創新大會(BIGC 2021)上,來自祖龍娛樂的引擎專家王遠明帶來了「龍族幻想材質系統優化和在天氣系統中的應用」主題分享,以祖龍娛樂旗下手遊《龍族幻想》為例子,講述祖龍娛樂在基於UE4引擎的移動遊戲製作過程中,如何對遊戲的材質以及天氣系統針對移動端進行優化。

以下是手遊那點事整理的演講實錄:

同學們下午好。今天我給大家帶來的題目是《龍族幻想》材質系統的優化,以及在天氣系統中的應用。《龍族幻想》是祖龍娛樂在2019年推出的一款使用UE製作的次世代手機遊戲。它也可以說是國內第一款使用UE在手機平臺的MMORPG的次世代遊戲,同時也證明了UE可以在移動端製作品質非常優秀的遊戲。因為它之前的給人印象是優勢主要在主機和PC平臺。《龍族幻想》充分展示了UE在移動平臺也是可以製作出效能優良和畫面精美的遊戲。

祖龍娛樂王遠明:如何用UE4做出3A級材質和天氣系統?

所以在這裡也藉助這個機會,感謝UE的同學給予祖龍娛樂大力的支援,謝謝。今天我的主題分兩個方面,第一方面是材質系統優化,第二個方面是天氣系統。我今天講的並不是說如何使用它來製作逼真的天氣效果,而是採用一個什麼樣的方式、利用UE引擎的哪些特性來製作天氣系統。

可能有些同學沒有玩過《龍族幻想》這個遊戲,我準備了幾段視訊來展示遊戲效果。給大家放一下。相信大家通過這三個視訊,會對《龍族幻想》的天氣效果變化會有初步的感覺。

祖龍娛樂王遠明:如何用UE4做出3A級材質和天氣系統?

好,我下面具體講一些技術方面的東西。我今天帶來的內容沒有王總剛才的nanite那麼硬核,是偏應用層的。我剛剛說有兩個主要的部分。第一個部分是材質系統的優化,我主要講的是優化,並不是全部的介紹材質系統,由於時間關係,我只講我們對於UE引擎在材質方面做出一些優化。

一、材質系統

祖龍娛樂王遠明:如何用UE4做出3A級材質和天氣系統?

第一部分就講一下材質系統。材質系統我分四個方面來講,第一方面是UMaterial,就是最核心的一類。第二部分是ShaderMap、第三個部分是在渲染方面做的一些優化,第四個是在Shadercode方面做的優化。

在講這個優化之前呢,我要先講一下優化所涉及到的一些關鍵的類,可能程式設計師比較敏感點,我直接寫了類的名字。從基類到派生類,以及其他的一些輔助類,UMaterialInterface,是材質的最基本的類。任何的一個材質,不管是UMaterial還是UMaterial Instance,都是UMaterialInterface的一個子類,他所表示的是一個最基礎的類。

祖龍娛樂王遠明:如何用UE4做出3A級材質和天氣系統?

第二個UMaterial是對應在你們編輯器裡面去建立一個材質的時候,檔案最終就會對應的一個UMaterial類。這裡面會儲存有不同的引數,在開啟之後會有各種節點,效果、貼圖引數之類的都會存在這個裡面,它對應的就是UMaterial類。

第三個是UMaterial Instance,當UMaterial寫好之後呢,可能裡面有很多引數比如一個float值,還有貼圖。但是如果不用Instance,而直接用UMaterial的話,那麼引數是固定的,在執行期的話是沒法改的。當你把一些引數匯出成Parameter引數的時候,你可以建立一個UMaterial Instance的類,再通過這個類賦值。在執行期的時候,可以通過介面來修改引數。這樣的話,其實可以做到UMaterial共享,不同的引數有不同的UMaterial Instance,設定不同的引數。底層使用同樣的一個UMaterial類,來達到共享以及效率的提升。

第四個類是UMaterial Instance Dynamic,這個類是在UMaterial Instance的基礎上。在執行期你可以有一些引數的設定,去設定不同的值。

在執行期間,如果你還有一些引數需要改,那怎麼辦呢?有Dynamic這麼一個派生類,在這裡面可以放你自己想要的東西。我們後續所做的優化還有包括天氣系統的實現,其實最主要就是在這個Dynamic上去儲存每一個基於模型的質量等級(QualityLevel)。

這前面的四個是有派生關係的,後面四個其實跟前面四個沒有多大關係,但是他是渲染最核心的四個類。第一個是FShader,程式設計師都知道,渲染的時候有Computer Shader、有Vertex Shader還有Pixel Shader。FShader是一個Shader最基礎的父類,他的派生類裡面可以是Vertex Shader、Computer Shader,也可以是Pixel Shader。

下一個FShaderMap,是當你做好了UMaterial之後,派生球拉好了之後、編譯完了之後,你可以給這個UMaterial賦予不同的模型,會有不同的頂點結構、還有各種引數,肯定是需要有不同的Shader。

他可以通過這個Shader以及factory去查詢最終需要渲染這個Mesh所需要的一個Shader。Shader最後對應的可能是不同的,首先你需要根據當前的模型來查詢到你合適的Shader。

所以它其實是一個記錄查詢表。對於mesh,是可以頂點結構不一樣的,需要結合vertex factory來查詢。有些是不需要頂點結構,只通過一個FShaderType作為關鍵詞就可以找到的。通過FShaderType和Vertex Factory,就可以找到一個最終的FShader,所以FShaderMap是一個類似於查詢表,也是我們後續做天氣系統的關鍵。

FMaterial這個類包含了一個FShaderMap。他增加了些介面方便大家用。比如說我現在是一個高質量的畫質,或者是一個低質量的畫質,通過引數的調節,來找到一個適合的FMaterial。在拿到這個FMaterial之後呢,他會最終找到這個FShaderMap。拿到FShader Map之後呢,你可以通過剛才說的FShaderType或者是頂點結構factory去找到真實的渲染需要的Shader。

最後一個ShaderCode,ShaderCode很簡單,就是不同的Shader寫完之後,編譯出來的二進位制的資料,一個UMaterial對應的一個檔案,它可以編譯出很多很多ShaderCode。一個ShaderCode,其實對應的是一個FShader的例項。

祖龍娛樂王遠明:如何用UE4做出3A級材質和天氣系統?

這就是一個關於材質系統,最核心的八個類。關係圖我只是列了上面這四個,他們是這樣的一個關係:UMaterialInterface是最上面的父類,派生出來UMaterial和UMaterial Instance。UMaterial和UMaterialInstance是兄弟關係。

UMaterialInstance還有一個子類,叫做UMaterialInstanceDynamic。Dynamic上面存在著我們的質量等級。然後在他的父類UMaterialInstance上面有一個parent,這個parent一般就指的是UMaterial,當然它也可以指向一個UMaterialInstance。

FShader是依靠在FMaterial裡面存在的渲染執行緒所使用的ShaderMap來查詢到的。這個是一個大致的關係圖,後續我還會講,他們在執行期會是怎樣這樣的一個結構,是一個什麼樣的情況。

祖龍娛樂王遠明:如何用UE4做出3A級材質和天氣系統?

剛才是繼承關係,這個是在引擎層面上的包含關係。

當你做好了一個材質,在編輯器裡建立好了一個之,材質載入進來的時候,它會變成一個UMaterial類,UMaterial的例項。他在這裡面最關鍵的成員是一個LoadedMaterial Resources陣列。

這個陣列記錄著很多ShaderMap,它是做什麼用的呢?比如說我們在遊戲裡面,在系統設定裡面,會選擇高畫質低畫質。每一種畫質每一種質量級別,對應著Loaded Material Resources這裡面的一項。比如說遊戲裡有3種級別,高中低的畫質。對應的陣列裡面就有三項,每一項都是一個ShaderMap,每一項最終都會得到一個ShaderMap,每一個FMaterial Resources裡面他表示的一個質量等級。

我們可以想象一下,如果你在系統設定裡面選擇了一個最高畫質,那麼在遊戲引擎執行的過程中,他就會選擇這個陣列裡面的最高畫質所對應的那一個FMaterial Resources。

在執行的時候,為什麼一個模型,它會呈現出最高畫質呢?是因為我們拿到了最高畫質所對應的FMaterial Resources,從而拿到了他的ShaderMap。我們在畫一個Mesh的時候,就可以通過FShaderType的關鍵標識,在這個map裡找到那個FShader,找到那個最高畫質的Shader,然後把它繪製出來。

這樣就達到了一個,你現在設計是最高效果,那它是一個非常好看的、很有質感的這麼一個渲染效果。如果你設定的是一個最低的,那麼通過這麼一個流程下來,你找到的就是一個最差的FShader。那麼效果可能就會很差。

UMaterial本質上是一個陣列的容器,他在執行期並不會知道需要去用哪一個質量等級,也就是不知道自己用哪一個Shader Map。他只是容器,後續是通過別的類來達到這樣的選擇。

祖龍娛樂王遠明:如何用UE4做出3A級材質和天氣系統?

剛才介紹了一些這個材質系統的一些關鍵的類的概念,我們講一下我們在《龍族幻想》裡面做了哪些優化的措施。

首先我們加了一個QualityBias,這個Bias就是一個偏差。我們可以設定一個全域性的級別,因為當你在系統設定頁面,你設定的可能是一個最高的精度,也可能是最差的精度,不管什麼精度,可能你的機器差一點,設計箇中等精度。那麼我們通過材質的QualityBias,這個偏差值,來疊加在你設定的這個級別上。比如說0的話,你在系統設定裡面選的中精度,渲染還是中精度。如果是1的話,它的這個質量級別應該會高一個等級,中就會變成高。當然這個中是不是變成高,這取決於你程式碼裡的具體數值,看你們怎麼去改引擎。

另外在lod中,Material Slot設定好之後,在每個LOD裡面,如果你們對編輯器比較熟悉,在每個LOD裡面會選擇用哪一個Material Slot,這樣的話,就決定了那個LOD,他在這個等級的偏差到底是多少。

到最終會體現在右下角的Dynamic,他會有一個Material 質量等級。我們會通過這個Bias,它的偏差值和系統設定裡的值,來計算出它當前的這個例項,所使用的材質的等級,從而讓這個模型本身達到區別於整體的畫質效果。

這樣我們就可以用他來做LOD的優化。比如說這個機器可能會比較好,設定的是一個最高精度的畫質,原來的引擎由於原生的功能,是隻能切換LOD,切換完了之後在材質上就可以替換。但是材質替換完之後還是用最高的精度其實是一種浪費。於是我們就加了優化功能,不僅切LOD,還降低質量等級,這樣的話就會更節省資源。

祖龍娛樂王遠明:如何用UE4做出3A級材質和天氣系統?

我們在Shader Map方面也做了一些優化,Shader Map剛才我講他最直觀的就是對應著系統設定裡面的高中低那麼幾檔,可能遊戲做了很多檔,不光是高中低,還有最高、最低。每一個這樣的等級,都在UMaterial這個材質檔案裡都會對應著一個Shader Map。不管是儲存資源,還是執行期的開銷,還是記憶體視訊記憶體,都是要佔資源的。

所以我們就做了優化,對於某些材質,其實不需要匯出所有的質量級別。也許他本身就很挫,可能是遠處的一個裝飾物,永遠沒有走近的機會,那可能無法展示出最高精度的那一面。所以我們就加了一個flag,沒有匯出所有的質量等級。勾選的話才會如同原生引擎效果一樣。如果不勾選的話,它會匯出預設的一個級別。預設的級別我們現在是用high來作為它的預設等級。

還有,我們又加了一個標誌,是記錄著哪些等級被使用。如果只有第一個的話,是否匯出?只有是或者否,要麼就所有的都導,要麼就只導一個預設的high。

後來我們還有個需求,就是它可能需要導一個high是不夠的,可能需要導一些。為什麼有這種需求,是因為後面會講到,我們使用了質量等級系統來做天氣。有些模型可能沒有雨和雪的這種效果,它也不需要雨和雪的這種質量等級。所以我們加了一個標誌位,加了一個flag,來記錄哪些質量等級是匯出了,哪些沒有匯出。

然後我們在UMaterial的函式裡面,我們優化了不需要的Shader Map。這個地方其實是可以優化的,但是因為它的檔案是逐位元組寫的,最好的方式是跳過那些不需要的Shader Map。但是現在我們並不太清楚每一個Shader Map佔的容量有多少。所以對於不需要的,我們直接載入進來,然後再丟棄,保證它序列化檔案的偏移值是正確的。

當你全部讀完之後,不需要的我們就把它扔掉,這樣能達到節省記憶體的目的。第四個優化方式是當我們寫了很多FShader子類的時候, FShader在這個UMaterial裡面肯定是用於特定的模型,當你不用於特定模型的時候,這種組合其實是不應該生成ShaderCode程式碼的。

舉一個最簡單的例子,我們角色的頭髮是用一個另外的渲染方式,我們為此改了非常多的程式碼、也加了很多的FShader型別。這些FShader的型別基本上導致了UE原生的FShader數量double了一下,是很恐怖的。如果我們不去通過這個函式指代需要的一些FShaderType和VertexFactory上面去使用它的話,FShader的量會很大。

還有就是我們需要降低Material 質量等級的數量。因為UE原來是有低、中、高、最高,後來我們發現FShader數量太多了扛不住,再做了一些優化,將不需要的一些質量等級去掉,這樣也會節省大量的FShader和ShaderCode,包體也會小很多。

祖龍娛樂王遠明:如何用UE4做出3A級材質和天氣系統?

這個是介面。在材質上面會加了一個匯出所有質量等級的選項,預設它是勾選的,只有對於一些特殊的材質才去掉。

剛才講了我們在遊戲的系統設定裡面,你會設定高中低。但是那樣一來的話,可能整體的遊戲都很挫。比如說你設計一個低的,後來又去設計一個高的話,有的時候你機器很差,但是我想讓玩家自己的那個角色呈現出一個比較好的效果,那麼我們需要針對於某些模型單獨設定它的級別。這個時候我們就改了UE引擎,我們基於PrimitiveComponent來設定一個質量級別。

祖龍娛樂王遠明:如何用UE4做出3A級材質和天氣系統?

為什麼放Primitive Component呢?因為他靜態的Mesh和蒙皮動畫都是這個類的子類。所以我把介面放在這個類上面。那麼第一步需要改的,就是當你呼叫這個介面的時候,我們發現當你設定的Material它不是一個Dynamic Material的時候,一般情況下美術做的時候它都會是一個Instance,美術做不出來Dynamic,Dynamic只是程式執行期生成的。一般都是Material Instance這麼一個類,它在這個類上是存不了資料,存不了我自己自定義的質量等級的。只有Dynamic這麼一個類,它上面會存著我的材質的質量等級。

當你發現這個材質需要設定,他不是一個Dynamic的時候,我會把它建立出一個Dynamic的一個Material類。然後再進行具體調整的方法。這個方法其實就是把當前的ShaderMap,就是FMaterial交給渲染執行緒。讓它把這FMaterial裡面對應的那個ShaderMap的Shader讓渲染執行緒建立出來。

祖龍娛樂王遠明:如何用UE4做出3A級材質和天氣系統?

每一個所使用的質量等級,它其實是儲存在這個UMaterialInstanceDynamic類上面的。在渲染的時候,它是怎麼使用的呢?在主執行緒你建立了Dynamic的Material Instance,獲取了對應的ShaderMap交給了渲染執行緒,來切換渲染執行緒使用的ShaderMap。

切換完之後,在FMaterial Instance Resource這個類裡面,它是渲染的一個代理類,是執行在渲染執行緒的。執行時候它會呼叫這麼一個方法去得到這個FShaderMap。因為最終它是要拿到Shader,渲染的時候你要拿到Shader,拿到Shader就要從ShaderMap裡去拿。

那麼,它最終是調了這個函式,在這個函式裡面拿到了正確的FMaterial。如果引擎不改原生的UE引擎,它怎麼拿呢?他是通過全域性的質量等級去拿。拿了之後呢,如果你改變了這個全域性的,那所有的、整個遊戲的畫質全部都要改。

我們是怎麼拿的呢?我們是在這個UMaterial Instance Dynamic裡面去拿到那個變數,最終需要的是這個子類變數而不是全域性變數,他們的型別都是一樣的。我們不讀全域性的變數,我們讀取在Dynamic裡面的變數來拿到這個ShaderMap。這樣的話,我們拿到了正確的ShaderMap之後,後續的邏輯、後續的功能,都是和UE原生的一樣。

我講一下ShaderCode。這個ShaderCode呢,是當你把材料球連好之後、質量等級建好了之後,他會編譯出很多的變種。因為你的Shader Type不一樣,你的頂點結構不一樣,你的質量等級不一樣,他會變出很多變種。這些變種都會變成一個二進位制的資料,有兩種儲存方式,在UE裡面需要設定。

祖龍娛樂王遠明:如何用UE4做出3A級材質和天氣系統?

如果你不選這個勾的話,這些東西它是存在這個UMaterial裡頭的,編譯完以後存在那個檔案裡面。如果你勾選這個勾,因為你不同的UMaterial生成的ShaderCode有可能是完全一樣的,為了共享,UE就把它放到了一個超大的檔案裡。因為ShaderCode很多,有幾萬個,他會放在一個很大的檔案裡面去。這個UMaterial檔案和那個UMaterial檔案,變出來的可能是一個完全相同的ShaderCode。

這樣的話,它在你最終釋出的包裡面,就只存在一份,整個的包體就會小。所以我們《龍族幻想》所有的ShaderCode方面優化都是需要勾選這個勾才行的。因為我們這種龐大的遊戲材質很多,如果不選這個勾,每一個都儲存在Material裡面的話,那整體的包體容量會很大,所以必須勾選這個。

如果對於小遊戲的話,本身包體就不是很大,那麼建議可以放在這裡面,不勾選這個。它的好處是你建立那個FShader的時間會比較快,會節省一點。他的包體,當你資源造好了之後,他這個Shader就已經準備好了。

祖龍娛樂王遠明:如何用UE4做出3A級材質和天氣系統?

接下來,再講一下我們對於ShaderCode儲存方面的優化,我記得是在UE4.17的時候,每一個Shader當你勾選的剛才那個選項之後,共享了ShaderCode之後呢,UE的方式是將每一個ShaderCode都存成一個檔案,放在磁碟的某一個目錄裡去。

這個我們當時覺得用的還是挺好的,後來在後續版本里它合併成一個大的檔案了,這個大的檔案裡面包含著若干個小的ShaderCode,相當於一個虛擬檔案系統。他的初衷可能想做一個這方面的優化,因為它的虛擬檔案包是pak,可能是想單獨為ShaderCode做一個優化。

但是這種優化,和我們《龍族幻想》的方式還有點衝突。所以我們在新版裡面,我們重新把這個大的檔案分拆成之前的版本,一個ShaderCode是一個檔案,檔名是它的雜湊值。因為很多的遊戲公司都有自己的虛擬檔案系統,當面對UE的這麼一個很大的檔案的時候,其實是沒辦法使用自己的虛擬檔案系統來做這個熱更新的。現在這個ShaderCode按照《龍族幻想》的級別應該是在四百兆左右的大檔案,如果是用UE的方式組成一個大包,那更新量是很大的。所以我們把它分拆了。

分拆了之後若干個檔案,我們需要使用自己的檔案系統。我們改變了哪些ShaderCode檔案,就單獨更新那些檔案。然後在這個FShaderCodeLibrary這個類,也做了修改。這個類的話,原來它的實現方式是你開啟一個檔案,首先開啟那個library,那個library裡面是一個很大的檔案,裡面再去逐個地開啟讀取ShaderCode。那麼我們現在就是去開啟單個ShaderCode檔案的時候,我們就直接開啟某一個檔案,然後把裡面的檔案內容讀出來。它的好處就是我們只會下載單獨的ShaderCode的更新包。

祖龍娛樂王遠明:如何用UE4做出3A級材質和天氣系統?

這是改進之後的長相。因為檔案特別多,有幾萬個檔案,在開發期的時候必須要做一個資料夾的對映。因為幾萬個檔案都存在一個資料夾裡的時候,檢視會卡很久。所以我們通過ShaderCode的檔名雜湊出來,把它分攤到若干個目錄裡去,這樣每個目錄的檔案數量不是很多。

然後每個檔案都是以它的雜湊值作為檔名的,這樣的話,當你在執行的時候去載入一個ShaderCode的時候,你拿到它的雜湊值,直接定位到這個檔案把它讀出來就ok了。然後,這樣的檔案通過我們的虛擬檔案管理打成一個大包,我們更改了哪些檔案,就只下載那些檔案,做到包體的最小化。這就是我們對於材質系統所做的一些優化。

二、天氣系統

祖龍娛樂王遠明:如何用UE4做出3A級材質和天氣系統?

下面我講一下天氣系統,之前我跟UE同學溝通,他們也收到了不少同學反饋說很想知道一些遊戲裡面的天氣系統是怎麼實現的,所以我覺得講一下這個還是很有必要的。在《龍族幻想》裡我們打算做天氣系統的時候想到很多方案。開始想的是,我們可以通過改Shader的引數,把雨啊雪啊什麼的都放在一個Shader裡面,通過它的引數來做一個切換。

最後發現這樣的話,我們的材質編器裡面的圖就會非常多,線畫的非常亂,並且效率非常低。我們後來想,要不就執行期改FShader,但改FShader其實對於每個模型,比如說當前是一個晴天的材質,裡面對應的是晴天的FShader,我們需要把它改成雨,那我們去找到這個雨的FShader,把它調整一下,這也是一個方案。

但是後來想一想,我們針對於每個模型去改FShader的這樣一個架構,在UE引擎裡面其實是沒有的。改動會比較大,挺麻煩的。直到我們看到了質量等級。我們發現它在全域性有一個功能是設定質量等級的,那我們在想,場景裡面很多天氣變化的時候,其實有一些模型是不會受天氣變化影響。比如室內的東西,我們不能通過改全域性的質量等級來切換Shade,我們就改成基於每個模型的。

所以我們最終想到,我們可以利用材質的質量等級這麼一個引擎自帶的功能去實現天氣系統。但是我們還需要改進,原先的功能不能滿足我們的要求。

然後第二個方面我介紹一下,就是我們需要採用這個Material Parameter Collection。因為每一個材質,它都會有一些差值。他需要有一些全域性的資料,比如我們現在需要知道他是從晴天開始切換到雨天,從雨天切回晴天,那麼這些全域性的資料和引數,就是取用這個引數的集合。

還有就是,我們在材質編輯器裡面,我們可以通過Quality Switch這個節點來做一個不同的等級效果的LOD,我現在細化一下。我們決定用質量等級來做天氣系統之後呢,我們就開始著手加,因為它原來不夠,只有低中高最高。我們加了好幾種,有雨和雪,雨和雪最終都加了最低、中、高、最高,很多很多。後來我們覺得這樣不行,我們砍掉了很多,只留下最基礎的一些。

祖龍娛樂王遠明:如何用UE4做出3A級材質和天氣系統?

所以我們在最差的機器上沒有天氣的變化,因為我們只有low,沒有low rain、low snow。在中等的機器上和最高階機器上,我們會有分別對應的雪和雨。比如說我們雪的變化,從medium的質量等級,切換到medium snow,然後晴天到雪、從晴天到雨還有反向的切換。

既然做了這種質量等級的劃分,我們就不需要把所有天氣做在一個Shader裡面。一個Shader裡,我們只做兩種天氣的過渡,比如晴天到雪天的過渡,我覺得這個不是特別的複雜,你只需要考慮兩種天氣。但是如果你要在這個材質裡面,還要做從雪到雨的切換的話,那這個Shader就會比較複雜,因為你要做到無縫的切換。

當前如果是雨的話,你想切換到雪, Shader即使換了之後,你還要保證現在效果跟換之前效果是對接好的,新的這種材質裡肯定也要有雨和雪的成分在裡面。所以我們做了限制,我們沒有雨和雪之間的切換,我們只做兩種。如果想做雨到雪的話,那通過策劃的玩法,先讓他變到晴天,然後再通過一個玩法或者事件讓他變到雪天。這樣曲線救國一下,Shader的複雜度就會大大的降低。

祖龍娛樂王遠明:如何用UE4做出3A級材質和天氣系統?

然後在一些低端機器上,我們會砍掉一些不必要的質量等級。比如說因為質量等級的載入是執行期做的,在UMaterial的函式裡面去做的,發現是比較差的機器的話,就只保留一種Low或者是Medium。要看實際的效果。可以寫一個很大的配置表,這個機器是用低還是用高,在執行期決定到底是扔掉哪些、保留哪些。

祖龍娛樂王遠明:如何用UE4做出3A級材質和天氣系統?

再講一下引數集。這個引數集我並不想特別展開每一項引數的意義。因為每一個遊戲,它天氣效果的渲染是非常複雜的一個圖表,會使用不同的引數。我現在只講一下它使用的思路。

天氣過渡會使用到到一些全域性的東西,比如說晴天到雨天,晴天到雪天過渡的一些權重,比如說時間,光線、風速,每個引數都可以在執行期改變。當你在做天氣切換的時候,你需要在遊戲邏輯上把一些引數設定好,讓它能夠有一個過渡銜接。比如說我們從晴天到雨天的過渡,當前這個模型所處的是晴天的一個Shader,這裡面的雨權重就是0。

祖龍娛樂王遠明:如何用UE4做出3A級材質和天氣系統?

切換的過程是在程式碼裡瞬間切換的。這個時候就需要我們在切換前把這些引數的集合,比如說雨,權重設成0,才不會突變。設成0之後,我們在執行期裡面去update時逐漸修改這個雨的權重值。這個雨的Shader裡面,他自然就會讀到雨的數值,雨的貼圖的比重,晴天貼圖的比重都會下降,漣漪的貼圖就慢慢的呈現出來。其他的效果就會慢慢地淡入,直到達到百分之百的穩定態。這個取決於你們的TA怎麼去連線材質的圖。

祖龍娛樂王遠明:如何用UE4做出3A級材質和天氣系統?

這是大概的一個長相。我們的引數非常多,有標量的引數,還有向量的引數,我只截了兩個。天氣材質球的連線也是相當複雜。在《龍族幻想》裡面還有一些效果,是區域性的,比如說我在這個區域裡面,可能是迷霧森林再走到一個別的區域裡面,我們會預先儲存很多的預製鍵,預製的一些資料集。

這些資料集就是對剛才那堆引數預先設定好的,比如說一個充滿黃色霧氣的地方,還有傍晚、早晨。我們存有很多引數集的檔案,當你在執行期想進入某一個區域的時候,通過邏輯把這些值載入進來,然後跟當前的值通過位置做一個lerp,取代或者是設定這個Shader讀取的那套引數集。最終的效果就會發生變化,就能達到魔獸世界那種穿過一個區域到達另外一個區域的效果。

祖龍娛樂王遠明:如何用UE4做出3A級材質和天氣系統?

這是一個簡單的示例,比如說像右邊就是一個全域性的引數集,它會有複雜的連線,這裡只是展示一個用法。用上這些全域性的引數之後,我們在引擎裡面去改這些值,整體渲染效果就會有變化。在我們的Shader圖示裡面,材質圖表裡面充斥著大量的、對於全域性的引數集合的應用,來達到這種最終的變化的效果。

祖龍娛樂王遠明:如何用UE4做出3A級材質和天氣系統?

當你在材質編輯器裡面去連線節點的時候,不光是通過一些剛才的那些全域性的引數來達到效果,還有一個重要的是通過Quality Switch節點。在最低精度下面,我可以走這條分支,高精度我走另外一條分支,達到一個Shader的優化。

祖龍娛樂王遠明:如何用UE4做出3A級材質和天氣系統?

這就是剛剛說的,基於這個節點的Switch。每一個節點的Switch最終都會被編譯成對應的ShaderCode,放在它自己的ShaderMap裡面。延伸過來之後可能會有非常複雜的邏輯,比如說這個Low,連過來之後可能是一個非常簡單的邏輯,這個高的雨、最高的雨連過來,可能它有更多的貼圖、更多的取樣,更多其他的效果,然後輸出到一個最終的節點。

祖龍娛樂王遠明:如何用UE4做出3A級材質和天氣系統?

限於時間關係,我給大家簡單介紹一下在渲染優化上做的一些事。UE原生的FShader沒有解除安裝的機制,載入進來之後就永遠留在那個地方,我覺得其實是一種效果浪費。因為可能FPS遊戲的Shader型別不是很多,當我們做《龍族幻想》的時候,對渲染效果要求很高。再加上我們加了很多質量等級去實現天氣效果,FShader的量就會很大,這個時候如果不解除安裝FShader的話,就會帶來很大的記憶體開銷,因此我們做了FShader的釋放。

這個原理其實也非常簡單,一個FShader建立出來會使用引用計數記錄在多少個地方使用,在切場景的時候我們會統一過一遍,我們會檢查一遍這些所有的FShader。如果它的引用計數為零了,我們就會把它釋放掉,需要放到渲染執行緒,或者是RHI執行緒裡面去釋放這個Shader。在不切場景的時候,我們也會時不時做一下,但是我們在切場景的時候,在Loading條走的時候,肯定要做一遍釋放檢查。因為loading的時候卡一下的話也沒什麼關係。

然後就是OpenGL的一個program binary data cache。這個是在OpenGL裡面,安卓平臺下面,當你設定一個shader的時候,你需要Link。Link有點卡,當然如果Fps遊戲FShader不多的時候,不會感覺特別明顯。但是《龍族幻想》有很多Shader,並且不同的模型可能Shader會不一樣,他的這種卡頓就會變成一個問題。

祖龍娛樂王遠明:如何用UE4做出3A級材質和天氣系統?

當Link完了之後,生成一個program,我們會拿到這個data,把它存下來,存在一個檔案裡。我做了一個虛擬的檔案系統,這個虛擬檔案的key就是那段program的data。

存下來以後,當第一次執行遊戲時這個檔案是空的,每次都會Link,然後存進去。當第二次執行的時候,情況就會變好了。以前曾經Link過的,我直接在檔案裡找到,通過這個Program載入進來,也不需要去設定Shader,也不需要Link,直接就可以用了。

第三個是多個PSO cache file 。UE4自己有一個功能是對PSO的快取,原來是叫Shader cache,後來改成pipelinecache。

他只有一個檔案,我們改進了這個功能。在錄的時候,我們可以錄很多個檔案。比如我們在打Boss戰的時候,那個Boss以前從沒出來過。他一出來,不管你是Link還是怎麼樣,反正他會卡頓。因為就算是你Link了,他往顯示卡送的那一刻,往鏡頭送那一刻,他也會有一定的時間開銷。然後當那個Boss從來沒出現過,一出來就卡一下,這個效果不太好。

還有就是我們遊戲執行Loading完了之後,我們需要播一個CG,那個CG有很多也是遊戲不太用到的資源,它也會卡一下。我們還是想用引擎的PSO功能,記錄的功能,然後把它預熱一下。但是一個不夠,尤其是出現怪物的時候。然後我們就做了一個錄不同的PSO的cache。PSO cache需要錄渲染所有的引數,Shader,各種引數都錄下來。錄下來之後當你需要播這些、需要畫這些檔案的時候,它會在後臺給你把這些東西跑一遍。這樣的話,當你真正渲染模型的時候就不會卡頓,我們做了多個這樣的檔案。比如說,在這個Loading條結束的時候,我們需要播CG,那在Loading條結束的時候,就載入這個場景所對應的記錄好的檔案。當這個CG播放的時候就會非常的平滑,沒有一絲的卡頓。對於boss也是這樣,快到播boss的時候,我們也在後臺把這個cache檔案載入進來,做一下這樣的預熱,就會達到非常好的平滑效果。今天的分享大概就是這些,非常感謝。


來源:手遊那點事
原文:https://mp.weixin.qq.com/s/YGqcmBq536OWpPGUFKKMNA


相關文章