日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 运维知识 > windows >内容正文

windows

祖龙娱乐王远明:如何用UE4做出3A级材质和天气系统?

發(fā)布時(shí)間:2024/8/26 windows 48 豆豆
生活随笔 收集整理的這篇文章主要介紹了 祖龙娱乐王远明:如何用UE4做出3A级材质和天气系统? 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

在近日舉行的北京國際游戲創(chuàng)新大會(huì)(BIGC 2021)上,來自祖龍娛樂的引擎專家王遠(yuǎn)明帶來了「龍族幻想材質(zhì)系統(tǒng)優(yōu)化和在天氣系統(tǒng)中的應(yīng)用」主題分享,以祖龍娛樂旗下手游《龍族幻想》為例子,講述祖龍娛樂在基于UE4引擎的移動(dòng)游戲制作過程中,如何對游戲的材質(zhì)以及天氣系統(tǒng)針對移動(dòng)端進(jìn)行優(yōu)化。

以下是手游那點(diǎn)事整理的演講實(shí)錄:

同學(xué)們下午好。今天我給大家?guī)淼念}目是《龍族幻想》材質(zhì)系統(tǒng)的優(yōu)化,以及在天氣系統(tǒng)中的應(yīng)用。《龍族幻想》是祖龍娛樂在2019年推出的一款使用UE制作的次世代手機(jī)游戲。它也可以說是國內(nèi)第一款使用UE在手機(jī)平臺的MMORPG的次世代游戲,同時(shí)也證明了UE可以在移動(dòng)端制作品質(zhì)非常優(yōu)秀的游戲。因?yàn)樗暗慕o人印象是優(yōu)勢主要在主機(jī)和PC平臺。《龍族幻想》充分展示了UE在移動(dòng)平臺也是可以制作出性能優(yōu)良和畫面精美的游戲。
?


所以在這里也借助這個(gè)機(jī)會(huì),感謝UE的同學(xué)給予祖龍娛樂大力的支持,謝謝。今天我的主題分兩個(gè)方面,第一方面是材質(zhì)系統(tǒng)優(yōu)化,第二個(gè)方面是天氣系統(tǒng)。我今天講的并不是說如何使用它來制作逼真的天氣效果,而是采用一個(gè)什么樣的方式、利用UE引擎的哪些特性來制作天氣系統(tǒng)。

可能有些同學(xué)沒有玩過《龍族幻想》這個(gè)游戲,我準(zhǔn)備了幾段視頻來展示游戲效果。給大家放一下。相信大家通過這三個(gè)視頻,會(huì)對《龍族幻想》的天氣效果變化會(huì)有初步的感覺。
?


好,我下面具體講一些技術(shù)方面的東西。我今天帶來的內(nèi)容沒有王總剛才的nanite那么硬核,是偏應(yīng)用層的。我剛剛說有兩個(gè)主要的部分。第一個(gè)部分是材質(zhì)系統(tǒng)的優(yōu)化,我主要講的是優(yōu)化,并不是全部的介紹材質(zhì)系統(tǒng),由于時(shí)間關(guān)系,我只講我們對于UE引擎在材質(zhì)方面做出一些優(yōu)化。

一、材質(zhì)系統(tǒng)
?


第一部分就講一下材質(zhì)系統(tǒng)。材質(zhì)系統(tǒng)我分四個(gè)方面來講,第一方面是UMaterial,就是最核心的一類。第二部分是ShaderMap、第三個(gè)部分是在渲染方面做的一些優(yōu)化,第四個(gè)是在Shadercode方面做的優(yōu)化。

在講這個(gè)優(yōu)化之前呢,我要先講一下優(yōu)化所涉及到的一些關(guān)鍵的類,可能程序員比較敏感點(diǎn),我直接寫了類的名字。從基類到派生類,以及其他的一些輔助類,UMaterialInterface,是材質(zhì)的最基本的類。任何的一個(gè)材質(zhì),不管是UMaterial還是UMaterial Instance,都是UMaterialInterface的一個(gè)子類,他所表示的是一個(gè)最基礎(chǔ)的類。
?


第二個(gè)UMaterial是對應(yīng)在你們編輯器里面去創(chuàng)建一個(gè)材質(zhì)的時(shí)候,文件最終就會(huì)對應(yīng)的一個(gè)UMaterial類。這里面會(huì)存儲(chǔ)有不同的參數(shù),在打開之后會(huì)有各種節(jié)點(diǎn),效果、貼圖參數(shù)之類的都會(huì)存在這個(gè)里面,它對應(yīng)的就是UMaterial類。

第三個(gè)是UMaterial Instance,當(dāng)UMaterial寫好之后呢,可能里面有很多參數(shù)比如一個(gè)float值,還有貼圖。但是如果不用Instance,而直接用UMaterial的話,那么參數(shù)是固定的,在運(yùn)行期的話是沒法改的。當(dāng)你把一些參數(shù)導(dǎo)出成Parameter參數(shù)的時(shí)候,你可以創(chuàng)建一個(gè)UMaterial Instance的類,再通過這個(gè)類賦值。在運(yùn)行期的時(shí)候,可以通過接口來修改參數(shù)。這樣的話,其實(shí)可以做到UMaterial共享,不同的參數(shù)有不同的UMaterial Instance,設(shè)置不同的參數(shù)。底層使用同樣的一個(gè)UMaterial類,來達(dá)到共享以及效率的提升。

