【Siggraph 2015】GPU-Driven Rendering Pipelines

本文是育碧的兩個(gè)工程師在Siggraph2015上的陳述,是《刺客信條Unity》(以下簡稱ACU,Montreal工作室)開發(fā)過程中所使用的GPU驅(qū)動(dòng)的渲染管線以及RedLynx工作室的GPU驅(qū)動(dòng)渲染管線實(shí)施方案的介紹。

整個(gè)陳述分成如下幾個(gè)部分,第一個(gè)部分是GPU驅(qū)動(dòng)渲染管線的背景與動(dòng)機(jī);第二個(gè)部分是GPU/CPU渲染管線都會(huì)用到的mesh cluster rendering方法的簡介;第三個(gè)部分會(huì)對(duì)ACU的GPU驅(qū)動(dòng)渲染管線做一個(gè)詳細(xì)的介紹;第四個(gè)部分則是對(duì)Occlusion Depth數(shù)據(jù)的生成算法的介紹,最后給出所有成果的實(shí)施效果。

GPU驅(qū)動(dòng)渲染管線是什么意思?總的來說,就是將此前由CPU完成的物件渲染前剔除處理以及渲染輸出的target viewport的指定工作移交給GPU來完成,物件渲染的整個(gè)過程不需要CPU對(duì)資源數(shù)據(jù)進(jìn)行干涉以避免對(duì)GPU流程的阻塞。

之所以要這樣做,是因?yàn)殡S著算法復(fù)雜度的增加以及場景復(fù)雜度的提升,相對(duì)于串行處理運(yùn)算器CPU而言,并行處理運(yùn)算器GPU的消耗會(huì)更低,計(jì)算效率更高,且物體可見性使用時(shí)延基本可以忽略。

育碧RedLynx工作室(以下簡稱R工作室)產(chǎn)出的游戲,對(duì)于UGC(user generated content)依賴較高:

  1. 包括背景在內(nèi),場景大多是由小塊數(shù)據(jù)拼接而成;
  2. 場景渲染范圍通常也比較廣(深度大);
  3. 場景數(shù)據(jù)需要從服務(wù)器下載得到,而由于場景是由小塊組成的,因此離線光照烘焙基本用不了。如果再考慮到陰影的渲染管線的話,整個(gè)渲染管線的負(fù)擔(dān)進(jìn)一步加重;
  4. 物理模擬以及邏輯腳本系統(tǒng)會(huì)占用較多的CPU時(shí)間。

背景介紹:

R工作室很早就在Xbox 360上試驗(yàn)GPU驅(qū)動(dòng)渲染管線的可行性了,最早是嘗試通過可編程頂點(diǎn)fetch以及memexport方案來實(shí)現(xiàn),不過由于硬件限制,當(dāng)時(shí)的性能表現(xiàn)并不能達(dá)到要求。

之后Persson在Siggraph 2012上給出的Merge-Instancing技術(shù)方案(詳情參考此前的這篇文章)進(jìn)入了R工作室的視線,這個(gè)技術(shù)使用了Xbox 360的可編程頂點(diǎn)fetch技術(shù)在運(yùn)行時(shí)通過vs對(duì)mesh數(shù)據(jù)進(jìn)行合并處理。這個(gè)技術(shù)實(shí)現(xiàn)過程中不需要通過在內(nèi)存中進(jìn)行頂點(diǎn)或者索引數(shù)據(jù)的的拷貝來實(shí)現(xiàn)不同mesh的一次性繪制,而是通過在VS中模擬index buffer的工作流程來強(qiáng)制對(duì)每個(gè)三角面片執(zhí)行三遍VS邏輯的方式實(shí)現(xiàn)的。在這個(gè)過程中不需要用到Post Vertex Cache(即post-transform cache ,指的是那些使用帶索引的渲染API在執(zhí)行的時(shí)候,會(huì)將一小批近期用到的頂點(diǎn)數(shù)據(jù)存儲(chǔ)到cache中,從而提升后續(xù)渲染時(shí)訪問數(shù)據(jù)的速度),因此性能上有一個(gè)非常大的提升。

ACU是第一代為新一代硬件而設(shè)計(jì)的《刺客信條》游戲,在這個(gè)游戲中,美術(shù)同學(xué)添加了大量的幾何物件以實(shí)現(xiàn)對(duì)真實(shí)巴黎的模擬。

同時(shí),ACU也是第一次嘗試實(shí)現(xiàn)模型內(nèi)部空間的無縫銜接(seamless interior spaces這個(gè)是啥,目的何在?推測是指內(nèi)部面片結(jié)構(gòu)無縫銜接,以達(dá)到高真實(shí)度的表現(xiàn)效果,通常會(huì)需要使用較多的面片來對(duì)細(xì)節(jié)進(jìn)行填充)的游戲,這種做法使得需要處理的幾何數(shù)據(jù)進(jìn)一步上升。

此外,還有眾多的角色模型,進(jìn)一步加劇了渲染管線的壓力。

為了能夠創(chuàng)建一個(gè)如此巨大的游戲場景,巴黎場景的第一輪構(gòu)建是通過半自動(dòng)的方式實(shí)現(xiàn)的,整個(gè)過程使用了數(shù)百個(gè)可以復(fù)用的模型來創(chuàng)建大量的房屋模塊(house blocks)。如果按照傳統(tǒng)的一個(gè)模型占用一個(gè)DP的渲染方式,將會(huì)導(dǎo)致DP數(shù)超過五萬,而即使使用實(shí)例化渲染技術(shù),最終的DP也會(huì)高于一萬五。

即使在主機(jī)上,CPU也是非常寶貴的資源,為了避免CPU稱為渲染管線的瓶頸,這里給出的做法是為使用更為激進(jìn)的合批策略,同時(shí)采用更為高效的剔除手段。而Mesh cluster rendering正好符合這個(gè)標(biāo)準(zhǔn)。

Mesh Cluster Rendering可以在加大剔除粒度的前提下同時(shí)得到更為激進(jìn)的合批策略(??沒看出這兩者有什么矛盾)。由于在GPU中無法通過可編程方式獲得頂點(diǎn)的索引數(shù)據(jù),因此想要通過單個(gè)帶實(shí)例的DP實(shí)現(xiàn)多個(gè)不同物件的渲染,就需要強(qiáng)制多個(gè)物體使用相同的拓?fù)浣Y(jié)果(為什么使用相同的拓?fù)浣Y(jié)構(gòu),就能實(shí)現(xiàn)單DP,多Mesh渲染?當(dāng)每個(gè)instance的頂點(diǎn)數(shù)固定時(shí),通過instance_id就能夠拿到當(dāng)前instance在VB中的起始地址,從而可以一次性取出所有頂點(diǎn)進(jìn)行VS處理?)。ACU選擇的是“Vertex Strip”拓?fù)浣Y(jié)構(gòu):將所有的mesh數(shù)據(jù)分割成64個(gè)vertex strips組成的clusters(由于每個(gè)mesh的頂點(diǎn)數(shù)不同,組成這個(gè)mesh的clusters數(shù)目也有所區(qū)別,相同的是cluster的尺寸是恒定的),也就是62個(gè)三角形組成一個(gè)cluster。

