Cocos Creator 3.6 新特性詳解 2/3:渲染篇

這段是負(fù)責(zé)承上啟下的
上一篇文章,麒麟子和各位一起體驗了 Cocos Creator 3.6 版本中那些令人激動的編輯器新特性。不管從編輯器外觀,還是從工作效率上都帶來了不錯的提升。
相信有不少朋友和麒麟子一樣,每次的版本更新,都會分外關(guān)注引擎渲染相關(guān)的特性,有沒有新增渲染技術(shù),有沒有畫質(zhì)方面的提升。
今天我們細(xì)數(shù)一下 3.6 版本中那些與渲染特性相關(guān)的內(nèi)容。
一切都是為了顏值
3.6 中較為硬核的新渲染特性:
GGX 環(huán)境反射卷積(GGX Convolution)
級聯(lián)陰影(CSM, Cascaded Shadow Maps)
粒子噪聲模塊(Noise Module)
動態(tài)模型(Dynamic Mesh)
各向異性光照(Anisotropic Lighting)
表面著色器(Surface Shader)
如果朋友們在之前的版本中遇上過:
粗糙表面油油的?
陰影效果糊糊的?
粒子軌跡傻傻的?
不用擔(dān)心,這些問題在 3.6 中都將不復(fù)存在。
如果朋友們在之前的版本中問過:
如何使用代碼控制模型頂點運動?
如何制作出如絲般的材質(zhì)效果?
如何更簡單的自定義Shader?
不用著急,這些問題在 3.6 中都有有解決方案。
鋪墊了這么多,就是想說:還不趕緊到碗里來!。
GGX 環(huán)境反射卷積圖
這是麒麟子一直想要的特性,它可以極大的提升非光滑表面的真實感,請看下圖:

從上面的對比圖中,我們可以明顯感知到有卷積(GGX Convolution)和無卷積(Mipmaps)的效果差異。
那什么是 GGX 環(huán)境反射卷積圖呢?
它是指采用 GGX 算法,使用卷積方式生成的用于環(huán)境反射計算的 Cubemap 貼圖。
這東西性能如何?是我這800塊的手機能跑的嗎?
由于卷積圖是預(yù)生成的,朋友們完全不用擔(dān)心性能問題,在同樣需要環(huán)境反射的情況下,使用卷積圖和使用 Mipmap 圖性能是一致的,只需在編輯器中烘焙好即可。
既然它這么棒,我們?nèi)绾卧?Cocos Creator 中生成和使用 GGX 環(huán)境反射卷積圖呢?
大家請看下圖:

這是 Cocos Creator 3.6 中最新的 Skybox 面板,我們重點關(guān)注:
環(huán)境光照類型(Env Lighting Type)
環(huán)境反射卷積圖(Reflection Convolution)烘焙(Bake)
麒麟小貼士:PBR材質(zhì)在處理環(huán)境光照時,會將環(huán)境貼圖視作一個巨大的環(huán)繞光源,貼圖上的每一個像素都各自代表了一個方向上的光亮度。這個處理方式被稱為:IBL(Image Based Lighting),而IBL中主要由漫反射和環(huán)境反射兩個部分的計算構(gòu)成。
環(huán)境光照類型
引擎環(huán)境光照提供了三種組合選擇:
HEMISPHERE_DIFFUSE(半球光照)
AUTOGEN_HEMISPHERE_DIFFUSE_WITH_REFLECTION(自動計算半球光照漫反射+環(huán)境反射)
DIFFUSEMAP_WITH_REFLECTION(環(huán)境輻照度卷積+環(huán)境反射)
選擇?XXX_WITH_REFLECTION?表示我們希望環(huán)境反射,反之則沒有環(huán)境反射。 如果把同樣的測試場景切換到?HEMISPHERE_DIFFUSE?,我們只能得到下面這樣的效果:

HEMISPHERE與AUTOGEN_HEMISPHERE的區(qū)別是,AUTOGEN_HEMIPSHERE會根據(jù)環(huán)境貼圖自動計算天光和地光。
環(huán)境反射卷積圖烘焙
環(huán)境反射卷積圖后面的烘焙(Bake)按鈕,用于生成環(huán)境反射卷積圖,當(dāng)烘焙成功,IBL運算會采用生成好的環(huán)境反射卷積圖。
如果不想使用環(huán)境反射卷積圖,可點擊移除按鈕,IBL運算會采用環(huán)境反射貼圖的 Mipmaps 參與運算。