第四個(gè)類是UMaterial Instance Dynamic,這個(gè)類是在UMaterial Instance的基礎(chǔ)上。在運(yùn)行期你可以有一些參數(shù)的設(shè)置,去設(shè)置不同的值。

在運(yùn)行期間,如果你還有一些參數(shù)需要改,那怎么辦呢?有Dynamic這么一個(gè)派生類,在這里面可以放你自己想要的東西。我們后續(xù)所做的優(yōu)化還有包括天氣系統(tǒng)的實(shí)現(xiàn),其實(shí)最主要就是在這個(gè)Dynamic上去存儲(chǔ)每一個(gè)基于模型的質(zhì)量等級(QualityLevel)。

這前面的四個(gè)是有派生關(guān)系的,后面四個(gè)其實(shí)跟前面四個(gè)沒有多大關(guān)系,但是他是渲染最核心的四個(gè)類。第一個(gè)是FShader,程序員都知道,渲染的時(shí)候有Computer Shader、有Vertex Shader還有Pixel Shader。FShader是一個(gè)Shader最基礎(chǔ)的父類,他的派生類里面可以是Vertex Shader、Computer Shader,也可以是Pixel Shader。

下一個(gè)FShaderMap,是當(dāng)你做好了UMaterial之后,派生球拉好了之后、編譯完了之后,你可以給這個(gè)UMaterial賦予不同的模型,會(huì)有不同的頂點(diǎn)結(jié)構(gòu)、還有各種參數(shù),肯定是需要有不同的Shader。

他可以通過這個(gè)Shader以及factory去查找最終需要渲染這個(gè)Mesh所需要的一個(gè)Shader。Shader最后對應(yīng)的可能是不同的,首先你需要根據(jù)當(dāng)前的模型來查找到你合適的Shader。

所以它其實(shí)是一個(gè)記錄查詢表。對于mesh,是可以頂點(diǎn)結(jié)構(gòu)不一樣的,需要結(jié)合vertex factory來查詢。有些是不需要頂點(diǎn)結(jié)構(gòu),只通過一個(gè)FShaderType作為關(guān)鍵詞就可以找到的。通過FShaderType和Vertex Factory,就可以找到一個(gè)最終的FShader,所以FShaderMap是一個(gè)類似于查詢表,也是我們后續(xù)做天氣系統(tǒng)的關(guān)鍵。

FMaterial這個(gè)類包含了一個(gè)FShaderMap。他增加了些接口方便大家用。比如說我現(xiàn)在是一個(gè)高質(zhì)量的畫質(zhì),或者是一個(gè)低質(zhì)量的畫質(zhì),通過參數(shù)的調(diào)節(jié),來找到一個(gè)適合的FMaterial。在拿到這個(gè)FMaterial之后呢,他會(huì)最終找到這個(gè)FShaderMap。拿到FShader Map之后呢,你可以通過剛才說的FShaderType或者是頂點(diǎn)結(jié)構(gòu)factory去找到真實(shí)的渲染需要的Shader。

最后一個(gè)ShaderCode,ShaderCode很簡單,就是不同的Shader寫完之后,編譯出來的二進(jìn)制的數(shù)據(jù),一個(gè)UMaterial對應(yīng)的一個(gè)文件,它可以編譯出很多很多ShaderCode。一個(gè)ShaderCode,其實(shí)對應(yīng)的是一個(gè)FShader的實(shí)例。
?


這就是一個(gè)關(guān)于材質(zhì)系統(tǒng),最核心的八個(gè)類。關(guān)系圖我只是列了上面這四個(gè),他們是這樣的一個(gè)關(guān)系:UMaterialInterface是最上面的父類,派生出來UMaterial和UMaterial Instance。UMaterial和UMaterialInstance是兄弟關(guān)系。

UMaterialInstance還有一個(gè)子類,叫做UMaterialInstanceDynamic。Dynamic上面存在著我們的質(zhì)量等級。然后在他的父類UMaterialInstance上面有一個(gè)parent,這個(gè)parent一般就指的是UMaterial,當(dāng)然它也可以指向一個(gè)UMaterialInstance。

FShader是依靠在FMaterial里面存在的渲染線程所使用的ShaderMap來查詢到的。這個(gè)是一個(gè)大致的關(guān)系圖,后續(xù)我還會(huì)講,他們在運(yùn)行期會(huì)是怎樣這樣的一個(gè)結(jié)構(gòu),是一個(gè)什么樣的情況。
?


剛才是繼承關(guān)系,這個(gè)是在引擎層面上的包含關(guān)系。

當(dāng)你做好了一個(gè)材質(zhì),在編輯器里創(chuàng)建好了一個(gè)之,材質(zhì)加載進(jìn)來的時(shí)候,它會(huì)變成一個(gè)UMaterial類,UMaterial的實(shí)例。他在這里面最關(guān)鍵的成員是一個(gè)LoadedMaterial Resources數(shù)組。

這個(gè)數(shù)組記錄著很多ShaderMap,它是做什么用的呢?比如說我們在游戲里面,在系統(tǒng)設(shè)置里面,會(huì)選擇高畫質(zhì)低畫質(zhì)。每一種畫質(zhì)每一種質(zhì)量級別,對應(yīng)著Loaded Material Resources這里面的一項(xiàng)。比如說游戲里有3種級別,高中低的畫質(zhì)。對應(yīng)的數(shù)組里面就有三項(xiàng),每一項(xiàng)都是一個(gè)ShaderMap,每一項(xiàng)最終都會(huì)得到一個(gè)ShaderMap,每一個(gè)FMaterial Resources里面他表示的一個(gè)質(zhì)量等級。