除了這種拓?fù)浣Y(jié)構(gòu)之外,其他的比如32四邊形(quad)等固定索引buffer的拓?fù)浣Y(jié)構(gòu)也是可用的。不管選擇哪種拓?fù)浣Y(jié)構(gòu),都是需要在其中添加退化三角形實(shí)現(xiàn)多個(gè)mesh part之間的連接,以及在每個(gè)mesh末尾添加退化三角形來補(bǔ)足最后一個(gè)cluster。

這個(gè)過程是在mesh編輯完成后進(jìn)行的,可以看成mesh編輯后處理:從mesh數(shù)據(jù)構(gòu)建出triangle strips,之后使用一個(gè)貪心算法構(gòu)建一個(gè)局部clusters。由于在渲染的時(shí)候需要獲取一個(gè)cluster的不同頂點(diǎn)數(shù)據(jù),因此這里不能直接使用硬件自帶的vertex fetch函數(shù),而是需要通過vertex id & instance id手動(dòng)讀取全量數(shù)據(jù)。按照這種方式,我們可以通過一個(gè)DrawInstancedIndirect(在D3D11中,這個(gè)函數(shù)可以看成是DrawInstanced函數(shù)的重載版本,其作用是將某個(gè)instance繪制多遍,其中指定了起始vertex在VB中的Offset跟instance的Offset,如果這個(gè)接口真的能夠完成對(duì)多個(gè)不同mesh的一次性繪制,那么即使每個(gè)instance使用不同尺寸的VB應(yīng)該也是可以的吧?DrawInstancedIndirect有兩個(gè)參數(shù),一個(gè)是參數(shù)列表指針,第二個(gè)是參數(shù)偏移,通過調(diào)整參數(shù)偏移,可以實(shí)現(xiàn)對(duì)不同instance的繪制,因此在設(shè)置好VB之后,多次調(diào)用這個(gè)接口就能夠?qū)崿F(xiàn)不同mesh的instance繪制,不過這樣就是多個(gè)DP了,跟描述貌似不太符合?這個(gè)接口應(yīng)該是將多個(gè)不同mesh的數(shù)據(jù)統(tǒng)一到一個(gè)VB中,并且將這些帶有instance的數(shù)據(jù)塞入到instance buffer中,之后調(diào)用一個(gè)DrawInstancedIndirect接口,在VS中完成對(duì)不同mesh cluster數(shù)據(jù)的讀取與訪問,實(shí)現(xiàn)多個(gè)mesh的一次性繪制) DP完成任意數(shù)目的mesh的繪制,DP參數(shù)以及cluster stream數(shù)據(jù)會(huì)通過GPU計(jì)算得到,計(jì)算過程會(huì)對(duì)每個(gè)cluster啟用一次culling計(jì)算。


對(duì)于stripped渲染拓?fù)浣Y(jié)構(gòu),每個(gè)strip cut(相當(dāng)于告訴硬件當(dāng)前strip已經(jīng)結(jié)束,下面進(jìn)入下一個(gè)strip)都會(huì)導(dǎo)致4個(gè)額外的冗余頂點(diǎn)與4個(gè)冗余面片,對(duì)內(nèi)存占用,VS渲染消耗以及最大多邊形吞吐量有影響。

ACU中將mesh數(shù)據(jù)分割成固定64個(gè)頂點(diǎn)的實(shí)例數(shù)據(jù)(每個(gè)cluster可以看成一個(gè)instance),這個(gè)過程通過貪心clustering算法完成,這種渲染架構(gòu)對(duì)于頂點(diǎn)數(shù)據(jù)與instance數(shù)據(jù)獲取的時(shí)間復(fù)雜度為O(1)。

算法Bonus:DX11的DrawInstancedIndirect接口會(huì)在每個(gè)instance結(jié)束的時(shí)候自動(dòng)添加一個(gè)strip cut(這個(gè)是什么?可以堪稱是一個(gè)用于結(jié)束繪制的overhead),這個(gè)過程是免費(fèi)的。

在PC可編程管線中,無法通過vertex fetch獲得頂點(diǎn)數(shù)據(jù)。ACU是通過SRV(Shader Resource View)來對(duì)頂點(diǎn)數(shù)據(jù)進(jìn)行讀取的:頂點(diǎn)數(shù)據(jù)按照SoA(Structure of Array,由多個(gè)數(shù)組作為成員組成的結(jié)構(gòu)體,對(duì)應(yīng)到VB上,就是將頂點(diǎn)的每個(gè)屬性都單獨(dú)抽取出來組成一個(gè)個(gè)的屬性數(shù)組;與之相對(duì)應(yīng)的是AoS,Array of Structure,由相同結(jié)構(gòu)體組成的結(jié)構(gòu)體數(shù)組,對(duì)應(yīng)到VB上,就是將每個(gè)頂點(diǎn)的多個(gè)屬性組成一個(gè)結(jié)構(gòu)體,之后用這種結(jié)構(gòu)體數(shù)組表示VB)方式排布。這種做法可以降低GPU延遲(為啥?對(duì)于不同頂點(diǎn)的同一個(gè)屬性的讀取,其速度更快)在GCN(這個(gè)是啥?Graphics Core Next,是AMD為其GPU所開發(fā)的微結(jié)構(gòu)microarchitecture的代號(hào),也指與之對(duì)應(yīng)的指令集)上比硬件Vertex Buffer讀取數(shù)據(jù)表現(xiàn)更好。

這些特點(diǎn)不但可以用于實(shí)現(xiàn)更為激進(jìn)的合批方案,而且還可以用于對(duì)GPU Frustum/Occlusion Bulling效果進(jìn)行精細(xì)調(diào)整。

這里不會(huì)對(duì)GPU Occlusion Culling的實(shí)現(xiàn)細(xì)節(jié)進(jìn)行深入介紹,不過會(huì)給出一些參考文獻(xiàn)供大家查閱。

