引言:
GameByro作為一款次世代引擎,使用了復(fù)雜的材質(zhì)系統(tǒng),用來(lái)滿(mǎn)足各種各樣的需求。材質(zhì)代表了物體受到光照后所呈現(xiàn)出的質(zhì)感,而這種質(zhì)感在計(jì)算機(jī)圖形學(xué)中需要著色代碼來(lái)完成,所以當(dāng)前流行的圖形引擎設(shè)計(jì)是使用被渲染對(duì)象的材質(zhì)與shader相關(guān)聯(lián),GameByro也不例外。GameByro的材質(zhì)系統(tǒng)可以通過(guò)shade tree生成shader程序,增強(qiáng)了應(yīng)用程序?qū)訉?duì)可編程渲染管線(xiàn)的控制能力。
渲染架構(gòu)概覽:
在GameByro中,對(duì)象表面的色彩、紋理、光滑度、透明度、反射率、折射率、發(fā)光度等可視屬性與傳統(tǒng)的材質(zhì)系統(tǒng)分離,獨(dú)立的成為了對(duì)象的渲染屬性(NiProperty),而材質(zhì)(NiMaterial)僅用來(lái)對(duì)著色程序的封裝,這樣就實(shí)現(xiàn)了渲染數(shù)據(jù)和渲染方法的分離,降低了耦合性。如上所說(shuō)的這些可視屬性在Gamebyro中會(huì)封裝成一個(gè)屬性對(duì)象,在應(yīng)用程序中如果對(duì)對(duì)象掛載這個(gè)屬性對(duì)象,在GPU程序中就可以訪(fǎng)問(wèn)這個(gè)屬性對(duì)象的值。渲染屬性對(duì)象可以在創(chuàng)建時(shí)指定其類(lèi)型,如紋理、浮點(diǎn)、矩陣、向量或數(shù)組,此外一些全局性的對(duì)象也可以通過(guò)在Shader中用語(yǔ)意聲明為全局object對(duì)象,如燈光和攝影機(jī)等,這樣就可以以同樣的方式來(lái)訪(fǎng)問(wèn)這些對(duì)象上的屬性。
GameByro每一幀的渲染(NiRenderFrame)劃分為多個(gè)步驟(NiRenderStep),每個(gè)步驟又包含很多個(gè)批(NiRenderClick),NiRenderFrame封裝了上層對(duì)渲染系統(tǒng)調(diào)用的接口,而NiRenderClick則代表了圖形硬件的一次繪制操作(對(duì)渲染隊(duì)列中所有的對(duì)象的頂點(diǎn)緩存調(diào)用DrawPrimitive),當(dāng)應(yīng)用程序調(diào)用NiRenderFrame的Display接口時(shí), NiRenderFrame會(huì)依次調(diào)用每一個(gè)NiRenderStep的Render()接口,NiRenderStep就會(huì)執(zhí)行所有的NiRenderClick操作。
對(duì)于每個(gè)NiRenderClick來(lái)說(shuō),首先要設(shè)置視口和渲染目標(biāo),也就是渲染數(shù)據(jù)流的入口和出口。視口建立以后就可以通過(guò)關(guān)聯(lián)的攝影機(jī)對(duì)場(chǎng)景圖中的對(duì)象進(jìn)行裁剪(默認(rèn)的有視口裁剪和遮擋裁剪,此外還可以通過(guò)回調(diào)函數(shù)加入自己的裁剪方式),將未被裁剪的對(duì)象放入渲染隊(duì)列。然后Gambyro會(huì)根據(jù)材質(zhì)來(lái)對(duì)渲染隊(duì)列中的對(duì)象進(jìn)行排序,讓材質(zhì)相同的對(duì)象處于相鄰位置,這樣可以減少切換shader的開(kāi)銷(xiāo)。
如圖所示為幀渲染系統(tǒng)的結(jié)構(gòu)圖(簡(jiǎn)化版)
材質(zhì)系統(tǒng):
GameByro中的材質(zhì)代表渲染對(duì)象所采用的方法。前面說(shuō)過(guò)。紋理屬性包含了著色所需的原料,那么材質(zhì)就指定了對(duì)這些原料的加工方法。基于當(dāng)前可編程渲染管線(xiàn)設(shè)計(jì),材質(zhì)就成為連接對(duì)象與GPU程序的中間層,應(yīng)用程序可以通過(guò)材質(zhì)將shader應(yīng)用于幾何體。
NiMaterial類(lèi)是所有材質(zhì)的基類(lèi),這個(gè)類(lèi)通過(guò)一個(gè)Map來(lái)保存當(dāng)前應(yīng)用程序中所有NiMaterial的指針,當(dāng)然這個(gè)Map是靜態(tài)也就是說(shuō)相當(dāng)于全局變量,通過(guò)static NiMaterial* NiMaterial:: GetMaterial(const NiFixedString& kName)接口對(duì)這個(gè)全局的Map進(jìn)行訪(fǎng)問(wèn)。也就是說(shuō),當(dāng)前環(huán)境中所有的NiMaterial對(duì)象是通過(guò)NiMaterial類(lèi)來(lái)管理的。此外NiMaterial類(lèi)還通過(guò)靜態(tài)成員變量保存了一個(gè)工作路徑(即shader文件路徑),通過(guò)這個(gè)路徑加載shader文件。NiMaterial就像是一個(gè)中介,全權(quán)代理對(duì)對(duì)象的渲染工作。用戶(hù)可以通過(guò)重載NiMaterial來(lái)實(shí)現(xiàn)自己的渲染機(jī)制。每個(gè)NiMaterial都是全局性的,可以作用于多個(gè)甚至是所有的渲染對(duì)象,但一個(gè)渲染對(duì)象也可以擁有多個(gè)NiMaterial,但只能有一個(gè)處于激活狀態(tài)的NiMaterial。
NiMaterial的派生類(lèi)NiFragmentMaterial提供了對(duì)可編程渲染管線(xiàn)完整的控制機(jī)制,內(nèi)部保存了NiShader的哈希表、一個(gè)NiGPUProgramCache數(shù)組。并且NiFragmentMaterial會(huì)生成一個(gè)用來(lái)編譯GPU程序的shade tree(后面會(huì)有解釋?zhuān)_@樣的話(huà),每個(gè)NiFragmentMaterial可以對(duì)應(yīng)多個(gè)shader程序,這樣就提供了一種機(jī)制,在運(yùn)行時(shí)根據(jù)不同的運(yùn)行環(huán)境和渲染對(duì)象不同的狀態(tài),來(lái)選擇合適的shader程序。在NiRenderClick依次渲染可見(jiàn)集中的每個(gè)對(duì)象時(shí),首先會(huì)判斷其是否需要被渲染的標(biāo)記(flag),如果需要被渲染,則使用NiMaterial::IsShaderCurrent接口判斷當(dāng)前shader(上一次渲染所使用的shader)是否有效,所謂有效就是仍然存在并且可以應(yīng)用于本次的渲染對(duì)象,如果無(wú)效,則會(huì)調(diào)用NiMaterial::GetCurrentShader獲得shader,用于本次渲染。NiMaterial::GetCurrentShader會(huì)根據(jù)渲染對(duì)象的屬性和當(dāng)前環(huán)境硬件條件來(lái)選擇合適的shader程序。當(dāng)然,可以通過(guò)重載IsShaderCurrent和GetCurrentShader接口來(lái)指定自己的有效性判斷規(guī)則和如何選擇shader程序的方案。 NiFragmentMaterial提供了一套搭建shade tree的框架,用戶(hù)可以通過(guò)重載來(lái)搭建自己的shade tree,當(dāng)然,如果不想通過(guò)shade tree的形式生成shader程序也可以,使用NiSingleShaderMaterial可以從文件生成shader程序。
Shade Tree:
什么是shade tree呢?我們通常編寫(xiě)的shader代碼是線(xiàn)性執(zhí)行的,即每個(gè)pass流程執(zhí)行的是文本上定義好的shader流程,每一段shader功能模塊是按一定順序依次執(zhí)行的。如果需要修改流程中的某一部分就需要更改相關(guān)的shader代碼并重新編譯。而shade tree將shader代碼以樹(shù)形結(jié)構(gòu)組織起來(lái),每一個(gè)shader代碼塊(一般是一個(gè)函數(shù))都會(huì)被編譯成一個(gè)節(jié)點(diǎn),通過(guò)定義輸入變量和輸出變量來(lái)提供數(shù)據(jù)流的入口和出口,這些節(jié)點(diǎn)的插入和刪除可以通過(guò)應(yīng)用程序來(lái)控制,從而靈活的控制整個(gè)渲染過(guò)程。這樣shader程序中的一些核心模塊可以由美術(shù)通過(guò)工具生成,然后插入到shade tree中,只要輸入和輸出的接口不變,就無(wú)須修改其他代碼,從而降低了美術(shù)開(kāi)發(fā)shader的門(mén)檻。
GameByro通過(guò)以下幾個(gè)類(lèi)搭建shade tree:
l NiMaterialConfigurator:shade tree被封裝在這個(gè)對(duì)象中,Uniform constants被封裝在NiMaterialResource中,而NiMaterialNode封裝了相關(guān)的shader代碼,所有的資源和節(jié)點(diǎn)通過(guò)NiMaterialResourceBinding連接起來(lái)。當(dāng)所有的連接都確立以后,NiMaterialConfigurator會(huì)調(diào)用Evaluate接口生成GPU程序和一個(gè)輸入U(xiǎn)niform資源的集合。
l NiMaterialFragmentNodes:這個(gè)類(lèi)包含了一個(gè)shader代碼片段的集合,這些代碼片段為不同的平臺(tái)和編程語(yǔ)言所編寫(xiě)。這就為shader程序員提供了更大的靈活性,用以控制他們的代碼在不同平臺(tái)和圖形硬件上的表現(xiàn)。例如:在高端平臺(tái)可以采用高級(jí)的shader model提供更好的效果,而在低端平臺(tái)上可以關(guān)閉一些特效來(lái)加快速度。
l NiMaterialNodeLibraries:這個(gè)類(lèi)是一個(gè)NiMaterialNode的集合,其實(shí)也就是一個(gè)shader庫(kù)。它允許shade tree節(jié)點(diǎn)完全基于數(shù)據(jù)驅(qū)動(dòng)。shader庫(kù)的生成可以通過(guò)兩種方式,解析XML文件或用XML文件生成C++代碼。GameByro提供了相關(guān)的解析器和代碼生成器。
l NiMaterialResources:shade tree中的Uniform constants,支持多種數(shù)據(jù)類(lèi)型,包括Constant、Predefined、Attribute、Global、Object。
固定管線(xiàn)的渲染:
GameByro支持固定管線(xiàn)的渲染,其紋理混合過(guò)程如下。
固定管線(xiàn)的著色處理流程
上圖很清楚的顯示出了每個(gè)stage的操作,平行的表示兩張紋理的采樣是同時(shí)進(jìn)行的,特定情況下右邊的紋理可能被忽略。
大部分情況下,應(yīng)用程序不會(huì)使用上面所有的stage,開(kāi)啟或者關(guān)閉那個(gè)stage可以由應(yīng)用程序來(lái)指定。
以下為多重采樣的原理圖:
固定管線(xiàn)的紋理多重采樣
缺省的著色處理流程:
GameByro提供了一個(gè)默認(rèn)的著色處理流程,封裝在NiMaterial的派生類(lèi)NiStandardMaterial中,這個(gè)類(lèi)執(zhí)行類(lèi)似于固定管線(xiàn)的流程,在不同階段將紋理采樣、并將采樣到的數(shù)據(jù)混合到最終的結(jié)果中去。
GameByro默認(rèn)的材質(zhì)系統(tǒng)的特性如下:
- Skinned and unskinned transformations. Skinned transformations can support up to 30 bones per draw call.
- Vertex colors
- Base maps
- Normal maps
- Parallax maps
- Dark maps
- Detail maps
- Bump environment maps
- Gloss maps
- Glow maps
- Decal maps (up to 3)
- Cubic and spherical environment maps
- Point/Spot/Directional/Ambient lights contributing to the diffuse, specular, and ambient color. Up to 8 total lights. Per-pixel or per-vertex.
- Projected light maps. Clipped or unclipped. (Up to 3)
- Projected shadow maps. Clipped or unclipped. (Up to 3)
- Texture transforms per map.
- Per-vertex fog
下圖顯示為不同的紋理、燈光、材質(zhì)屬性的組合過(guò)程,不過(guò)需要注意的是,視差貼圖和凹凸貼圖屬于特殊的情況,它們僅僅影響到紋理采樣的UV坐標(biāo),而并非直接對(duì)最后的顏色值產(chǎn)生貢獻(xiàn)。視差貼圖會(huì)改變所有貼圖采樣的UV坐標(biāo),而凹凸貼圖僅對(duì)環(huán)境貼圖的UV產(chǎn)生影響。
以上流程完全由shade tree構(gòu)建,NiStandardMaterial提供了大量的函數(shù)接口用于對(duì)每個(gè)流程的控制,用戶(hù)可以通過(guò)重載相關(guān)的接口,插入自己的shade tree節(jié)點(diǎn),修改每一步的操作或處理過(guò)程。例如:
virtual bool HandleBaseMap(Context& kContext, NiMaterialResource* pkUVSet,
NiMaterialResource*& pkDiffuseColorAccum,
NiMaterialResource*& pkOpacity, bool bOpacityOnly);
當(dāng)然,整個(gè)流程的順序和結(jié)構(gòu)修改起來(lái)比較困難,如果有需要可以定制自己的材質(zhì)系統(tǒng),搭建自己的shade tree。
NiStandardMaterial提供了若干回調(diào)函數(shù),這些函數(shù)可以動(dòng)態(tài)的修改流程,分割PASS,對(duì)shader運(yùn)行失敗進(jìn)行容錯(cuò)。
l SplitPerPixelLights/SplitPerVertexLights:這兩個(gè)函數(shù)分別作用于逐頂點(diǎn)光照和逐像素光 照,當(dāng)物體所受的光源數(shù)量太多,超過(guò)了頂點(diǎn)或像素著色器的能力時(shí),通過(guò)這些函數(shù)可以將失敗的pass分割成兩個(gè),如果分割出的pass仍然不能執(zhí)行,那么函數(shù)會(huì)被遞歸調(diào)用,直到每個(gè)pass只有一個(gè)光源為止。
l SplitTextureMaps:這個(gè)函數(shù)會(huì)把對(duì)紋理采樣的pass進(jìn)行分割,當(dāng)紋理查詢(xún)過(guò)多時(shí),頂點(diǎn)或像素著色器就會(huì)過(guò)于復(fù)雜,這時(shí)就可能導(dǎo)致shader運(yùn)行失敗。此函數(shù)只能迭代一次,生成一個(gè)額外的pass。
l DropParallaxMap:這個(gè)函數(shù)用來(lái)從幾何體上移除視差貼圖,且不產(chǎn)生額外的pass。
l DropParallaxMapThenSplitLights:這個(gè)函數(shù)首先調(diào)用DropParallaxMap移除視差貼圖,然后一直調(diào)用SplitPerPixelLights直到失敗為止。
NiMaterialInstance:
NiMaterial并不是直接與NiRenderObject相關(guān)聯(lián),而是經(jīng)過(guò)了NiMaterialIstance這個(gè)中間層,由它來(lái)代理將NiMaterial關(guān)聯(lián)到幾何體,它負(fù)責(zé)調(diào)用NiMaterial為NiRenderObject生成NiShader,通過(guò)改變NiMaterialIstance上的接口SetMaterialNeedsUpdate,可以決定每一幀NiRenderObject所使用的材質(zhì)是否需要被更換,通過(guò)接口SetDefaultMaterialNeedsUpdateFlag,可以決定當(dāng)前材質(zhì)所需的數(shù)據(jù)(即當(dāng)前渲染流程所需的數(shù)據(jù))是否要被更新。這樣每個(gè)NiMaterialIstance只能被一個(gè)NiRenderObject所擁有,而多個(gè)NiMaterialIstance可以共享一個(gè)NiMaterial,這樣就減少了重復(fù)創(chuàng)建NiMaterial的時(shí)間和空間上的開(kāi)銷(xiāo),同時(shí)降低了渲染對(duì)象個(gè)材質(zhì)之間的耦合度。
如下為材質(zhì)系統(tǒng)的類(lèi)結(jié)構(gòu)簡(jiǎn)化圖:
渲染屬性:
前面提到過(guò),GameByro將渲染所需要加工的數(shù)據(jù)全部封裝在了NiProperty中,只要在shader中用指定的語(yǔ)法進(jìn)行聲明,就可以訪(fǎng)問(wèn)這些屬性的值。
目前引擎中已經(jīng)定義了12種屬性,均派生自NiProperty,分別代表渲染數(shù)據(jù)的12種不同類(lèi)型:
用戶(hù)也可以自定義屬性類(lèi)型,但所對(duì)應(yīng)的數(shù)據(jù)類(lèi)型要被shader語(yǔ)言所支持。
光照與陰影:
光照與陰影密不可分,因?yàn)殛幱熬褪怯晒庹债a(chǎn)生的,前面在材質(zhì)系統(tǒng)中已經(jīng)提到過(guò)光照對(duì)著色的影響,這里重點(diǎn)闡述,GameByro是怎樣根據(jù)光源產(chǎn)生陰影的。由GameByro提供的陰影均基于ShadowMap技術(shù),
但也提供了ShaowVolume的示例代碼。
Shadowing System是完全建立在幀渲染系統(tǒng)上的, 通過(guò)一個(gè)RenderClick生成ShadowMap,然后在正常渲染流程開(kāi)始之前將ShadowMap更新到可見(jiàn)集內(nèi)每一個(gè)渲染對(duì)象上。這樣當(dāng)渲染對(duì)象使用NiStandardMaterial時(shí)就會(huì)根據(jù)光源的陰影技術(shù)來(lái)對(duì)ShadowMap進(jìn)行采樣,并將結(jié)果與最終的輸出顏色按一定比例混合。
陰影系統(tǒng)由以下幾個(gè)類(lèi)構(gòu)成:
- Shadow Write Materials:從NiFragmentMaterial派生,封裝了生成 ShadowMap的算法和著色程序。
GameByro提供了三種類(lèi)型的Shadow Write Materials,分別為NiPointShadowWriteMaterial、
NiDirectionalShadowWriteMaterial、NiSpotShadowWriteMaterial,適用于三種不同的光源類(lèi)型。
- Shadow Technique:這個(gè)類(lèi)封裝了陰影算法的細(xì)節(jié),包括生成ShadowMap和使用ShadowMap投射陰影。
- Shadow Render Click: 這個(gè)類(lèi)是一個(gè)生成ShadowMap的批,這個(gè)類(lèi)的對(duì)象是由shadow click generator負(fù)責(zé)生成。
- Shadow Click Validator: Shadow Render Click:通過(guò)此類(lèi)對(duì)象判斷接受陰影的幾何體對(duì)于shadow generator來(lái)說(shuō)是否可見(jiàn)。
- Shadow Map: 每個(gè)ShadowMap對(duì)象包含一個(gè)作為陰影圖的紋理,shadowmap對(duì)象由shadowManager直接管理,每個(gè)shadowmap對(duì)象都被一個(gè)shadow generator引用。
- Shadow Cube Map: 同shadowmap作用相同,只是陰影圖的紋理類(lèi)型為CubeMap。主要用于點(diǎn)光源生成的全方向陰影。
- Shadow Generator: 陰影生成器,每個(gè)ShadowGenerator都對(duì)應(yīng)一個(gè)NiDyamicEffect(NiLight的基類(lèi)),也就是為這個(gè)NiDyamicEffect代表的光源生成陰影, 生成陰影所采用的技術(shù)由對(duì)象引用的ShadowTechnique來(lái)決定。
- Shadow Click Generator: 生成ShadowMap的Shadow Render Click都由此類(lèi)負(fù)責(zé)創(chuàng)建。這個(gè)類(lèi)為每個(gè)ShadowGenerator指定ShadowMap,并負(fù)責(zé)每幀更新ShadowMap和ShadowMap所對(duì)應(yīng)的變換矩陣。
- Shadow Manager:所有的ShadowMap、ShadowTechnique、ShadowGenerator、shadow render click對(duì)象都由ShadowManager統(tǒng)一管理,并負(fù)責(zé)使用一個(gè)shadow click generator 在每一幀生成一個(gè)shadow render click的列表。
陰影系統(tǒng)靜態(tài)結(jié)構(gòu)如下:
1. 在應(yīng)用程序初始化階段,通過(guò)調(diào)用NiShadowManager的Initialize()接口實(shí)現(xiàn)對(duì)整個(gè)陰影系統(tǒng)的初始化,此時(shí)應(yīng)用程序會(huì)注冊(cè)所有的ShadowTechnique,并初始化NiShadowClickGenerator。
2. 當(dāng)我們創(chuàng)建一個(gè)NiLight以后,我們可以通過(guò)NiShadowManager為這個(gè)NiLight新建一個(gè)NiShadowGenerator,NiShadowGenerator會(huì)通過(guò)NiLight的類(lèi)型來(lái)選擇合適的NiShadowTechnique,此時(shí)NiShadowManager會(huì)為新的NiShadowGenerator創(chuàng)建一個(gè)NiShadowRenderClick。
3. 當(dāng)幀渲染系統(tǒng)啟動(dòng)后,NiShadowRenderClick的PerformRendering()接口會(huì)被調(diào)用,此時(shí)NiShadowRenderClick會(huì)通過(guò)引用的NiGenerator獲得陰影生成的著色程序和所需的數(shù)據(jù)(例如深度偏移),同時(shí)通過(guò)NiGenerator引用的Camera獲得場(chǎng)景圖中的可見(jiàn)集。下一步就是對(duì)可見(jiàn)集中的渲染對(duì)象添加ShadowWriteMaterial并設(shè)為激活狀態(tài),而ShadowWriteMaterial的類(lèi)型是根據(jù)NiDyamicEffect的類(lèi)型指定的。最后NiRenderClick就會(huì)啟動(dòng)渲染流水線(xiàn),將可見(jiàn)集中的對(duì)象的深度全部渲染到ShadowMap中。
4. 在第一個(gè)RenderClick中生成了ShadowMap,下面就要使用這些ShadowMap投射陰影。在每一幀開(kāi)始之前,用戶(hù)還可以自己指定不接受陰影的節(jié)點(diǎn),手動(dòng)將其插入NiShadowGenerator::m_kUnaffectedReceiverList中。在渲染BackBuffer的RenderClick中,首先會(huì)對(duì)場(chǎng)景圖中的節(jié)點(diǎn)進(jìn)行一次遍歷,將不受陰影的節(jié)點(diǎn)放入NiShadowGenerator:: m_kUnaffectedCasterList的列表。在對(duì)每個(gè)節(jié)點(diǎn)進(jìn)行渲染時(shí),會(huì)遍歷NiShadowManager中所有的NiShadowGenerator,判斷這些NiShadowGenerator是否對(duì)這個(gè)節(jié)點(diǎn)有影響,判斷的規(guī)則是此節(jié)點(diǎn)是否存在于UnaffectedReceiverList和UnaffectedCasterList這兩個(gè)鏈表中,如果存在于任何一個(gè)鏈表,則節(jié)點(diǎn)不受此NiShadowGenerator影響,如果受此NiShadowGenerator影響,那么就將該NiShadowGenerator上的ShadowMap和數(shù)據(jù)更新到節(jié)點(diǎn)上的渲染屬性中,NiStandardMaterial會(huì)根據(jù)這些數(shù)據(jù)選擇對(duì)ShadowMap采樣的方式,并將結(jié)果混合到最終的輸出顏色中。
值得注意的是,點(diǎn)光源的shadowMap默認(rèn)的是采用CubeMap實(shí)現(xiàn),用戶(hù)可以通過(guò)接口選擇不使用CubeMap實(shí)現(xiàn),當(dāng)采用CubeMap實(shí)現(xiàn)時(shí),光源無(wú)法產(chǎn)生軟陰影。
渲染系統(tǒng)的擴(kuò)展:
為了驗(yàn)證GameByro渲染系統(tǒng)的擴(kuò)展性,筆者嘗試著加入了一個(gè)后期處理特效Screen Space Ambient Occlusion(SSAO),即屏幕空間的遮蔽,由于僅僅為了熟悉GameByro的渲染流程,所以筆者并未對(duì)SSAO算法做深究,僅僅用了自己簡(jiǎn)化的算法。
在渲染過(guò)程中先單獨(dú)使用一個(gè)RanderClick將場(chǎng)景中的深度渲染到一張紋理上,然后在渲染到后臺(tái)緩沖區(qū)的RenderClick中對(duì)深度紋理進(jìn)行采樣,執(zhí)行SSAO算法,將結(jié)果混合到最終的結(jié)果中。采樣點(diǎn)的偏移坐標(biāo)是通過(guò)對(duì)一組隨機(jī)向量進(jìn)行歸一化再乘以0~1之間的隨機(jī)數(shù)而生成的,即長(zhǎng)度為0~1之間的隨機(jī)向量。
PS代碼如下:
float VerticalRange:GLOBAL; //控制XY方向采樣范圍變量,可以在應(yīng)用程序?qū)訉?duì)其進(jìn)行調(diào)整
float HorizontalRange:GLOBAL; //控制在Z方向采樣范圍的變量
float calAO(float2 texCoord,float dw, float dh ) //通過(guò)當(dāng)前像素所標(biāo)和偏移量計(jì)算AO
{
float2 coord = float2(texCoord.x + dw, texCoord.y + dh);
float4 CenterPos = tex2D(DepthSampler,texCoord);
float4 CurPos = tex2D(DepthSampler,coord);
float depthDiff = clamp(CenterPos.z - CurPos.z,0,VerticalRange);
float ao = depthDiff/length(CurPos.xyz - CenterPos.xyz);
return ao;
}
// Pixel shader
float4 PS_SSAO(VS_OUTPUT In) : COLOR
{
float2 texCoord = In.BaseTex;
float depth = tex2D(DepthSampler,texCoord).z;
float ao = 0.0;
float scale = HorizontalRange/depth; //因?yàn)椴蓸臃秶鷷?huì)受深度影響,故除以此系數(shù)。
for(int i=0; i<32; ++i)
{
float2 offset = arrRandomPt[i].xy* scale;
ao += calAO(texCoord, offset.x, offset.y);
}
ao/=32;
float4 color = tex2D(BaseSampler,texCoord);
color.xyz *= (1.0 - ao);
return color;
}
最終實(shí)現(xiàn)效果如下:
SSAO生成的明暗圖 無(wú)SSAO材質(zhì)
以下兩圖上圖為無(wú)SSAO效果,下圖為開(kāi)啟SSAO后的效果。
總結(jié):
GameByro的幀渲染系統(tǒng)是比較靈活,想加入自己的渲染流程是比較容易的,此外由于RenderTarget和RenderView都可以由用戶(hù)指定,所以想實(shí)現(xiàn)自己的shader效果不是很難。然而,NiStanderMaterial的shade tree比較復(fù)雜,總共高達(dá)6000行代碼以上,過(guò)程非常復(fù)雜,這就是說(shuō),如果想實(shí)現(xiàn)自己的材質(zhì)處理流程也要付出相當(dāng)大的工作量,陰影系統(tǒng)雖然實(shí)現(xiàn)了較低的耦合度,但是實(shí)現(xiàn)過(guò)于復(fù)雜,不夠簡(jiǎn)潔高效。
作者:葉起漣漪