我們可以想象一下,如果你在系統(tǒng)設(shè)置里面選擇了一個(gè)最高畫質(zhì),那么在游戲引擎運(yùn)行的過程中,他就會(huì)選擇這個(gè)數(shù)組里面的最高畫質(zhì)所對應(yīng)的那一個(gè)FMaterial Resources。

在運(yùn)行的時(shí)候,為什么一個(gè)模型,它會(huì)呈現(xiàn)出最高畫質(zhì)呢?是因?yàn)槲覀兡玫搅俗罡弋嬞|(zhì)所對應(yīng)的FMaterial Resources,從而拿到了他的ShaderMap。我們在畫一個(gè)Mesh的時(shí)候,就可以通過FShaderType的關(guān)鍵標(biāo)識,在這個(gè)map里找到那個(gè)FShader,找到那個(gè)最高畫質(zhì)的Shader,然后把它繪制出來。

這樣就達(dá)到了一個(gè),你現(xiàn)在設(shè)計(jì)是最高效果,那它是一個(gè)非常好看的、很有質(zhì)感的這么一個(gè)渲染效果。如果你設(shè)置的是一個(gè)最低的,那么通過這么一個(gè)流程下來,你找到的就是一個(gè)最差的FShader。那么效果可能就會(huì)很差。

UMaterial本質(zhì)上是一個(gè)數(shù)組的容器,他在運(yùn)行期并不會(huì)知道需要去用哪一個(gè)質(zhì)量等級,也就是不知道自己用哪一個(gè)Shader Map。他只是容器,后續(xù)是通過別的類來達(dá)到這樣的選擇。
?


剛才介紹了一些這個(gè)材質(zhì)系統(tǒng)的一些關(guān)鍵的類的概念,我們講一下我們在《龍族幻想》里面做了哪些優(yōu)化的措施。

首先我們加了一個(gè)QualityBias,這個(gè)Bias就是一個(gè)偏差。我們可以設(shè)置一個(gè)全局的級別,因?yàn)楫?dāng)你在系統(tǒng)設(shè)置頁面,你設(shè)置的可能是一個(gè)最高的精度,也可能是最差的精度,不管什么精度,可能你的機(jī)器差一點(diǎn),設(shè)計(jì)個(gè)中等精度。那么我們通過材質(zhì)的QualityBias,這個(gè)偏差值,來疊加在你設(shè)置的這個(gè)級別上。比如說0的話,你在系統(tǒng)設(shè)置里面選的中精度,渲染還是中精度。如果是1的話,它的這個(gè)質(zhì)量級別應(yīng)該會(huì)高一個(gè)等級,中就會(huì)變成高。當(dāng)然這個(gè)中是不是變成高,這取決于你代碼里的具體數(shù)值,看你們怎么去改引擎。

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

到最終會(huì)體現(xiàn)在右下角的Dynamic,他會(huì)有一個(gè)Material 質(zhì)量等級。我們會(huì)通過這個(gè)Bias,它的偏差值和系統(tǒng)設(shè)置里的值,來計(jì)算出它當(dāng)前的這個(gè)實(shí)例,所使用的材質(zhì)的等級,從而讓這個(gè)模型本身達(dá)到區(qū)別于整體的畫質(zhì)效果。

這樣我們就可以用他來做LOD的優(yōu)化。比如說這個(gè)機(jī)器可能會(huì)比較好,設(shè)置的是一個(gè)最高精度的畫質(zhì),原來的引擎由于原生的功能,是只能切換LOD,切換完了之后在材質(zhì)上就可以替換。但是材質(zhì)替換完之后還是用最高的精度其實(shí)是一種浪費(fèi)。于是我們就加了優(yōu)化功能,不僅切LOD,還降低質(zhì)量等級,這樣的話就會(huì)更節(jié)省資源。
?


我們在Shader Map方面也做了一些優(yōu)化,Shader Map剛才我講他最直觀的就是對應(yīng)著系統(tǒng)設(shè)置里面的高中低那么幾檔,可能游戲做了很多檔,不光是高中低,還有最高、最低。每一個(gè)這樣的等級,都在UMaterial這個(gè)材質(zhì)文件里都會(huì)對應(yīng)著一個(gè)Shader Map。不管是存儲(chǔ)資源,還是運(yùn)行期的開銷,還是內(nèi)存顯存,都是要占資源的。

所以我們就做了優(yōu)化,對于某些材質(zhì),其實(shí)不需要導(dǎo)出所有的質(zhì)量級別。也許他本身就很挫,可能是遠(yuǎn)處的一個(gè)裝飾物,永遠(yuǎn)沒有走近的機(jī)會(huì),那可能無法展示出最高精度的那一面。所以我們就加了一個(gè)flag,沒有導(dǎo)出所有的質(zhì)量等級。勾選的話才會(huì)如同原生引擎效果一樣。如果不勾選的話,它會(huì)導(dǎo)出默認(rèn)的一個(gè)級別。默認(rèn)的級別我們現(xiàn)在是用high來作為它的默認(rèn)等級。