另外,culling方案調(diào)整程度越精細(xì),不但可以剔除更多的頂點(diǎn)數(shù)據(jù),而且還能進(jìn)一步降低overdraw(如右圖所示),此外對(duì)于美術(shù)同學(xué)的工作也有所幫助:他們不再需要對(duì)于物件尺寸做精心規(guī)劃以得到更為有效的裁剪結(jié)果。

測試顯示,手動(dòng)vertex fetch功能比自動(dòng)vertex fetch功能還要快一點(diǎn),此外,這種方法還可以用于對(duì)cluster depth進(jìn)行排序,從而起到類似depth pre-pass一樣的降低overdraw的作用。

Stripped triangle instance渲染方法+mesh clustering 渲染方法在所有平臺(tái)上的表現(xiàn)都還不錯(cuò)。不過ACU在上線之前還是選擇切換到另一種渲染方法,原因是為了降低由于退化三角形導(dǎo)致的內(nèi)存消耗以及修復(fù)由于無法調(diào)整cluster的渲染順序(non-deterministic cluster order)而導(dǎo)致的深度競爭問題(這些問題通常是由于building模塊對(duì)齊關(guān)系處理得不夠好導(dǎo)致)。

使用比如手動(dòng)index buffer數(shù)據(jù)獲取或者cluster depth排序方法可以解決這些問題,不過ACU選擇的是一個(gè)類似的mesh cluster渲染方法,即使用多個(gè)DrawIndexedInstancedIndirect接口來完成繪制(這個(gè)接口跟之前的DrawInstancedIndirect接口大同小異,區(qū)別在于不再使用strip拓?fù)浣Y(jié)構(gòu),加上了索引數(shù)據(jù)來降低頂點(diǎn)數(shù)據(jù)的空間占用)。在這種方法中,會(huì)借助傳統(tǒng)的頂點(diǎn)緩存優(yōu)化策略來對(duì)mesh數(shù)據(jù)進(jìn)行優(yōu)化,之后將mesh分割成統(tǒng)一的64個(gè)triangle的cluster(如果使用索引方法,就沒有辦法通過instance_id + vertex_id來得到頂點(diǎn)在vertex buffer中的位置,反之亦然,此時(shí)還有必要保證每個(gè)cluster的triangle數(shù)目恒定在64上有什么意義呢?難道僅僅是為了指定一個(gè)恒定的cluster尺寸嗎?方便實(shí)現(xiàn)cluster的拆分與深度排序)。

這是整個(gè)渲染管線的概覽,在CPU側(cè),依然需要進(jìn)行粗糙的frustum culling,之后將所有未被剔除的物件按照材質(zhì)進(jìn)行合批處理。在GPU側(cè),對(duì)每個(gè)instance進(jìn)行frustum/occlusion culling處理。在對(duì)cluster按照frustum/occlusion depth進(jìn)行cull之前還需要進(jìn)行一個(gè)cluster expansion處理(干了啥,目的何在?)。部分backfacing面片在index buffer compaction過程中會(huì)被剔除掉。完成所有可見性測試之后的輸出數(shù)據(jù)被用作multi-drawcall處理階段的輸入。此外,可形變物體不需要經(jīng)過上述管線的cluster相關(guān)步驟,直接跳過即可。

正如之前所說,在CPU上,需要進(jìn)行簡單的quad tree culling。之后對(duì)每個(gè)動(dòng)態(tài)實(shí)例物件進(jìn)行數(shù)據(jù)更新,如transform等數(shù)據(jù)的更新,更新過程在GPU Ring Buffer(Ring buffer是GPU Command的索引buffer,存儲(chǔ)了每個(gè)GPU Command在Command buffer中的位置與長度)進(jìn)行,并為所有無法通過GPU Instancing繪制的物體構(gòu)建一個(gè)hash值,之后基于此hash值對(duì)DP進(jìn)行合并處理,并為GPU渲染構(gòu)建所需要的instance stream。


四叉樹中物體的粒度會(huì)隨著數(shù)據(jù)而變化:

1.房屋

2.大型物件

3.部分特別的物體

4.部分細(xì)小的動(dòng)態(tài)物件(比如角色)

DP會(huì)在合并之前按照距離進(jìn)行排序。雖然通過通過boundingbox來進(jìn)行排序得到的并不是完美的深度順序結(jié)果,但是相對(duì)于合并后排序,其效果已經(jīng)好很多了。

由于傳統(tǒng)技術(shù)的限制,ACU的渲染管線距離完美的CPU目標(biāo)還有很長的距離。在CPU上依然需要對(duì)物件(即使是靜態(tài)的)進(jìn)行處理,且只能對(duì)使用相同材質(zhì)的物件進(jìn)行實(shí)例繪制。

Instance stream包含了各個(gè)instance在GPU-buffer中的offset列表,從而使得GPU獲取如transform,instance bounds等數(shù)據(jù)。

GPU會(huì)使用這些數(shù)據(jù)進(jìn)行instance層面的frustum/Occlusion culling。對(duì)于所有通過culling test的instance,會(huì)生成一個(gè)cluster chunks列表。

這里ACU使用中間過程的cluster chunk expansion(這個(gè)是啥意思?將mesh數(shù)據(jù)拆分成多個(gè)cluster嗎?)而非直接的cluster expansion,這是因?yàn)槊總€(gè)mesh的clusters數(shù)目是可變的(1~1000)。直接的cluster export在同一個(gè)wavefront的不同GPU線程中可能會(huì)非常的不平衡(會(huì)有什么結(jié)果呢?GPU算力浪費(fèi))。而每個(gè)cluster chunk卻最多對(duì)應(yīng)于64個(gè)clusters。

之后的cluster culling過程會(huì)使用instance transform&bounds數(shù)據(jù)來對(duì)cluster進(jìn)行frustum/occlusion culling處理。對(duì)于每個(gè)cluster,ACU還會(huì)獲取一個(gè)View Dependent Triangle Mask用于進(jìn)行烘焙前的backface culling。通過culling的clusters會(huì)輸出一個(gè)index compaction job,用于構(gòu)建繪制所需要的index buffer。index compaction job輸出數(shù)據(jù)包含了triangle mask以及索引讀寫offsets。這些offsets可以通過相關(guān)的instance面片數(shù)目計(jì)算得到。

對(duì)所有通過剔除測試的cluster進(jìn)行index compaction處理,并將結(jié)果寫入到一個(gè)動(dòng)態(tài)index buffer中。