能詳細(xì)說說環(huán)境反射卷積嗎?
對于想要了解它背后原理的朋友,我們先來看看兩個名詞解釋:
GGX:一種微表面分布函數(shù),用GGX算法渲染的材質(zhì),過渡會更加自然,分布曲線更加擬合真實物體,最主要的是可以還原一部分真實BRDF的高光拖尾,對比效果如下圖所示:

環(huán)境反射卷積圖:通過適合的算法,預(yù)先生成對應(yīng)粗糙度的反射圖并存到Cubemap的各個Mipmap等級中,如下圖所示:


可以明顯的看到,Cubemap 中的 Mipmaps 每一級都比上一級更模糊,這是普通的 Mipmap 生成算法無法達到的效果。
由于本文重點關(guān)注它能帶來什么效果,因此不會解釋是什么GGX,什么是卷積。有興趣了解這方面的朋友們可以自行搜索,或者私信交流。
高質(zhì)量的動態(tài)陰影(CSM)
Cocos Creator 從 3.0 版本開始就支持了ShadowMap,這是一個在實時圖形引擎中被廣泛采用的陰影技術(shù)。
但開發(fā)者們在使用的時候,經(jīng)常遇上下面兩個問題困惑:
如果 Shadow Distance 設(shè)小了,離攝像機遠(yuǎn)一點的地方就沒了陰影,如下圖所示:

如果 Shadow Distance 設(shè)大了,陰影又會特別模糊,如下圖所示:

以上問題,開啟CSM就好啦,不管從遠(yuǎn)處看,還是從近處看,效果都妥妥的,如下圖所示:

那什么是 CSM 呢?
CSM 是 Cascaded Shadow Maps 的縮寫,中文一般叫此術(shù)語翻譯為:級聯(lián)陰影。
為什么叫級聯(lián)呢,請看下圖:

CSM 中,將視錐體內(nèi)的場景從近到遠(yuǎn)分為了四個層級(在 Cocos Creator 3.6中,默認(rèn)的 CSM 層級為 4 層),每一個層級對應(yīng)一張 ShadowMap,在陰影采樣階段再根據(jù)物體位置計算出使用哪一張 ShadowMap 最適合。
這樣的操作,使得近處的 ShadowMap 覆蓋較少的內(nèi)容,從而確保近處的陰影清晰,而越遠(yuǎn)的 ShadowMap 則需要覆蓋較多的內(nèi)容,從而確保較遠(yuǎn)處的陰影可見。
CSM 的 DrawCall 開銷如何呢?
雖然 4 層 CSM 需要繪制四次場景,但由于引擎的裁剪機制和陰影 Pass 繪制時的優(yōu)化,額外的 DrawCall 開銷不超過普通 ShadowMap 的 40%。
CSM 的內(nèi)存開銷如何呢?
在 Cocos Creator 的場景面板中,ShadowMap Size有四個選項:
Low_256x256
Medium_512x512
High_1024x1024
Ultra_2048x2048
CSM 4 級共享一張 ShadowMap,且每一級的開銷占ShadowMap Size的1/4,比如選擇了 Ultra_2048x2048,則每一級的 ShadowMap 占 1024x1024 大小。
簡單說來就是,開啟 CSM 和不開 CSM 的 ShadowMap 內(nèi)存開銷是一樣的。
CSM 區(qū)域可視化調(diào)試
在上一篇關(guān)于編輯器的介紹中,我們提到了渲染調(diào)試功能,通過此功能,可以查看 CSM 各級所占的比例,如下圖所示:

這個功能可以使我們在調(diào)節(jié) Shadow Distance 和 Shadow Invisble Occlusion Range 的時候,直觀地看到各級變化,根據(jù)需求精確定位各級位置。
CSM 四級太多了,可以少些嗎?
目前只開放了4級,后續(xù)規(guī)劃中,可能會開放層級設(shè)置和層級的lambda分布設(shè)置,從而實現(xiàn)更精準(zhǔn)的層級分布和性能節(jié)省。
粒子噪聲模塊(Noise Module)
Cocos Creator 3.6 為粒子系統(tǒng)新增了噪聲模塊(Noise Module),可以很方便地制作出一些粒子隨機飄蕩的效果,如下所示:

目前粒子噪聲模塊有以下參數(shù):
Noise Preview:預(yù)覽目前采樣所使用的噪點圖
Stength X:粒子在X方向的noise強度
Stength Y:粒子在Y方向的noise強度
Stength Z:粒子Z方向的noise強度
Noise Speed X:噪點圖在X方向的滾動變化速度
Noise Speed Y:噪點圖在Y方向的滾動變化速度
Noise Speed Z:噪點圖在Z方向的滾動變化速度
Noise Frequency:生成噪點圖所使用的噪點頻率,數(shù)值越大,噪點越密集
Octaves:生成噪點圖所使用的算法層數(shù),數(shù)值越大變化越多,計算量也越大
Octave Multiplier:對新層數(shù)的強度乘數(shù)
Octave Scale:對新層數(shù)的頻率乘數(shù)
那個噪聲預(yù)覽圖(Noise Preview)是拿來干什么用的呢?
麒麟子猜到大家會有這個疑問,所以提前問了引擎組相關(guān)人員這個問題。由于他不愿意透露自己的身份,所以在這里我們就用安迪來替代。
安迪說,設(shè)計這個主要有三個原因:
1.目前所有的噪聲控制參數(shù)會生成到一張 Noise Map中,并傳遞給粒子系統(tǒng),所以希望把這個圖的樣子呈現(xiàn)給開發(fā)者。
2.有一些參數(shù)在拖動的時候,很難察覺,但會在 Noise Map 中表現(xiàn)出來,能夠有效感知參數(shù)變化
3.有助于粒子系統(tǒng)開發(fā)過程中的迭代調(diào)試,避免工程師嘴硬
看這毫無規(guī)律、翩翩起舞的蝴蝶,只需要一個粒子發(fā)射器就搞定,是不是很棒?

除此之外,我們還可以利用噪聲模塊為粒子效果添加細(xì)節(jié),如下所示:


動態(tài)模型(Dynamic Mesh)
如果你想實現(xiàn)一條彎彎扭扭,由你掌控的3D小蛇。
如果你想在3D空間中生成根據(jù)指示方向動態(tài)生成的提示弧線。
如果你想發(fā)揮你才華橫溢的優(yōu)勢,自己寫一套3D特效、云霧、天氣系統(tǒng)。
你最需要的特性,就是動態(tài)更新模型數(shù)據(jù)。
代碼創(chuàng)建靜態(tài)模型
在 3.6 版本之前,Cocos Creator 引擎只提供了一個用于創(chuàng)建靜態(tài)模型的函數(shù):?utils.MeshUtils.createMesh
?函數(shù)。 但它每次都會構(gòu)建模型相關(guān)對象,只適合在更新頻率不高的情況下使用。
代碼創(chuàng)建動態(tài)模型
3.6 提供了專門用于處理動態(tài)模型的?utils.MeshUtils.createDynamicMesh
?函數(shù),如下圖所示:

三步搞定動態(tài)模型
1、options 定義容量

動態(tài)模型創(chuàng)建時,需要通過 options 參數(shù)指定 3 個數(shù)據(jù):
maxSubMeshes:子網(wǎng)格數(shù)量
maxSubMeshVertiecs:每一個子網(wǎng)格最大頂點數(shù)
maxSubMeshIndices:每一個子網(wǎng)格最大索引數(shù)
2、調(diào)用 createDynamicMesh 創(chuàng)建動態(tài)模型