還有,我們又加了一個(gè)標(biāo)志,是記錄著哪些等級被使用。如果只有第一個(gè)的話,是否導(dǎo)出?只有是或者否,要么就所有的都導(dǎo),要么就只導(dǎo)一個(gè)缺省的high。

后來我們還有個(gè)需求,就是它可能需要導(dǎo)一個(gè)high是不夠的,可能需要導(dǎo)一些。為什么有這種需求,是因?yàn)楹竺鏁?huì)講到,我們使用了質(zhì)量等級系統(tǒng)來做天氣。買二手手游賬號有些模型可能沒有雨和雪的這種效果,它也不需要雨和雪的這種質(zhì)量等級。所以我們加了一個(gè)標(biāo)志位,加了一個(gè)flag,來記錄哪些質(zhì)量等級是導(dǎo)出了,哪些沒有導(dǎo)出。

然后我們在UMaterial的函數(shù)里面,我們優(yōu)化了不需要的Shader Map。這個(gè)地方其實(shí)是可以優(yōu)化的,但是因?yàn)樗奈募侵鹱止?jié)寫的,最好的方式是跳過那些不需要的Shader Map。但是現(xiàn)在我們并不太清楚每一個(gè)Shader Map占的容量有多少。所以對于不需要的,我們直接加載進(jìn)來,然后再丟棄,保證它序列化文件的偏移值是正確的。

當(dāng)你全部讀完之后,不需要的我們就把它扔掉,這樣能達(dá)到節(jié)省內(nèi)存的目的。第四個(gè)優(yōu)化方式是當(dāng)我們寫了很多FShader子類的時(shí)候, FShader在這個(gè)UMaterial里面肯定是用于特定的模型,當(dāng)你不用于特定模型的時(shí)候,這種組合其實(shí)是不應(yīng)該生成ShaderCode代碼的。

舉一個(gè)最簡單的例子,我們角色的頭發(fā)是用一個(gè)另外的渲染方式,我們?yōu)榇烁牧朔浅6嗟拇a、也加了很多的FShader類型。這些FShader的類型基本上導(dǎo)致了UE原生的FShader數(shù)量double了一下,是很恐怖的。如果我們不去通過這個(gè)函數(shù)指代需要的一些FShaderType和VertexFactory上面去使用它的話,FShader的量會(huì)很大。

還有就是我們需要降低Material 質(zhì)量等級的數(shù)量。因?yàn)閁E原來是有低、中、高、最高,后來我們發(fā)現(xiàn)FShader數(shù)量太多了扛不住,再做了一些優(yōu)化,將不需要的一些質(zhì)量等級去掉,這樣也會(huì)節(jié)省大量的FShader和ShaderCode,包體也會(huì)小很多。


這個(gè)是界面。在材質(zhì)上面會(huì)加了一個(gè)導(dǎo)出所有質(zhì)量等級的選項(xiàng),默認(rèn)它是勾選的,只有對于一些特殊的材質(zhì)才去掉。

剛才講了我們在游戲的系統(tǒng)設(shè)置里面,你會(huì)設(shè)置高中低。但是那樣一來的話,可能整體的游戲都很挫。比如說你設(shè)計(jì)一個(gè)低的,后來又去設(shè)計(jì)一個(gè)高的話,有的時(shí)候你機(jī)器很差,但是我想讓玩家自己的那個(gè)角色呈現(xiàn)出一個(gè)比較好的效果,那么我們需要針對于某些模型單獨(dú)設(shè)置它的級別。這個(gè)時(shí)候我們就改了UE引擎,我們基于PrimitiveComponent來設(shè)置一個(gè)質(zhì)量級別。
?


為什么放Primitive Component呢?因?yàn)樗o態(tài)的Mesh和蒙皮動(dòng)畫都是這個(gè)類的子類。所以我把接口放在這個(gè)類上面。那么第一步需要改的,就是當(dāng)你調(diào)用這個(gè)接口的時(shí)候,我們發(fā)現(xiàn)當(dāng)你設(shè)置的Material它不是一個(gè)Dynamic Material的時(shí)候,一般情況下美術(shù)做的時(shí)候它都會(huì)是一個(gè)Instance,美術(shù)做不出來Dynamic,Dynamic只是程序運(yùn)行期生成的。一般都是Material Instance這么一個(gè)類,它在這個(gè)類上是存不了數(shù)據(jù),存不了我自己自定義的質(zhì)量等級的。只有Dynamic這么一個(gè)類,它上面會(huì)存著我的材質(zhì)的質(zhì)量等級。

當(dāng)你發(fā)現(xiàn)這個(gè)材質(zhì)需要設(shè)置,他不是一個(gè)Dynamic的時(shí)候,我會(huì)把它創(chuàng)建出一個(gè)Dynamic的一個(gè)Material類。然后再進(jìn)行具體調(diào)整的方法。這個(gè)方法其實(shí)就是把當(dāng)前的ShaderMap,就是FMaterial交給渲染線程。讓它把這FMaterial里面對應(yīng)的那個(gè)ShaderMap的Shader讓渲染線程創(chuàng)建出來。
?


每一個(gè)所使用的質(zhì)量等級,它其實(shí)是保存在這個(gè)UMaterialInstanceDynamic類上面的。在渲染的時(shí)候,它是怎么使用的呢?在主線程你創(chuàng)建了Dynamic的Material Instance,獲取了對應(yīng)的ShaderMap交給了渲染線程,來切換渲染線程使用的ShaderMap。