這個(gè)index buffer是在CPU上分配的,因此需要為每個(gè)instance mesh按照全量的未被剔除前的索引數(shù)據(jù)分配所需要的空間。由于index buffer尺寸比較小(8mb,為什么比較小,8mb怎么得到的?),因此一個(gè)render pass的數(shù)據(jù)可能無法全部塞入到buffer中,而需要分成多個(gè)render passes,因此索引buffer compaction與multi-draw rendering操作會(huì)交叉進(jìn)行。

此時(shí)index compaction會(huì)將被cluster culling & backface culling的triangle移除掉。在每個(gè)index compaction計(jì)算任務(wù)中,每個(gè)wavefront會(huì)處理一個(gè)cluster,各個(gè)wavefront之間相互獨(dú)立,每個(gè)線程處理一個(gè)triangle,且線程之間也是相互獨(dú)立的。基于cluster culling輸出input/output offsets以及triangle mask數(shù)據(jù),每個(gè)線程會(huì)獨(dú)立計(jì)算輸出數(shù)據(jù)在動(dòng)態(tài)index buffer的位置(write position)并拷貝3個(gè)triangle 索引(每個(gè)triangle包含三個(gè)頂點(diǎn),對(duì)應(yīng)三個(gè)index)。這一步需要占據(jù)5%~10%的渲染時(shí)間。

之后對(duì)每個(gè)批次調(diào)用MultiDrawIndexInstancedIndirect 接口來進(jìn)行g(shù)roup rendering,group rendering的DP則是在cluster culling階段通過原子操作生成的。

前面說過,在cluster culling時(shí),會(huì)需要取到一個(gè)view dependent的面片mask用于計(jì)算每個(gè)cluster內(nèi)部的可見面片數(shù)目。這里是通過將cluster內(nèi)部面片的可見性數(shù)據(jù)烘焙到一張cubemap中,cubemap的每個(gè)像素?cái)?shù)值對(duì)應(yīng)于一個(gè)相機(jī)位置的可見性結(jié)果。比如相機(jī)處于圖中黃色像素所對(duì)應(yīng)的frustum區(qū)域內(nèi),那么綠色面片的可見性就可以根據(jù)相機(jī)與cluster的相對(duì)位置直接從預(yù)計(jì)算cubemap中讀取出來,用這種做法可以一次性拿到cluster中所有64個(gè)面片的可見性結(jié)果,這個(gè)結(jié)果是一個(gè)bit mask,之后根據(jù)面片索引就能得到對(duì)應(yīng)面片的可見性結(jié)果。

換個(gè)視角來解釋這個(gè)方法,這個(gè)box是cluster的boundingbox,box中帶有箭頭的線段表示的是cluster中的面片與朝向。右邊包裹住相機(jī)的綠色區(qū)域表示的是黃色像素對(duì)應(yīng)的frustum。每個(gè)面片是否可見可以簡單的轉(zhuǎn)換為面片的正向半空間(half space)跟當(dāng)前像素對(duì)應(yīng)的frustum是否存在交集。如果相機(jī)本身位于box內(nèi)部,那么這個(gè)時(shí)候會(huì)直接將所有面片的可見性設(shè)置為可見。

ACU出于對(duì)內(nèi)存的考慮,只使用了六個(gè)像素來表示cubemap,即每個(gè)face只用一個(gè)像素表示,這種做法會(huì)導(dǎo)致culling精度有所下降,雖然通過增加像素frustum深度范圍可以對(duì)提升一點(diǎn)精度,但是提升非常有限,且還可能會(huì)導(dǎo)致一些不正確的剔除結(jié)果(比如本來是不可見的面片隨著frustum深度范圍的增加變成了可見(如上賬圖片中下面的面片一樣))。總的來說,這種做法可以剔除10%~30%的面片。

要想實(shí)現(xiàn)高效的GPU遮擋剔除,就需要一個(gè)較好的遮擋深度數(shù)據(jù)(occlusion depth)。這是ACU中的一個(gè)經(jīng)典場景,下面將以此為例給出ACU是如何生成不同類型的遮擋深度的。

ACU給出的第一種遮擋深度生成算法,是使用前n個(gè)最佳遮擋物(面積夠大,距離夠近)完成一個(gè)深度渲染pass(depth pre-pass)。depth渲染結(jié)果不但可以用作GPU culling,還可以作為early-z用于實(shí)際的normal渲染pass。這種做法可以降低由于合批或者排序問題導(dǎo)致的overdraw。這里生成的depth會(huì)被下采樣到512x256分辨率,并與上一幀normal渲染pass輸出的depth buffer經(jīng)過reprojection映射到本幀位置的結(jié)果相結(jié)合來給出最終的depth輸出。

最佳遮擋物的選擇是基于bounding volume以及美術(shù)同學(xué)預(yù)先設(shè)置的標(biāo)記來進(jìn)行的。在這些符合條件的遮擋物中,距離相機(jī)最近的300個(gè)遮擋物會(huì)被選擇用于進(jìn)行depth繪制,(也可以考慮面片數(shù)少的,以及覆蓋屏幕范圍大的)。

ACU這邊嘗試了多種選擇策略(比如考慮屏幕空間投影面積等),最終考慮到時(shí)間消耗,選擇了最為簡單的距離判定方法。

在這個(gè)pass中,不會(huì)進(jìn)行遮擋剔除,雖然確實(shí)可以使用上一幀depth buffer reprojection后的結(jié)果來進(jìn)行遮擋篩選。

這里給出一種填充此前Occlusion depth pre-pass中輸出的depth貼圖中的孔洞的簡易方法。孔洞的產(chǎn)生可能是由于物件不符合作為遮擋物的條件,也可以是選擇的遮擋物效果比較差,也可能是由于alpha test物體導(dǎo)致。

當(dāng)相機(jī)靜止不動(dòng)時(shí),使用上一幀的depth buffer結(jié)果可以很好的填充這些孔洞。但當(dāng)相機(jī)向著屏幕邊緣移動(dòng)時(shí)或者由于視差原因(相機(jī)旋轉(zhuǎn)導(dǎo)致),孔洞的填充就比較難辦了。

如果是大型的動(dòng)態(tài)物體,上一幀的深度數(shù)據(jù)也會(huì)被污染,為了避開這種錯(cuò)誤的depth數(shù)據(jù),ACU這邊會(huì)在處理reprojection時(shí)拒絕掉那些距離相機(jī)過近的物體(近才會(huì)導(dǎo)致在depth buffer中占據(jù)足夠多的像素,錯(cuò)誤就會(huì)很明顯?)。

