ETH官方钱包

創(chuàng)作內(nèi)容

10 GP

Unheard Engine: 兩個(gè)月的Vulkan學(xué)習(xí)之旅

作者:巧克力喬斯達(dá)│2022-11-21 04:30:26│巴幣:1,216│人氣:748

繼D3D12之後另一個(gè)更重大的學(xué)習(xí),但對(duì)於之前D3D12 Demo感到不是很滿意,因?yàn)槟莻€(gè)是一個(gè)依賴於Unity的C++ DLL插件,程式架構(gòu)根本一蹋糊塗,還依賴了Unity的Editor傳了些資料

現(xiàn)在該是邁向下一個(gè)境界的時(shí)候了! 遂決定從0開始寫一個(gè)Vulkan小引擎,由於是從0開始,其實(shí)我花了更多時(shí)間來製作High-Level的類別們:


這會(huì)使得未來要擴(kuò)充功能時(shí)更加順利
關(guān)於Unheard引擎的完整篇幅可參閱我在Gamedev.net的發(fā)文 (這次意識(shí)到GameDev的介面屌打WordPress XD)

由於實(shí)在過長(zhǎng),我這邊就只轉(zhuǎn)一些過來了
如果是Vulkan新手,建議從這個(gè)教學(xué)網(wǎng)站開始,你能夠真正體驗(yàn)到何謂"寫了千行程式碼就只為了畫一個(gè)三角形" XD

環(huán)境
● IDE: Visual Studio 2022
● C++ Version: C++17
● Vulkan SDK version: 1.3.224.1
● FBX SDK version: 2020.0.1
● Windows SDK Version: 10.0.20348.0
● Platform Toolset: v143
● DXC Compiler: 2022_07_18 release on their GitHub
IDE超想用Rider...但那是公司授權(quán)的IDE,我不能公器私用

引擎組合是Vulkan + DirectXMath + Win32 Window + HLSL shaders + WICTextureLoader + FBX Importer,和Vulkan教學(xué)網(wǎng)站的組合大庭相勁 (他們用了GLFW + GLM + Obj loader + GLSL),我想要真正的融會(huì)貫通並以自己的方式實(shí)現(xiàn),所以就沒有用GL相關(guān)的東西了
如果未來要跨平臺(tái)再考慮,現(xiàn)在就算寫了我也沒windows以外的系統(tǒng)能測(cè)試

我是在筆電上開發(fā)的,I7 11375H + RTX 3060 Laptop

功能
目前非常的單純,分成Debug/Release build,有些東西只有Debug Build會(huì)有,像是設(shè)定視窗等等


素材匯入
執(zhí)行時(shí),UHE會(huì)自動(dòng)把RawAssets/底下的素材全部匯入(支援的話)
並且有著快取機(jī)制,如果RawAssets下的素材已經(jīng)匯入過,下次啟動(dòng)程式不會(huì)再匯入
讀FBX跟貼圖真的很慢!有了快取就可以避免每次都要等一下才看到渲染的窘境
當(dāng)然未來如果實(shí)作了編輯器,就可以拿掉自動(dòng)匯入了

貼圖: 僅支援WICTextureLoader支援的格式,所以像dds那種可以直接存cube map資料我是不會(huì)匯入的 (而且dds檔案也很舊了),要建立Cubemap一定要使用6張2D貼圖

模型: 僅支援FBX的匯入,有條件地忽略一些Node (例如不是Triangle Geometry),多重材質(zhì)目前也暫不支援,引擎很簡(jiǎn)單的一個(gè)Renderer只能帶一個(gè)材質(zhì)跟網(wǎng)格

Shader編譯
DirectXShaderCompiler絕對(duì)是最大功臣! 他們實(shí)作了HLSL轉(zhuǎn)Spir-V (Vulkan的Shader語言)的功能,讓我省去不少時(shí)間,我可以把Shader實(shí)作維持在HLSL上 (同時(shí)也是為了DXR光追)
由於過程中會(huì)在Shader裡面用到macro定義,那麼Shader變體就是少不了的
我有實(shí)作簡(jiǎn)單Shader變體機(jī)制,不同變體會(huì)編譯成不同檔案


渲染流程
由於是從0開始的小引擎,目前的流程還很單純,不大,但從物件渲染、後製效果、甚至光追都有涵蓋到
所以不要問我: 為什麼沒有半透明物件,為什麼光追只有陰影,為什麼沒有陰影Pass等等,我只是還沒實(shí)作他們罷了,一些理所當(dāng)然存在於引擎的東西,都是必須花時(shí)間去製作的

另外,目前系統(tǒng)沒有任何Culling以及Draw Call Batching,還有很大的最佳化空間
目前沒有最佳化也讓我比較好Debug (藉口)
渲染上也因?yàn)樵谏疃菳uffer使用了Reversed Infinite Z,所以已經(jīng)下Draw Call的物件怎麼樣都不會(huì)被剔除,未來要好好在CPU做最佳化才行