切換完之后,在FMaterial Instance Resource這個(gè)類里面,它是渲染的一個(gè)代理類,是運(yùn)行在渲染線程的。運(yùn)行時(shí)候它會(huì)調(diào)用這么一個(gè)方法去得到這個(gè)FShaderMap。因?yàn)樽罱K它是要拿到Shader,渲染的時(shí)候你要拿到Shader,拿到Shader就要從ShaderMap里去拿。

那么,它最終是調(diào)了這個(gè)函數(shù),在這個(gè)函數(shù)里面拿到了正確的FMaterial。如果引擎不改原生的UE引擎,它怎么拿呢?他是通過全局的質(zhì)量等級去拿。拿了之后呢,如果你改變了這個(gè)全局的,那所有的、整個(gè)游戲的畫質(zhì)全部都要改。

我們是怎么拿的呢?我們是在這個(gè)UMaterial Instance Dynamic里面去拿到那個(gè)變量,最終需要的是這個(gè)子類變量而不是全局變量,他們的類型都是一樣的。我們不讀全局的變量,我們讀取在Dynamic里面的變量來拿到這個(gè)ShaderMap。這樣的話,我們拿到了正確的ShaderMap之后,后續(xù)的邏輯、后續(xù)的功能,都是和UE原生的一樣。

我講一下ShaderCode。這個(gè)ShaderCode呢,是當(dāng)你把材料球連好之后、質(zhì)量等級建好了之后,他會(huì)編譯出很多的變種。因?yàn)槟愕腟hader Type不一樣,你的頂點(diǎn)結(jié)構(gòu)不一樣,你的質(zhì)量等級不一樣,他會(huì)變出很多變種。這些變種都會(huì)變成一個(gè)二進(jìn)制的數(shù)據(jù),有兩種存儲(chǔ)方式,在UE里面需要設(shè)置。
?


如果你不選這個(gè)勾的話,這些東西它是存在這個(gè)UMaterial里頭的,編譯完以后存在那個(gè)文件里面。如果你勾選這個(gè)勾,因?yàn)槟悴煌腢Material生成的ShaderCode有可能是完全一樣的,為了共享,UE就把它放到了一個(gè)超大的文件里。因?yàn)镾haderCode很多,有幾萬個(gè),他會(huì)放在一個(gè)很大的文件里面去。這個(gè)UMaterial文件和那個(gè)UMaterial文件,變出來的可能是一個(gè)完全相同的ShaderCode。

這樣的話,它在你最終發(fā)布的包里面,就只存在一份,整個(gè)的包體就會(huì)小。所以我們《龍族幻想》所有的ShaderCode方面優(yōu)化都是需要勾選這個(gè)勾才行的。因?yàn)槲覀冞@種龐大的游戲材質(zhì)很多,如果不選這個(gè)勾,每一個(gè)都儲(chǔ)存在Material里面的話,那整體的包體容量會(huì)很大,所以必須勾選這個(gè)。

如果對于小游戲的話,本身包體就不是很大,那么建議可以放在這里面,不勾選這個(gè)。它的好處是你創(chuàng)建那個(gè)FShader的時(shí)間會(huì)比較快,會(huì)節(jié)省一點(diǎn)。他的包體,當(dāng)你資源造好了之后,他這個(gè)Shader就已經(jīng)準(zhǔn)備好了。
?


接下來,再講一下我們對于ShaderCode存儲(chǔ)方面的優(yōu)化,我記得是在UE4.17的時(shí)候,每一個(gè)Shader當(dāng)你勾選的剛才那個(gè)選項(xiàng)之后,共享了ShaderCode之后呢,UE的方式是將每一個(gè)ShaderCode都存成一個(gè)文件,放在磁盤的某一個(gè)目錄里去。

這個(gè)我們當(dāng)時(shí)覺得用的還是挺好的,后來在后續(xù)版本里它合并成一個(gè)大的文件了,這個(gè)大的文件里面包含著若干個(gè)小的ShaderCode,相當(dāng)于一個(gè)虛擬文件系統(tǒng)。他的初衷可能想做一個(gè)這方面的優(yōu)化,因?yàn)樗奶摂M文件包是pak,可能是想單獨(dú)為ShaderCode做一個(gè)優(yōu)化。

但是這種優(yōu)化,和我們《龍族幻想》的方式還有點(diǎn)沖突。所以我們在新版里面,我們重新把這個(gè)大的文件分拆成之前的版本,一個(gè)ShaderCode是一個(gè)文件,文件名是它的哈希值。因?yàn)楹芏嗟挠螒蚬径加凶约旱奶摂M文件系統(tǒng),當(dāng)面對UE的這么一個(gè)很大的文件的時(shí)候,其實(shí)是沒辦法使用自己的虛擬文件系統(tǒng)來做這個(gè)熱更新的。現(xiàn)在這個(gè)ShaderCode按照《龍族幻想》的級別應(yīng)該是在四百兆左右的大文件,如果是用UE的方式組成一個(gè)大包,那更新量是很大的。所以我們把它分拆了。