此外,ACU還為每級(jí)Shadow Map生成了一個(gè)64x64的低分辨率遮擋深度貼圖。

第一步,通過reprojection操作,將上一幀的depth buffer數(shù)據(jù)投射到光源空間,來得到由陰影接收者組成的occlusion depth數(shù)據(jù),后面會(huì)給出更詳細(xì)的介紹。

這個(gè)occlusion depth結(jié)果接下來會(huì)跟上一幀生成的shadow map經(jīng)過reprojection后的數(shù)據(jù)結(jié)合起來,同樣的,這個(gè)過程會(huì)由于大型的可移動(dòng)物體而導(dǎo)致錯(cuò)誤的occlusion數(shù)據(jù)。

由于fog的計(jì)算需要用到下采樣的指數(shù)shadow map,因此上一幀的下采樣陰影貼圖是可以直接拿到的,不需要額外的計(jì)算。

通過上一幀的depth buffer reprojection得到的shadow map occlusion,目的是為了剔除那些不會(huì)有陰影接收者的陰影投影物體的繪制。在這個(gè)示例場景中,院子里的所有處于角色前面(這里指的是靠近光源方向)的陰影投射物體(剔除左側(cè)的大建筑物)都不會(huì)對(duì)陰影貼圖的輸出有任何貢獻(xiàn),因?yàn)檎麄€(gè)院子都被前面的建筑物所遮擋住了。

這里給出一個(gè)示意圖。

黃色箭頭表示的是太陽光方向。紅色的線段給出的是一級(jí)shadow map的覆蓋區(qū)域。

紅色方塊則是找不到接收陰影物體的陰影投射物體(因?yàn)檫@些物件對(duì)應(yīng)的陰影接收物體被地表或者其他物件所遮擋。)

亮黃色區(qū)域標(biāo)出的是foreground物件所創(chuàng)建的地平線以下部分,處于這個(gè)區(qū)域中的物體都是不可見的(即都是不需要用于繪制陰影的),因此這些物體在shadow map渲染中都可以剔除掉。

黃色區(qū)域上方的紅線在光源空間中的depth數(shù)據(jù)就是我們這里拿來進(jìn)行occlusion culling的數(shù)據(jù)了。

如果對(duì)于相機(jī)空間depth buffer中的每個(gè)像素,都在這個(gè)像素對(duì)應(yīng)的深度處繪制一個(gè)cube,之后從近平面向著這個(gè)立方體所在的深度延伸,就得到了這些cubes。

可以看到,圖中的綠色線條就是這些cube在光源空間中的最大深度對(duì)應(yīng)的位置,而這些位置與前面給出的黃色區(qū)域上方的紅色線條是一致的。很顯然,如果真的為每個(gè)像素繪制一個(gè)cube,那么消耗會(huì)非常高,ACU這邊的做法是為每16x16個(gè)像素組成的tile繪制一個(gè)cube,之后使用每個(gè)tile中的最大深度用作遮擋剔除的depth,這樣做的結(jié)果就是綠線所表示的結(jié)果相對(duì)于此前紅線表示的結(jié)果會(huì)更為保守(從位置上來看,綠線會(huì)比紅色更為往下)。

另外,在將這些cube繪制到光源空間中時(shí),需要注意修正cube的尺寸以保證繪制的結(jié)果不小于一個(gè)shadow map像素,且需要考慮shadow map的最大filter尺寸。

將相機(jī)空間的depth數(shù)據(jù)通過reprojection投影到光源空間。

ACU這里使用的方法跟[Silvennoinen2012]中的方法是非常相似的,不同的是原文中使用的depth mask,而ACU使用的是depth buffer,這是因?yàn)锳CU中計(jì)算光源空間frontface的是box tile中的遠(yuǎn)距面而非近距面,如果使用mask的話,得到的精度會(huì)低很多。

由于體積霧使用的是exponential shadow map,因此必須要考慮遠(yuǎn)距離陰影投射物體的濾除作用(為什么ESM需要考慮這個(gè),標(biāo)準(zhǔn)SM不用嗎?)。ACU在生成exponential shadow map的時(shí)候,會(huì)通過相機(jī)空間的深度經(jīng)過reprojected 后的數(shù)據(jù)來填充由于一些次要的陰影投射物體被culling掉(不能進(jìn)行全量shadow map繪制,成本太高)導(dǎo)致shadow map上的孔洞。之所以要這樣做,是因?yàn)槭褂玫仄骄€來代替光源空間的遠(yuǎn)平面來對(duì)陰影投射物體進(jìn)行剔除的效果要好得多,且光源空間中某個(gè)區(qū)域的陰影投射物體距離光源越遠(yuǎn),這個(gè)效果就越好。

這里給出ACU實(shí)施管線的結(jié)果,基本達(dá)成目標(biāo);GPU可能還需要優(yōu)化下異步處理邏輯,增強(qiáng)GPU處理的并行性。

后面會(huì)考慮通過bindless texture進(jìn)一步降低DP(為什么可以降低?),這樣做除了可以降低CPU使用率,同時(shí)還有助于降低由于合批mesh在距離上的順序并不嚴(yán)格所導(dǎo)致的GPU overdraw,當(dāng)DP數(shù)較少的時(shí)候,渲染的順序就會(huì)再次接近于按照物體box中心點(diǎn)到相機(jī)的距離來排序時(shí)的表現(xiàn)(推測這里的渲染指的是cluster的渲染),也就是說,當(dāng)DP數(shù)較低的時(shí)候,可以按照cluster到相機(jī)的距離來排序了(DP數(shù)多的時(shí)候不可以嗎?)

由于DX12&Vulkan極大的降低了DP的消耗,因此GPU渲染管線(如這里ACU介紹的實(shí)現(xiàn)算法)的優(yōu)勢將被抑制。而這種GPU渲染管線在DX12&Vulkan下是否能夠生效還取決于所用的數(shù)據(jù)結(jié)構(gòu)與算法。后面部分將會(huì)給出一些可能的前進(jìn)方向。


GPU驅(qū)動(dòng)渲染管線在DX12上的優(yōu)勢:

1.在DX12中,尤其是PC上,CPU想要獲取到GPU的depth buffer等資源,依然具有較高的延遲-à因此不能根據(jù)可見像素的數(shù)據(jù)來對(duì)shadow進(jìn)行剔除

2.CPU在數(shù)目超過100k的sub-object層面的剔除處理效率依然很低

3.GPU驅(qū)動(dòng)的剔除算法更為高效。

- 參考: Modified Intel DirectX 12 asteroids demo with ExecuteIndirect. Runs faster on Intel GPU and has much lower CPU usage.

