Unity 和 Unreal 作為流行的商業引擎,引擎這個東西,最主要的特點就是抽象。其中關于Shader部分,他們都做了比較好的抽象,使用者不需關注每個平台的shader不同,寫一次,build各個平台。
這是當今遊戲引擎的一個重要标配,Unity 和 Unreal 都自己做了大量的工作,網上也有人分析過Unity和Unreal各自的方案。
替代方案
現如今,開源世界也有一些方案來處理這個問題了
主要是兩條線
1.DirectXShaderCompiler + spirvcross
2.glsllang +spirvcross
方案一,DirectXShaderCompiler 是微軟開源的hlsl編譯器,最新的版本增加了一個編譯開關,重編譯後可擷取将hlsl 編譯為 spirv 的能力,然後通過spirvcross項目編譯成各種各樣的東西
方案二,glsllang 是 Khronos 官方的 for vulkan 的 glsl編譯器,他負責将glsl編譯為spirv,然後通過spirvcross項目編譯成各種各樣的東西
這兩個方案使用的編寫語言分别是hlsl 和 glsl(方案二其實也支援hlsl,但是資料比較少,我還沒有實驗出來),都是用sprivcross來跨平台
這裡就要解釋一下什麼是spirv
爺爺Khronos: 是圖形标準化組織,OPENGL就是他們制定的,然後在微軟搞dx12,蘋果搞metal的時候,他們也推出了新一代圖形API标準,就是Vulkan。
爹Vulkan:大伯OPENGL,二伯OPENGL ES
兒子spirv:而vulkan使用的shader二進制格式就是spirv
因為opengl 和opengles 的時代,都沒有發展二進制格式,爺爺制定标準的時候就想把他打造成一個通用的格式,是以spirv實際上是個很通用的執行格式(想象一下c#背後的IL)
而spirvcross也是爺爺開發的一個反編譯項目,專門把spirv反編譯成各種shader源檔案
https://github.com/KhronosGroup/SPIRV-Cross
官網上說他的功能是,(反編譯spirv)生成可讀的GLSL,蘋果MSL,HLSL,c++,json好了,天下大同了
- Convert SPIR-V to readable, usable and efficient GLSL
- Convert SPIR-V to readable, usable and efficient Metal Shading Language (MSL)
- Convert SPIR-V to readable, usable and efficient HLSL
- Convert SPIR-V to debuggable C++ [DEPRECATED]
- Convert SPIR-V to a JSON reflection format [EXPERIMENTAL]
廢話不多說,咱們試試
實踐方案一
DirectXShaderCompiler 還要自己重編譯,麻煩(其實是我沒編譯通過…build.py寫的太爛了,辣雞微軟)
好在微軟直接給了個整合好的方案一
ShaderConductor
https://github.com/microsoft/ShaderConductor
根據文檔指導,直接下個最新的預發行版本就好
https://dev.azure.com/msft-ShaderConductor/public/_build/results?buildId=205&view=artifacts&type=publishedArtifacts
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIyZuBnL4gDN0UTO3YzNtAzM0AjM2ITNxQjM3ADMyAjMtIDN2QDOx8CX3ADMyAjMvwlM0YDN4EzLcd2bsJ2Lc12bj5ycn9Gbi52YuAjMwIzZtl2Lc9CX6MHc0RHaiojIsJye.png)
下份windowsx64的回來,其它平台的我沒測試,都是指令行工具,大差不差。
寫一個test.hlsl
然後執行指令行來編譯成glsl
ShaderConductorCmd –I test.hlsl –E vs_main –S vs –T glsl –V 200
一長串的參數
-I test.hlsl 輸入檔案
-E vs_main 要編譯的函數
-S vs 編譯那種shader
-T glsl 輸出為glsl格式
-V 110 glsl版本号 ,glsl版本号
副GLSL版本号表
2.0 110
2.1 120
3.0 130
3.1 140
3.2 150
3.3 330
4.0 400
4.1 410
4.2 420
4.3 430
4.5 450
産生的ios_matal檔案
産生的glsl檔案
問題,因為這個方案裡的DXshadercompiler 是dx11的,是以這有個問題
我單獨寫的uniform matView也被放進了cbuffer裡,觀看glsl就會發現有了語義變化,這就像卡了一根刺。
實驗方案二
下載下傳glsllang
https://github.com/KhronosGroup/glslang
有release下載下傳
https://github.com/KhronosGroup/glslang/releases
也下載下傳個x64的
下載下傳spirvcross
也有release下載下傳
https://github.com/KhronosGroup/SPIRV-Cross/releases
解壓出來就能用
這次寫glsl
glsllang 預設使用檔案擴充名識别功能
vertexshader就要.vert結尾
調用上述指令編譯test.vert 和 test.frag 分别是 vertexshader 和 fragmentshader
分别得到frag.spv 和 vert.spv檔案
這就是spirv的二進制格式shader
然後調用spirvcross 可以反編譯他們到不同的shader
沒有參數就反編譯為glsl
加--hlsl就反編譯為hlsl
加--msl就反編譯為蘋果metal
還有一堆參數就得你自己研究了
生成的hlsl比較啰嗦
生成的glsl就比較幹爽了
最後說兩句
目前我想要做的備胎,第一服務對象是Android(gl +vulkan)
第二才考慮 IOS \PC \web 這些
方案二明顯更加合适,使用glsl作為基準,對gl和vulkan環境支援良好,在第二考慮時才需要spirvcross上馬,可以支援dx 和 metal。
原理通了,接下去就是整合了。