分拆了之后若干個(gè)文件,我們需要使用自己的文件系統(tǒng)。我們改變了哪些ShaderCode文件,就單獨(dú)更新那些文件。然后在這個(gè)FShaderCodeLibrary這個(gè)類,也做了修改。這個(gè)類的話,原來它的實(shí)現(xiàn)方式是你打開一個(gè)文件,首先打開那個(gè)library,那個(gè)library里面是一個(gè)很大的文件,里面再去逐個(gè)地打開讀取ShaderCode。那么我們現(xiàn)在就是去打開單個(gè)ShaderCode文件的時(shí)候,我們就直接打開某一個(gè)文件,然后把里面的文件內(nèi)容讀出來。它的好處就是我們只會(huì)下載單獨(dú)的ShaderCode的更新包。
?


這是改進(jìn)之后的長相。因?yàn)槲募貏e多,有幾萬個(gè)文件,在開發(fā)期的時(shí)候必須要做一個(gè)文件夾的映射。因?yàn)閹兹f個(gè)文件都存在一個(gè)文件夾里的時(shí)候,查看會(huì)卡很久。所以我們通過ShaderCode的文件名哈希出來,把它分?jǐn)偟饺舾蓚€(gè)目錄里去,這樣每個(gè)目錄的文件數(shù)量不是很多。

然后每個(gè)文件都是以它的哈希值作為文件名的,這樣的話,當(dāng)你在運(yùn)行的時(shí)候去加載一個(gè)ShaderCode的時(shí)候,你拿到它的哈希值,直接定位到這個(gè)文件把它讀出來就ok了。然后,這樣的文件通過我們的虛擬文件管理打成一個(gè)大包,我們更改了哪些文件,就只下載那些文件,做到包體的最小化。這就是我們對于材質(zhì)系統(tǒng)所做的一些優(yōu)化。

二、天氣系統(tǒng)
?


下面我講一下天氣系統(tǒng),之前我跟UE同學(xué)溝通,他們也收到了不少同學(xué)反饋說很想知道一些游戲里面的天氣系統(tǒng)是怎么實(shí)現(xiàn)的,所以我覺得講一下這個(gè)還是很有必要的。在《龍族幻想》里我們打算做天氣系統(tǒng)的時(shí)候想到很多方案。開始想的是,我們可以通過改Shader的參數(shù),把雨啊雪啊什么的都放在一個(gè)Shader里面,通過它的參數(shù)來做一個(gè)切換。

最后發(fā)現(xiàn)這樣的話,我們的材質(zhì)編器里面的圖就會(huì)非常多,線畫的非常亂,并且效率非常低。我們后來想,要不就運(yùn)行期改FShader,但改FShader其實(shí)對于每個(gè)模型,比如說當(dāng)前是一個(gè)晴天的材質(zhì),里面對應(yīng)的是晴天的FShader,我們需要把它改成雨,那我們?nèi)フ业竭@個(gè)雨的FShader,把它調(diào)整一下,這也是一個(gè)方案。

但是后來想一想,我們針對于每個(gè)模型去改FShader的這樣一個(gè)架構(gòu),在UE引擎里面其實(shí)是沒有的。改動(dòng)會(huì)比較大,挺麻煩的。直到我們看到了質(zhì)量等級。我們發(fā)現(xiàn)它在全局有一個(gè)功能是設(shè)置質(zhì)量等級的,那我們在想,場景里面很多天氣變化的時(shí)候,其實(shí)有一些模型是不會(huì)受天氣變化影響。比如室內(nèi)的東西,我們不能通過改全局的質(zhì)量等級來切換Shade,我們就改成基于每個(gè)模型的。

所以我們最終想到,我們可以利用材質(zhì)的質(zhì)量等級這么一個(gè)引擎自帶的功能去實(shí)現(xiàn)天氣系統(tǒng)。但是我們還需要改進(jìn),原先的功能不能滿足我們的要求。

然后第二個(gè)方面我介紹一下,就是我們需要采用這個(gè)Material Parameter Collection。因?yàn)槊恳粋€(gè)材質(zhì),它都會(huì)有一些差值。他需要有一些全局的數(shù)據(jù),比如我們現(xiàn)在需要知道他是從晴天開始切換到雨天,從雨天切回晴天,那么這些全局的數(shù)據(jù)和參數(shù),就是取用這個(gè)參數(shù)的集合。

還有就是,我們在材質(zhì)編輯器里面,我們可以通過Quality Switch這個(gè)節(jié)點(diǎn)來做一個(gè)不同的等級效果的LOD,我現(xiàn)在細(xì)化一下。我們決定用質(zhì)量等級來做天氣系統(tǒng)之后呢,我們就開始著手加,因?yàn)樗瓉聿粔?#xff0c;只有低中高最高。我們加了好幾種,有雨和雪,雨和雪最終都加了最低、中、高、最高,很多很多。后來我們覺得這樣不行,我們砍掉了很多,只留下最基礎(chǔ)的一些。


?


所以我們在最差的機(jī)器上沒有天氣的變化,因?yàn)槲覀冎挥衛(wèi)ow,沒有l(wèi)ow rain、low snow。在中等的機(jī)器上和最高級機(jī)器上,我們會(huì)有分別對應(yīng)的雪和雨。比如說我們雪的變化,從medium的質(zhì)量等級,切換到medium snow,然后晴天到雪、從晴天到雨還有反向的切換。