在后面加強(qiáng)異步計(jì)算邏輯的控制之后,ACU這邊希望能夠?qū)⒁恍〨PU驅(qū)動(dòng)的管線中的非渲染的部分移動(dòng)到異步計(jì)算邏輯中以移除由于眾多的細(xì)小異步計(jì)算任務(wù)所導(dǎo)致的pipeline bubbles。

下面要介紹的是RedLynx工作室(下面簡稱R工作室)的GPU驅(qū)動(dòng)渲染管線,核心技術(shù)跟前面介紹過的Montreal的差不多,因此接下來將著重介紹兩者之間的區(qū)別。

首先要介紹的是Virtual Texture(以下簡稱VT)。這種技術(shù)方案能夠跟GPU驅(qū)動(dòng)的渲染管線完美結(jié)合起來,從而可以極大的降低DP數(shù)目。

Deferred Texturing不是一個(gè)新的想法,不過當(dāng)將之與VT以及GPU驅(qū)動(dòng)的渲染管線相結(jié)合時(shí),將煥發(fā)出新的生機(jī)。后面將會(huì)介紹著三種技術(shù)方案結(jié)合的實(shí)施細(xì)節(jié)以及R工作室的G-buffer layout,以及后面稱之為MSAA Trick的優(yōu)化方案。

R工作室的遮擋剔除方案跟AC此前的方案有所不同,這種新的方案稱之為Unity剔除系統(tǒng)。需要注意的是,R工作室的核心工作大多跟UGC(user generated content) 有關(guān),因此會(huì)需要著重考慮多種不同技術(shù)方案的適配與篩選。此外,R工作的陰影貼圖管線也跟前面不一樣,其主要原理跟VT系統(tǒng)類似。

R工作室使用VT方案已經(jīng)有5年了,到目前為止已經(jīng)有兩款成功的產(chǎn)品應(yīng)用了這項(xiàng)技術(shù):,Trials Evolution and Trials Fusion

其中的核心思想是指將可見的貼圖數(shù)據(jù)維持在內(nèi)存中,主要通過將貼圖拆分成不同的小塊(tile)來實(shí)現(xiàn),這些小塊稱之為page。

Page ID會(huì)提供Page所需要的精確信息以及每個(gè)像素所需要的mip級(jí)別,ID數(shù)據(jù)會(huì)被加載到一個(gè)常數(shù)尺寸的Cache中(按照LRU算法進(jìn)行更新置換)

R工作室的實(shí)現(xiàn)是基于256k^2虛擬地址空間實(shí)現(xiàn)的,所有的貼圖數(shù)據(jù)都會(huì)被適配到這個(gè)atlas中,這個(gè)atlas會(huì)被分割成128x128分辨率的page。

在運(yùn)行時(shí),有一張8k的貼圖atlas(7680×4320?)用于存儲(chǔ)當(dāng)前需要駐守在內(nèi)存中的page數(shù)據(jù)。整個(gè)貼圖cache一共包含了5個(gè)數(shù)組元素(array slice)用于存儲(chǔ)所有需要的材質(zhì)屬性:基色,高光,粗糙度,法線等,貼圖cache采用的是DXT壓縮。

R工作室跟Montreal工作室的GPU驅(qū)動(dòng)渲染管線的最大區(qū)別就在于VT的使用。

VT跟GPU驅(qū)動(dòng)渲染管線能夠?qū)崿F(xiàn)完美契合,可以通過單張貼圖binding來實(shí)現(xiàn)所有的可見貼圖數(shù)據(jù)的訪問。

對(duì)于GPU驅(qū)動(dòng)的渲染管線而言,這項(xiàng)特性非常重要,因?yàn)榭梢灾挥靡粋€(gè)DP就允許GPU從任意張數(shù)的貼圖中讀取數(shù)據(jù)。也就是說,通過這項(xiàng)技術(shù),物件的渲染就不需要進(jìn)行合批了。

前面說過,GPU驅(qū)動(dòng)渲染管線會(huì)一次性取得所有的mesh數(shù)據(jù),而通過VT技術(shù),則可以一次性取得所有的貼圖數(shù)據(jù),也就是說,只用一個(gè)DP就可以完成全場景物件的繪制。

通過shader分支,可以實(shí)現(xiàn)不同的頂點(diǎn)動(dòng)畫效果,在現(xiàn)代GPU上,shader分支的執(zhí)行效率很高,通過測試,使用shader分支實(shí)現(xiàn)三種不同的復(fù)雜動(dòng)畫類型(包括蒙皮動(dòng)畫)只有不超過2%的額外消耗。

使用一個(gè)DP繪制所有物體的做法有很多優(yōu)點(diǎn):比如說可以將整個(gè)場景的物件cluster按照深度來排序,之后以cluster為基本單元按照從前往后的順序進(jìn)行繪制。這種做法可以提供類似于early-z(depth prepass)的overdraw優(yōu)化效果,且不需要一個(gè)額外的渲染pass

相對(duì)于同類解決方案比如說bindless textures,VT還有一些其他的優(yōu)點(diǎn):

1.可以將復(fù)雜材質(zhì)混合的結(jié)果以及貼花渲染結(jié)果存儲(chǔ)到VT atlas中

2.Atlas Cache可以將高帶寬消耗的操作均攤到多幀完成

(這些優(yōu)點(diǎn)對(duì)于場景編輯的同學(xué)來說是非常有幫助的,可以使用較低的消耗來得到非常豐富的表現(xiàn)效果。)

3.使用VT技術(shù),可以不管實(shí)際上有多少張貼圖,最終繪制所需要的貼圖在內(nèi)存中的尺寸是恒定不變的:美術(shù)同學(xué)就不需要關(guān)注貼圖內(nèi)存預(yù)算,將精力專注在效果上。

Deferred Texturing并不是一項(xiàng)新技術(shù),最早可以追溯到2007年的一篇論壇文章. 不過在此之前,貌似并沒有哪款游戲有使用過這項(xiàng)技術(shù),其原因可能是無法實(shí)現(xiàn)高效而魯棒的貼圖像素?cái)?shù)據(jù)的存儲(chǔ)與讀取。

Nathan Reed上一年的博客對(duì)此問題給出了一個(gè)解決方案,不過假設(shè)只考慮一些重要的特性比如說各向異性采樣的話,這個(gè)方案會(huì)將G-Buffer的存儲(chǔ)數(shù)據(jù)量增加到144bits,成本還是有點(diǎn)高。