效能
Debug / Release Build的效能真的相去甚遠(yuǎn)
因?yàn)镈ebug開了Vulkan layer validation,以及使用了GPU Labeling,所以我能夠透過RenderDoc之類的工具來做GPU Debug (但是光追啟動(dòng)時(shí),RenderDoc又不能用了T_T)
目前Release build,沒開Vsync的話可以到300多FPS

關(guān)於Vulkan物件
所有初始化、釋放等等那些底層的API呼叫基本上都包在High-Level設(shè)計(jì)裡了,像這樣:

如果是為了學(xué)Vulkan想追UHE的程式碼的話,我大概vk的函式用最多的地方就Graphic, 跟Graphic Builder這兩個(gè)了

Pool Management
在UHE,我會(huì)盡可能地重複利用物件,像是目前測(cè)試場(chǎng)景其實(shí)有747個(gè)renderers (747個(gè)draw call),但我的Graphic State只建立了16個(gè) (包含光追以及非物件pass)、然後SamplerState只建立了5個(gè),大多都是共用的
這對(duì)效能的幫助可說是非常巨大的

Base Pass Rendering
要點(diǎn)在於如何建立MRT (Multiple Render Target)
基本上就是建立複數(shù)個(gè)VkAttachmentDescription以及VkAttachmentReference結(jié)構(gòu)
UHE目前使用了5個(gè)GBuffer:
● Scene Diffuse: R8G8B8A8_SRGB, 就顏色, A通道拿來存Occlusion參數(shù)
● Scene Normal: R10G10B10A2_UNORM, 法向量, A目前沒用
● Scene PBR: R8G8B8A8_UNORM, Specular跟Rougness資訊
● Scene Result: R16G16B16A16_SFLOAT, 渲染結(jié)果, emissive跟indirect specular結(jié)果也會(huì)存在這, 但目前這個(gè)維京小屋場(chǎng)景沒有螢光物件就是了
● Scene Mip: R16_SFLOAT, 比較特別的GBuffer,它存的是UV的ddx/ddy結(jié)果,所以我可以在Pixel Shader以外的地方計(jì)算mip map level,例如Compute Shader或Ray Tracing Shader, 這種只能用SampleLevel()不能用Sample(), 必須自己算mip map的場(chǎng)合

Ray Tracing Shadow
是能夠支援多光源陰影的,會(huì)像下圖一樣混合起來:

這邊是故意放了兩道Directional Light展示的
為了知道光追擊中後的物件資訊,我在光追Shader定義了幾個(gè)所謂的Descriptor Array
Texture2D UHTextureTable[] : register(t0, space1);SamplerState UHSamplerTable[] : register(t0, space2);
StructuredBuffer<VertexInput> UHVertexTable[] : register(t0, space3);ByteAddressBuffer UHIndicesTable[] : register(t0, space4);

對(duì),把Texture2D, SamplerState, StructureBuffer<>, ByteAddressBuffer當(dāng)成陣列綁定在D3D12/Vulkan是做得到的! 這讓資料的取得順利多了,因?yàn)镈XR系統(tǒng)有著InstanceID()、InstanceIndex()等等資訊可以利用

Light Pass Rendering
這裡就是結(jié)合Base Pass的GBuffer,來做燈光計(jì)算的地方了
目前UHE僅實(shí)作了Directional Light~

取樣陰影時(shí)我施加了PCSS過濾,所以距離遮擋物較近的陰影會(huì)比較銳利,而距離遠(yuǎn)的會(huì)比較柔和:

Sky Pass Rendering
沒啥好說,就只是畫一個(gè)Skybox罷了

Motion Vector Rendering
由於UHE實(shí)作了TAA這個(gè)抗鋸齒,移動(dòng)向量是必須用來解決一些問題的
分成兩個(gè)Pass
Camera Motion: 基於深度值來建立移動(dòng)向量,適合靜態(tài)物件
Object Motion: 針對(duì)會(huì)移動(dòng)的物件,來輸出移動(dòng)向量

沒有這一步,TAA可是會(huì)出現(xiàn)鬼影的,另外也不能做Motion Blur (雖然我還沒做)

Post Process Rendering
第一是Tone mapping, 我採(cǎi)用了Stephen Hill (@self_shadow)的ACES曲線
簡(jiǎn)單來說這位傳說他把ACES過程的所有轉(zhuǎn)換整體成只要兩個(gè)矩陣就能完成
也就是把目前的顏色值float4,簡(jiǎn)單乘一下這兩個(gè)矩陣,就差不多做完了

Tone mapping前後比對(duì):