既然做了這種質(zhì)量等級的劃分,我們就不需要把所有天氣做在一個(gè)Shader里面。一個(gè)Shader里,我們只做兩種天氣的過渡,比如晴天到雪天的過渡,我覺得這個(gè)不是特別的復(fù)雜,你只需要考慮兩種天氣。但是如果你要在這個(gè)材質(zhì)里面,還要做從雪到雨的切換的話,那這個(gè)Shader就會(huì)比較復(fù)雜,因?yàn)槟阋龅綗o縫的切換。

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


然后在一些低端機(jī)器上,我們會(huì)砍掉一些不必要的質(zhì)量等級。比如說因?yàn)橘|(zhì)量等級的加載是運(yùn)行期做的,在UMaterial的函數(shù)里面去做的,發(fā)現(xiàn)是比較差的機(jī)器的話,就只保留一種Low或者是Medium。要看實(shí)際的效果。可以寫一個(gè)很大的配置表,這個(gè)機(jī)器是用低還是用高,在運(yùn)行期決定到底是扔掉哪些、保留哪些。
?


再講一下參數(shù)集。這個(gè)參數(shù)集我并不想特別展開每一項(xiàng)參數(shù)的意義。因?yàn)槊恳粋€(gè)游戲,它天氣效果的渲染是非常復(fù)雜的一個(gè)圖表,會(huì)使用不同的參數(shù)。我現(xiàn)在只講一下它使用的思路。

天氣過渡會(huì)使用到到一些全局的東西,比如說晴天到雨天,晴天到雪天過渡的一些權(quán)重,比如說時(shí)間,光線、風(fēng)速,每個(gè)參數(shù)都可以在運(yùn)行期改變。當(dāng)你在做天氣切換的時(shí)候,你需要在游戲邏輯上把一些參數(shù)設(shè)置好,讓它能夠有一個(gè)過渡銜接。比如說我們從晴天到雨天的過渡,當(dāng)前這個(gè)模型所處的是晴天的一個(gè)Shader,這里面的雨權(quán)重就是0。
?


切換的過程是在代碼里瞬間切換的。這個(gè)時(shí)候就需要我們在切換前把這些參數(shù)的集合,比如說雨,權(quán)重設(shè)成0,才不會(huì)突變。設(shè)成0之后,我們在運(yùn)行期里面去update時(shí)逐漸修改這個(gè)雨的權(quán)重值。這個(gè)雨的Shader里面,他自然就會(huì)讀到雨的數(shù)值,雨的貼圖的比重,晴天貼圖的比重都會(huì)下降,漣漪的貼圖就慢慢的呈現(xiàn)出來。其他的效果就會(huì)慢慢地淡入,直到達(dá)到百分之百的穩(wěn)定態(tài)。這個(gè)取決于你們的TA怎么去連線材質(zhì)的圖。
?


這是大概的一個(gè)長相。我們的參數(shù)非常多,有標(biāo)量的參數(shù),還有矢量的參數(shù),我只截了兩個(gè)。天氣材質(zhì)球的連線也是相當(dāng)復(fù)雜。在《龍族幻想》里面還有一些效果,是區(qū)域性的,比如說我在這個(gè)區(qū)域里面,可能是迷霧森林再走到一個(gè)別的區(qū)域里面,我們會(huì)預(yù)先保存很多的預(yù)制鍵,預(yù)制的一些數(shù)據(jù)集。

這些數(shù)據(jù)集就是對剛才那堆參數(shù)預(yù)先設(shè)置好的,比如說一個(gè)充滿黃色霧氣的地方,還有傍晚、早晨。我們存有很多參數(shù)集的文件,當(dāng)你在運(yùn)行期想進(jìn)入某一個(gè)區(qū)域的時(shí)候,通過邏輯把這些值加載進(jìn)來,然后跟當(dāng)前的值通過位置做一個(gè)lerp,取代或者是設(shè)置這個(gè)Shader讀取的那套參數(shù)集。最終的效果就會(huì)發(fā)生變化,就能達(dá)到魔獸世界那種穿過一個(gè)區(qū)域到達(dá)另外一個(gè)區(qū)域的效果。
?


這是一個(gè)簡單的示例,比如說像右邊就是一個(gè)全局的參數(shù)集,它會(huì)有復(fù)雜的連線,這里只是展示一個(gè)用法。用上這些全局的參數(shù)之后,我們在引擎里面去改這些值,整體渲染效果就會(huì)有變化。在我們的Shader圖標(biāo)里面,材質(zhì)圖表里面充斥著大量的、對于全局的參數(shù)集合的應(yīng)用,來達(dá)到這種最終的變化的效果。
?


當(dāng)你在材質(zhì)編輯器里面去連接節(jié)點(diǎn)的時(shí)候,不光是通過一些剛才的那些全局的參數(shù)來達(dá)到效果,還有一個(gè)重要的是通過Quality Switch節(jié)點(diǎn)。在最低精度下面,我可以走這條分支,高精度我走另外一條分支,達(dá)到一個(gè)Shader的優(yōu)化。
?


這就是剛剛說的,基于這個(gè)節(jié)點(diǎn)的Switch。每一個(gè)節(jié)點(diǎn)的Switch最終都會(huì)被編譯成對應(yīng)的ShaderCode,放在它自己的ShaderMap里面。延伸過來之后可能會(huì)有非常復(fù)雜的邏輯,比如說這個(gè)Low,連過來之后可能是一個(gè)非常簡單的邏輯,這個(gè)高的雨、最高的雨連過來,可能它有更多的貼圖、更多的采樣,更多其他的效果,然后輸出到一個(gè)最終的節(jié)點(diǎn)。
?