而這里的GPU渲染管線的一項(xiàng)重要特性就是,所有可能可見的貼圖數(shù)據(jù)都被加載到一張8k的貼圖atlas中了。這張atlas對(duì)應(yīng)的UV坐標(biāo)足以實(shí)現(xiàn)對(duì)任意可見的像素的讀寫。這張atlas的dimension尺寸相對(duì)于巨大的256k virtual texture而言,可以算是非常小的,從而可以只使用16+16bit的UV坐標(biāo)就能實(shí)現(xiàn)對(duì)任意像素的訪問,在這個(gè)情況下,texture filtering可以達(dá)到8x8的subpixel精度,而這個(gè)數(shù)值實(shí)際上已經(jīng)可以得到非常不錯(cuò)的顯示質(zhì)量了。

場景的渲染,只有UV以及depth數(shù)據(jù)是不夠的,還需要考慮各向異性采樣的梯度數(shù)據(jù)以及光照計(jì)算所需要的tangent數(shù)據(jù)

梯度數(shù)據(jù)會(huì)占用不小的空間,因此這里的做法不存儲(chǔ)這項(xiàng)數(shù)據(jù)。幸運(yùn)的是,由于G-Buffer中包含了UV坐標(biāo)數(shù)據(jù),因此可以在屏幕空間中完成對(duì)梯度的計(jì)算。對(duì)于連續(xù)的表面而言,這種計(jì)算方法得到的效果是沒什么問題的,不過對(duì)于那些在深度上不連續(xù)的情況表現(xiàn)就不太好了。為了解決這個(gè)問題,這里給出的方案是,比較從正負(fù)xy軸上的相鄰像素?cái)?shù)據(jù),并取其中UV坐標(biāo)差異小的一個(gè)作為輸出,在這個(gè)計(jì)算原則上,相同表面的相鄰像素可以得到較高的優(yōu)先級(jí)。

如果選擇的相鄰像素的UV距離超過了設(shè)定的N像素閾值(其中N指的是各向異性采樣的等級(jí)),就認(rèn)為此次搜索失效。在這種情況下,會(huì)將算法回退到雙邊線性濾波。實(shí)際上這種情況在幾何物體比較纖細(xì)的時(shí)候發(fā)生的頻率還挺高的,不過從實(shí)際表現(xiàn)上來看,并沒有構(gòu)成很大問題,可能是因?yàn)殡p邊線性濾波導(dǎo)致subpixel異常在濾波的過程中被模糊掉了。

光照計(jì)算所需的tangent數(shù)據(jù)會(huì)以32位歸一化的四元數(shù)(quaternion)的形式存儲(chǔ),其中2位的alpha通道用于存儲(chǔ)主軸索引(major axis index)。VT技術(shù)可以免費(fèi)讓我們得到每個(gè)page的屬性數(shù)據(jù),比如mip等級(jí),材質(zhì)ID以及colorize color,這些數(shù)據(jù)不需要存儲(chǔ)到G-Buffer中。而Page索引可以通過UV除上128來得到。

總結(jié):

每個(gè)像素64bits,在現(xiàn)代GPU上比如GCN可以得到Full fill rate,不需要使用MRT(multiple RT)

Deferred texturing技術(shù)跟VT技術(shù)可以很好的結(jié)合起來,UV buffer數(shù)據(jù)可以當(dāng)成page ID來使用。如果部分像素的梯度向量長度低于0.5,就表示此時(shí)需要加載更高更清晰的貼圖page了。

這里是實(shí)施方案的一些截圖,可以看到梯度重建質(zhì)量跟ground truth非常接近了。

這里還為deferred texturing+VT結(jié)合的方案(命名為virtual deferred texturing,簡稱VDT)做了一些優(yōu)化工作,這些優(yōu)化統(tǒng)稱為MSAA trick。

這個(gè)trick是基于以下觀察結(jié)果得到的:不管是UV坐標(biāo),還是tangent數(shù)據(jù),都是可以通過頂點(diǎn)數(shù)據(jù)插值得到,且這種插值是無損的。因此可以將G-buffer數(shù)據(jù)用一個(gè)低分辨率存儲(chǔ)下來,之后在使用的時(shí)候?qū)θ笔У臄?shù)據(jù)進(jìn)行插值求取即可。

使用MSAA繪制一個(gè)2x2的低分辨率結(jié)果,之后使用GCN可編程采樣pattern來構(gòu)建一個(gè)有序的網(wǎng)格,這個(gè)網(wǎng)格正好與高分辨率的結(jié)果貼圖的像素中心相匹配。

在進(jìn)行光照計(jì)算的時(shí)候,會(huì)通過multisample加載指令加載G-Buffer中任意的MSAA樣本數(shù)據(jù)

在lighting計(jì)算compute shader開始之前,會(huì)構(gòu)建一張1080p的G-buffer貼圖,并使用快速的LDS(這個(gè)是啥?Local Data Store)來存儲(chǔ)這張臨時(shí)貼圖。

MSAA會(huì)通過硬件完成PS計(jì)算,被多個(gè)三角面片覆蓋的像素,其結(jié)果會(huì)被存儲(chǔ)多次,這種做法是有必要的,因?yàn)榭梢员WC三角形edge兩邊的sample frequency數(shù)據(jù)是有效的,從而可以按照屏幕分辨率(native resolution)實(shí)現(xiàn)對(duì)edge的重建。

由于MSAA已經(jīng)可以兼顧triangle edge效果了,這里只需要完成三角形內(nèi)部的插值計(jì)算就可以實(shí)現(xiàn)1080p的UV坐標(biāo)以及tangent數(shù)據(jù)的重建。不過由于插值并不是perspective correct,因此相對(duì)于直接按照屏幕分辨率渲染的結(jié)果而言,可能會(huì)有一些輕微的差異。

Benchmark使用的是128bits/pixel的G-buffer格式,并將四個(gè)2xMSAA像素編碼成一個(gè)8xMSAA像素,其最終質(zhì)量與2xMSAA比較接近。

PS waves的數(shù)量削減達(dá)到一半,而渲染時(shí)間消耗削減達(dá)到30%,此外DRAM訪問量也有所削減,這是因?yàn)檫@里給出的算法可以將訪問最多的MSAA subsample plane數(shù)據(jù)遷移到ESRAM中(不太明白是啥意思,硬件改動(dòng)嗎?)。

接下來介紹下R工作室的遮擋剔除實(shí)現(xiàn)技術(shù),其方案跟ACU的有所不同。