大部分情況下,我們都只有一個 SubMesh,那在創(chuàng)建的時候順便填充數(shù)據(jù)就是最方便的。這個函數(shù)的原型像這樣就夠了:?createDynamicMesh(geometry,options):Mesh
。
但設(shè)計者覺得這樣不夠靈活,所以添加了?primitiveIndex
。這個參數(shù)用于指定 geometry 屬于哪個 SubMesh。
注意:記得調(diào)用?
meshRender.onGeometryChanged
?函數(shù)
3、調(diào)用 updateSubMesh 更新動態(tài)模型
函數(shù)原型為:updateSubMesh(primitiveIndex, geometry): void
用途是:用 geometry 的數(shù)據(jù)更新 primitiveIndex 指定的 SubMesh。
示例如下:

注意:更新完數(shù)據(jù)后,別忘了調(diào)用?
meshRender.onGeometryChanged
?函數(shù)
別走:關(guān)于動態(tài)模型頂點數(shù)量
回顧一下 options 中的兩個屬性:
maxSubMeshVertiecs:每一個子網(wǎng)格最大頂點數(shù)
maxSubMeshIndices:每一個子網(wǎng)格最大索引數(shù)
這里之所以叫?“最大”?,是因為這兩個值主要決定了分配的模型數(shù)據(jù)大小,而決定真正使用多少空間的是 geometry 數(shù)據(jù), 只要確保 geometry 數(shù)據(jù)的頂點和索引數(shù)據(jù)不超過 options 指定的值,均可正常工作。
對于頂點和索引固定的情況,建議?maxSubMeshVertiecs
?和?maxSubMeshIndices
?的值與?geometry
?數(shù)據(jù)保持一致,以避免不必要的內(nèi)存浪費。
但對于頂點和索引會更改的情況,則需要確保這兩個最大值總是能滿足?geometry
?的需要。
舉個簡單的例子:
假如我們要實現(xiàn)一個由?10x10x10?個方塊組成的物體,當(dāng)用戶點中某個方塊的時候,方塊會消失。
我們創(chuàng)建一個只有一個 SubMesh 的動態(tài)模型,maxSubMeshVertiecs
?為 8000,maxSubMeshIndices
?為 36000。
當(dāng)點中的方塊消失時,我們只需要移除?geometry
?中對應(yīng)的頂點數(shù)據(jù),并調(diào)用?updateSubMesh
?刷新模型即可。
類似的情況還有自制粒子、植被系統(tǒng)、自定義特效等場合。
官方示例
引擎組提供了一個龍的示例,點擊 Update 按鈕,龍會從無到有顯示出來,如下所示:

有需要研究和學(xué)習(xí)的朋友,可以查看引擎官方Github倉庫。
地址:https://github.com/cocos/cocos-test-projects/tree/v3.6?5
場景:assets/cases/dynamic-mesh
各向異性光照(Anisotropic Lighting)
在自然界中,有一些物體的表面,有大量細(xì)小齊整的溝槽。普通的圖形學(xué)光照模型無法很好的重現(xiàn)這類物體表面的光照效果,于是產(chǎn)生了專門模擬此類光照效果的技術(shù):各向異性光照。
常見的難以表達的表面:

雖然實現(xiàn)原理很復(fù)雜,但在 Cocos Creator 3.6 中使用各向異性光照只需下面三步:
1、在 Cocos Creator 中新建一個材質(zhì)
2、將材質(zhì)的?
Effect
?切換為?surfaces/standard
3、開啟?
IS ANISOTROPY
?宏

相關(guān)的參數(shù)解釋:
USE ANISOTROPY ROTATION MAP:是否啟用各向異性旋轉(zhuǎn)圖
Anisotropy Shape:各向異性形狀, 0為完全同性,1為完全異性。 這個參數(shù)也可以理解為強度(Intensity),但由于它會決定此效果的最終形狀,所以用了 Shape 這個詞。
Anisotropy Rotation:控制各向異性的條紋方向
Anisotropy Rotation Map:各向異性旋轉(zhuǎn)圖,用于精確控制條紋方向,僅在
USE ANISOTROPY ROTATION MAP
宏開啟時有效
Cocos Creator 3.6 中渲染的效果如下:

表面著色器(Surface Shader)
什么是 Surface Shader?
Surface Shader 并不是一個新的 Shader 品種,它是引擎為了方便 Shader 編寫提供的流程和框架。它對底層的 Vertex/Fragment Shader 做了封裝,省去了一些重復(fù)代碼編寫的工作量,從而降低了 Shader 編寫復(fù)雜度,提升 Shader 編寫效率和兼容性。
為什么需要 Surface Shader?
隨著 Cocos Creator 的 3D 特性越來越好,工作流越來越完善。越來越多的 3D 開發(fā)者,美術(shù),TA,圖形學(xué)愛好者加入到了 Cocos 生態(tài)行列。
社區(qū) KOL 們,以及 Cocos 官方布道師們也輸出了不少關(guān)于?Cocos 3D 編程,Cocos Shader?學(xué)習(xí)的教程。
關(guān)于這方面的問題,麒麟子收到的反饋集中在以下幾個方面:
Cocos Effect 內(nèi)容太多了,新手看著就打退堂鼓
有一些 PBR 運算相關(guān)參數(shù)和過程暴露得不夠多,需要去改 trunks 里的內(nèi)置 Shader 文件才能實現(xiàn)想要的效果
surf 函數(shù)里暴露了太多不需要改動的內(nèi)容,比如法線、roughness、metallic 等基礎(chǔ)運算。
沒有模塊化,實現(xiàn)功能更像是在默認(rèn)的Shader上打補丁,導(dǎo)致新版本兼容和跨項目復(fù)用困難
Surface Shader 流程的出現(xiàn),極大的改善了以上情況,使自定義 Shader 更便捷。由于寫法上更加簡潔,使得Shader編寫更容易上手,兼容性更高。
到底有多簡潔?
如下圖所示,一個完整的?standard-fs
?滿打滿算 10 行。

一個完整的 Vertex Shader 或者 Fragment Shader 需要包含宏定義、公共文件、UBO定義、光照模型、Shader Stage、輸出目標(biāo)六個部分。開發(fā)者可以根據(jù)需要,分別對這六個部分做自定義,不需要自定義的部分保持默認(rèn)即可。
如何自定義 Surface Shader?
官方文檔中,有完整的如何編寫 Surface Shader 的步驟,其中最重要的一個就是宏定義機制。
在?Internal/chunks/surfaces/default-functions
?目錄下有?common-vs
、standard-fs
、toon-fs
?三個文件,其中定義了所有可以用宏替換的內(nèi)置函數(shù)。如下圖所示:

#ifndef?CC_SURFACES_XXXX的意思是指,假如未定義?CC_SURFACES_XXXX,則使用下面的函數(shù)。
以較為簡單的邊緣光(RimLight)為例,只需要在自己的 Surface Shader 的?CCProgram surface-fragment %{ ... }%
?片段中,定義?#define CC_SURFACES_FRAGMENT_MODIFY_BASECOLOR_AND_TRANSPARENCY
?宏,并實現(xiàn)?vec4 SurfacesFragmentModifyBaseColorAndTransparency
?函數(shù)即可。
以上就是關(guān)于Surface Shader編寫中,麒麟子認(rèn)為最核心的機制部分。
由于篇幅有限,其余內(nèi)容就不展開細(xì)說,大家可結(jié)合官方文檔和?surfaces/standard.effect
?研究。
補充說明
1、編輯器中的渲染調(diào)試(Rendering Debug View)僅支持使用 Surface Shader 的材質(zhì)。
2、目前各向異性光照僅在 Surface Shader 中有選項。
3、引擎規(guī)劃中,后期的可視化 Shader 編輯器(Shader Graph)生成的 Shader 代碼為 Surface Shader。
4、為了遷移性、兼容性和可讀性,麒麟子建議在新項目中優(yōu)先使用 Surface Shader
簡單總結(jié)一下
此次的渲染相關(guān)的更新主要集中在環(huán)境反射卷積、級聯(lián)陰影、粒子噪聲模塊、動態(tài)模型?幾個部分,當(dāng)然也有許多渲染相關(guān)的提升由于太過細(xì)微,就沒有在本文中出現(xiàn),但并不意味著它們不重要。
這些細(xì)節(jié)部分的價值挖掘就交給朋友們自行解決咯。還等什么,打開 Cocos Creator 3.6,行動起來!
下一篇文章,麒麟子將為大家呈現(xiàn) 3.6 版本中,關(guān)于性能部分的提升,敬請期待!