限于時(shí)間關(guān)系,我給大家簡單介紹一下在渲染優(yōu)化上做的一些事。UE原生的FShader沒有卸載的機(jī)制,加載進(jìn)來之后就永遠(yuǎn)留在那個(gè)地方,我覺得其實(shí)是一種效果浪費(fèi)。因?yàn)榭赡蹻PS游戲的Shader類型不是很多,當(dāng)我們做《龍族幻想》的時(shí)候,對渲染效果要求很高。再加上我們加了很多質(zhì)量等級去實(shí)現(xiàn)天氣效果,FShader的量就會(huì)很大,這個(gè)時(shí)候如果不卸載FShader的話,就會(huì)帶來很大的內(nèi)存開銷,因此我們做了FShader的釋放。

這個(gè)原理其實(shí)也非常簡單,一個(gè)FShader創(chuàng)建出來會(huì)使用引用計(jì)數(shù)記錄在多少個(gè)地方使用,在切場景的時(shí)候我們會(huì)統(tǒng)一過一遍,我們會(huì)檢查一遍這些所有的FShader。如果它的引用計(jì)數(shù)為零了,我們就會(huì)把它釋放掉,需要放到渲染線程,或者是RHI線程里面去釋放這個(gè)Shader。在不切場景的時(shí)候,我們也會(huì)時(shí)不時(shí)做一下,但是我們在切場景的時(shí)候,在Loading條走的時(shí)候,肯定要做一遍釋放檢查。因?yàn)閘oading的時(shí)候卡一下的話也沒什么關(guān)系。

然后就是OpenGL的一個(gè)program binary data cache。這個(gè)是在OpenGL里面,安卓平臺下面,當(dāng)你設(shè)置一個(gè)shader的時(shí)候,你需要Link。Link有點(diǎn)卡,當(dāng)然如果Fps游戲FShader不多的時(shí)候,不會(huì)感覺特別明顯。但是《龍族幻想》有很多Shader,并且不同的模型可能Shader會(huì)不一樣,他的這種卡頓就會(huì)變成一個(gè)問題。
?


當(dāng)Link完了之后,生成一個(gè)program,我們會(huì)拿到這個(gè)data,把它存下來,存在一個(gè)文件里。我做了一個(gè)虛擬的文件系統(tǒng),這個(gè)虛擬文件的key就是那段program的data。

存下來以后,當(dāng)?shù)谝淮芜\(yùn)行游戲時(shí)這個(gè)文件是空的,每次都會(huì)Link,然后存進(jìn)去。當(dāng)?shù)诙芜\(yùn)行的時(shí)候,情況就會(huì)變好了。以前曾經(jīng)Link過的,我直接在文件里找到,通過這個(gè)Program加載進(jìn)來,也不需要去設(shè)置Shader,也不需要Link,直接就可以用了。

第三個(gè)是多個(gè)PSO cache file 。UE4自己有一個(gè)功能是對PSO的緩存,原來是叫Shader cache,后來改成pipelinecache。

他只有一個(gè)文件,我們改進(jìn)了這個(gè)功能。在錄的時(shí)候,我們可以錄很多個(gè)文件。比如我們在打Boss戰(zhàn)的時(shí)候,那個(gè)Boss以前從沒出來過。他一出來,不管你是Link還是怎么樣,反正他會(huì)卡頓。因?yàn)榫退闶悄鉒ink了,他往顯卡送的那一刻,往鏡頭送那一刻,他也會(huì)有一定的時(shí)間開銷。然后當(dāng)那個(gè)Boss從來沒出現(xiàn)過,一出來就卡一下,這個(gè)效果不太好。

還有就是我們游戲運(yùn)行Loading完了之后,我們需要播一個(gè)CG,那個(gè)CG有很多也是游戲不太用到的資源,它也會(huì)卡一下。我們還是想用引擎的PSO功能,記錄的功能,然后把它預(yù)熱一下。但是一個(gè)不夠,尤其是出現(xiàn)怪物的時(shí)候。然后我們就做了一個(gè)錄不同的PSO的cache。PSO cache需要錄渲染所有的參數(shù),Shader,各種參數(shù)都錄下來。錄下來之后當(dāng)你需要播這些、需要畫這些文件的時(shí)候,它會(huì)在后臺給你把這些東西跑一遍。這樣的話,當(dāng)你真正渲染模型的時(shí)候就不會(huì)卡頓,我們做了多個(gè)這樣的文件。比如說,在這個(gè)Loading條結(jié)束的時(shí)候,我們需要播CG,那在Loading條結(jié)束的時(shí)候,就加載這個(gè)場景所對應(yīng)的記錄好的文件。當(dāng)這個(gè)CG播放的時(shí)候就會(huì)非常的平滑,沒有一絲的卡頓。對于boss也是這樣,快到播boss的時(shí)候,我們也在后臺把這個(gè)cache文件加載進(jìn)來,做一下這樣的預(yù)熱,就會(huì)達(dá)到非常好的平滑效果。今天的分享大概就是這些,非常感謝。

總結(jié)

以上是生活随笔為你收集整理的祖龙娱乐王远明:如何用UE4做出3A级材质和天气系统?的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。