最大的區(qū)別在于,R工作室的遮擋剔除方案不需要一個(gè)額外的遮擋物體pass(occlusion geometry pass),這是因?yàn)镽工作室這邊需要逐像素精確的遮擋結(jié)果,因此粗糙的遮擋剔除結(jié)果并沒有什么意義。

R工作室這邊的項(xiàng)目通常沒有比較大的結(jié)構(gòu)性物體,場景中的大物體通常都是玩家用小物體搭建出來的,比如說一堵墻可能是由若干個(gè)細(xì)小物體搭建而成,中間可能會(huì)包括若干孔洞,如果使用一些低模物體來構(gòu)建遮擋幾何體的話,最終的表現(xiàn)可能比較差,且玩家也無法理解為什么會(huì)這樣。

因此R工作室的遮擋剔除數(shù)據(jù)是基于G-Buffer的深度數(shù)據(jù)計(jì)算得到的,從深度buffer生成一個(gè)深度金字塔(pyramid),按照GCN HTILE min/max深度buffer方式生成,這種做法可以使得金字塔的生成算法比普通的全分辨率遞歸下采樣算法快12倍。

而遮擋剔除測試則是使用一個(gè)gather4采樣指令一次性獲得四個(gè)采樣結(jié)果的方法進(jìn)行的。

整個(gè)culling跟rendering可以分成兩個(gè)階段:

第一個(gè)階段使用上一幀創(chuàng)建的深度金字塔數(shù)據(jù)實(shí)現(xiàn)視椎剔除與遮擋剔除,這個(gè)階段可以看成是一個(gè)occlusion hint(什么意思?可以看成是一輪預(yù)篩選)。對(duì)于通過這個(gè)檢測的物體,會(huì)以cluster作為基本粒度進(jìn)行視椎,back-face以及遮擋測試,并將cluster ids輸出到渲染所需要的buffer中。

在第二個(gè)階段,會(huì)先使用最新繪制完成的partial frame(是部分物體深度結(jié)果嗎)對(duì)深度金字塔進(jìn)行更新。對(duì)于所有通過其他測試但是沒通過遮擋測試的物體以及cluster,會(huì)再進(jìn)行一次遮擋測試,將那些之前檢測錯(cuò)誤的物體添加到渲染列表中(為啥不一步到位,直接在這里做遮擋檢測呢?因?yàn)檫@里遮擋檢測的遮擋物來源于當(dāng)前幀已經(jīng)繪制的物體,因此需要先繪制一遍,而繪制的數(shù)據(jù)來源于第一階段的檢測結(jié)果)。

如果不使用GPU驅(qū)動(dòng)的渲染管線的話,這種剔除方法是很難做到的,因?yàn)樾枰谥暗牟襟E中以低時(shí)延拿到GPU生成的數(shù)據(jù),如果中間夾雜了CPU的數(shù)據(jù)訪問就會(huì)嚴(yán)重阻塞GPU的工作流。

為了測試剔除效果,這里做了一個(gè)壓力測試,在整個(gè)場景中渲染了二十五萬個(gè)移動(dòng)物體,對(duì)于現(xiàn)存的粗糙遮擋剔除系統(tǒng)而言,這些分散的大量物體絕對(duì)是個(gè)噩夢。

這個(gè)Bencmark使用的是DX渲染路徑,每個(gè)cluster使用64個(gè)頂點(diǎn)組成。對(duì)于這么高面片場景而言,使用MultiDrawIndirect 接口可能沒什么收益。

在光照shader中,物體的貼圖用VT來實(shí)現(xiàn),整個(gè)場景的繪制只需要兩個(gè)DP。

從結(jié)果上可以看到,GPU驅(qū)動(dòng)的剔除以及setup過程消耗很低,加起來不超過0.5ms。需要注意的是,這些時(shí)間并不是損耗了的GPU時(shí)間,以cluster為粒度的剔除算法在G-buffer步驟中省下來的時(shí)間足以彌補(bǔ)GPU驅(qū)動(dòng)的管線的所有消耗。

在R工作室此前的游戲比如Trials Fusion中,這里列舉的這些工作會(huì)占據(jù)大概50%的CPU時(shí)間,而現(xiàn)在只需要單核GPU 0.2ms的時(shí)間就能完成,因此用這種方法可以騰出CPU給游戲邏輯,物理仿真,破壞等,以創(chuàng)建更豐富生動(dòng)的游戲表現(xiàn)。

對(duì)于陰影渲染,R工作室這邊選取的方法跟此前Montreal給出的方法有所不同。

2001年的時(shí)候,F(xiàn)ernando給出了一種稱之為Adaptive Shadow Maps的實(shí)現(xiàn)技術(shù)這種技術(shù)將陰影貼圖分割成細(xì)小的tiles,之后根據(jù)距離的不同為每個(gè)tile選定不同的分辨率

這種技術(shù)的問題在于,需要在渲染的過程中將深度數(shù)據(jù)從GPU傳送到CPU,在CPU中,會(huì)對(duì)深度數(shù)據(jù)進(jìn)行分析,并更新culling structures,之后據(jù)此進(jìn)行shadow rendering,這個(gè)過程會(huì)極大的阻塞工作流。

而如果使用GPU驅(qū)動(dòng)的剔除算法,就可以借助類似于VT的virtual shadow mapping方法解決這個(gè)問題,因?yàn)樵谶@個(gè)過程中將不再需要CPU的參與,且能夠移除shadow map tile page邊緣上的頂點(diǎn)處理消耗。DX12允許在VS中訪問RT array index,從而可以不使用geometry shader,也可以一次性渲染所有的shadow pages。

Virtual Shadow Mapping方案輸出的貼圖結(jié)果具有最小數(shù)量的under- & oversampling,Shadow map的分辨率將在任意區(qū)域都能夠與屏幕分辨率實(shí)現(xiàn)最佳匹配。

最終的性能表現(xiàn)令人非常滿意,尤其是在復(fù)雜場景中。在之前給出的25萬面片的超復(fù)雜場景中,測試的結(jié)果顯示相對(duì)于SDSM(標(biāo)準(zhǔn)shadow map方法)而言,這種方法具有3.5倍的優(yōu)勢(怎么定義的)。

至于后面更進(jìn)一步的優(yōu)化,比如XOR hashing頁面的數(shù)據(jù)重用等,還能夠進(jìn)一步提升此方案的運(yùn)行速度。

DX12提供了很多新的特性,而其他的API在PC上也有一些重要的特性,除此之外,相對(duì)于下一代主機(jī),還有一些特性是DX以及其他PC API沒有提供的,不過在DX 12_3中將會(huì)增加這些特性。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。