再來是Temporal AA,也是近代引擎的抗鋸齒常客了,原理是取樣前一張F(tuán)rame的結(jié)果來達(dá)到抗鋸齒的目的,乍聽之下很單純但其實(shí)也有一些問題要處理
  • Ghosting: 鬼影問題,在取樣時(shí)給UV加上Motion Vector就能解決
  • Disocclusion: 在前一個(gè)Frame被遮住的物件,突然在這個(gè)Frame出現(xiàn),也會(huì)導(dǎo)致TAA出現(xiàn)鬼影,我採(cǎi)用Motion Rejection來解決這個(gè)問題,當(dāng)目前這個(gè)Frame的移動(dòng)向量與前一個(gè)Frame的移動(dòng)向量差距過大,我就不會(huì)進(jìn)行取樣
  • Missing Depth: 畫面上的一些地方是沒有深度的,例如Skybox的部分,那Skybox那些像素自然就沒有移動(dòng)向量,也會(huì)導(dǎo)致TAA出問題,我簡(jiǎn)單使用一個(gè)3x3的遮罩來算max depth, 在此遮罩後, 要是還是沒有深度值, 我一樣不取樣
如此一來,鬼影問題就解決了,TAA的比較:

不過TAA本身可能也會(huì)有不足的地方,一般而言還會(huì)結(jié)合FXAA,這個(gè)等之後再做吧

Swap Chain Present
最後就是真正將渲染結(jié)果給Present到Swap Chain上了!
在UHE裡,其實(shí)Swap Chain跟渲染解析度是分開的,這是為了全螢?zāi)荒J?/div>
我個(gè)人發(fā)現(xiàn)vkAcquireFullScreenExclusiveModeEXT的運(yùn)作方式跟IDXGISwapChain::SetFullscreenState()大有不同! 後者會(huì)自動(dòng)幫你弄成全螢?zāi)?/div>
但Vulkan的vkAcquireFullScreenExclusiveModeEXT其實(shí)只是獲得"全螢?zāi)华?dú)佔(zhàn)"而已
視窗的尺寸調(diào)整要自己來,所以在UHE,我會(huì)在切換時(shí)先讓視窗變得跟桌面尺寸一樣,再呼叫
vkAcquireFullScreenExclusiveModeEXT獨(dú)佔(zhàn),所以這個(gè)全螢?zāi)黄鋵?shí)更像"無痕視窗全螢?zāi)?quot;
運(yùn)作得跟DirectX App實(shí)在很不一樣,這就是我決定分開Swap Chain / 渲染解析度的原因了

結(jié)語
終於打完了==

個(gè)人對(duì)於一些Vulkan的想法
1. API設(shè)計(jì)比DirectX12更加冗長(zhǎng),尤其是物件管理,幾乎每個(gè)Vulkan物件都要vkCreateXXXX, vkDestroyXXXX. 但是D3D12的介面幾乎都是繼承IUnknown,它是一個(gè)COM介面,必須實(shí)作Release函式,讓物件的管理相對(duì)簡(jiǎn)潔許多

2. Vulkan似乎沒有Thread safe的command queue! 還有VkCommandBuffer也不能在執(zhí)行後馬上Reset並重複利用. D3D12的Command Queue則是Thread Safe的、Command List也能在執(zhí)行後馬上Reset再重新錄製指令,這對(duì)平行化的工作指派設(shè)計(jì)可是會(huì)有著重大的影響

3. Vulkan沒有所謂的Local Descriptors, 只有Global Descriptor能用

4. 整體來說,Vulkan幾乎跟D3D12一樣強(qiáng)大! 它提供了開發(fā)者所有必要的東西,我猜如果是在Linux平臺(tái),Vulkan表現(xiàn)會(huì)更好

GitHub連結(jié) (只有程式碼):
https://github.com/EasyJellySniper/Unheard-Engine

完整專案連結(jié) (包含測(cè)試素材):

十分建議有興趣的人直接載完整專案
跑GitHub可能會(huì)因?yàn)槿彼夭亩霈F(xiàn)一些BUG,也不會(huì)有任何東西被畫出來
引用網(wǎng)址:http://www.jamesdambrosio.com/TrackBack.php?sn=5605370
All rights reserved. 版權(quán)所有,保留一切權(quán)利

相關(guān)創(chuàng)作

留言共 0 篇留言

我要留言提醒:您尚未登入,請(qǐng)先登入再留言

10喜歡★ltes50414 可決定是否刪除您的留言,請(qǐng)勿發(fā)表違反站規(guī)文字。

前一篇:UE4.27 - 光追衍... 後一篇:Unheard Engi...


face基於日前微軟官方表示 Internet Explorer 不再支援新的網(wǎng)路標(biāo)準(zhǔn),可能無法使用新的應(yīng)用程式來呈現(xiàn)網(wǎng)站內(nèi)容,在瀏覽器支援度及網(wǎng)站安全性的雙重考量下,為了讓巴友們有更好的使用體驗(yàn),巴哈姆特即將於 2019年9月2日 停止支援 Internet Explorer 瀏覽器的頁面呈現(xiàn)和功能。
屆時(shí)建議您使用下述瀏覽器來瀏覽巴哈姆特:
。Google Chrome(推薦)
。Mozilla Firefox
。Microsoft Edge(Windows10以上的作業(yè)系統(tǒng)版本才可使用)

face我們了解您不想看到廣告的心情? 若您願(yuàn)意支持巴哈姆特永續(xù)經(jīng)營(yíng),請(qǐng)將 gamer.com.tw 加入廣告阻擋工具的白名單中,謝謝 !【教學(xué)】