在unity的資源中,shader是比較特殊的一類。主要有下面幾個疑問html
1. Shader算是代碼,而且須要編譯。那麼是否能夠熱更新?android
2. AB中加載進來的shader是否能夠經過shader.Find(名稱)來索引?服務器
3.在使用shader_feature關鍵字後,build時忽略的變種是否要在運行時編譯?函數
4.預編譯shader cache的存儲位置究竟在哪裏?測試
直接補充最終結論:ui
1. shader能夠熱更新。spa
2. 使用multi_compile生成Shader Variant時,材質能夠直接熱更新。3d
3. 使用shader_feature生成Shader Variant時,可使用ShaderVariantCollection來記錄全部使用到的變種,實現材質熱更新。(目前仍有bug,必須將shader、SVC和全部材質放在一個AB中)htm
4. 不要用Shader.Find找本身包裏的shader,使用AssetBundle.LoadAsset<Shader>()blog
5. shadercache隨材質存儲,材質能夠熱更新。
針對以上問題我作了一系列測試,記錄以下:
測試一:
準備一個shader ,一個材質,一個cube作的prefab,各自打成一個AB。
在一個空場景中用腳本按以下順序加載:
shaderAB->materialAB->prefabAB->prefab->GameObject。顯示正常。
(事實上只要保證prefabAB->prefab->GameObject的順序,materialAB和shaderAB在同一函數的任意位置均可以。Unity應該是延遲處理了資源的引用關係)
修改shader,打成新的ab,更名或者另存爲備用。
發佈之後,在文件夾中找到對應的shaderAB,使用新的shaderAB替換。
從新啓動,效果已經更新。
結論一: shader能夠熱更新!
測試平臺:android和pc standalone
代碼稍做修改能夠在運行時實現熱更新。
測試二:
準備3個shader,引用同一個頭文件。shader和cginc所有進入一個ab裏。
運行時先加載shaderAB,而後用一個按鈕切換shader
結果以下表:
|
熱更新前 |
熱更新後 |
Shader.Find |
正常(原shader) |
錯誤(shader丟失)[1] |
AssetBundle.LoadAsset[2] |
正常(原shader) |
正常(新shader) |
[1] 在Standalone或者移動平臺上會有shader丟失;在Editor模式下會使用舊的shader,仔細分析後猜想是在Editor模式下,shader.Find的查找順序以下:已加載的 AssetBundle->Shader源文件。而在發佈平臺上,因爲沒有散的shader源文件,因此直接丟失。
[2] AssetBundle.LoadAsset的路徑要使用Manifest中記載的路徑,以下形式:
Assets/Shaders/Src/shaderTest2.shader
結論二:能夠在運行時手動替換上AB中的shader,但必須使用AssetBundle.LoadAsset!
·可使用cginc頭文件!
·可使用文件夾管理Shader!
·最好徹底不使用Shader.Find,除非你100%肯定這個shader不會熱更新。
關於Shader.Find,我的猜想以下:
Unity內部使用一個字典或者HashSet來支持Shader.Find,這裏暫且叫它ShaderMap。ShaderMap的鍵是ShaderLab語法中的名字;值是Shader文件的GUID。
ShaderMap生成於Build項目時,保存了來自三個地方的shader cache引用關係:
1. Resources中的shader或Resources其中其餘資源引用到的shader
2. 任意場景中引用到的shader
3. StreamingAssets中Asset Bundle內的Shader
運行時使用ShaderFind,只能找到這些Shader,若是對應GUID的shader不存在,查找就會失敗,即便熱更新後加入了新的Asset Bundle中含有同名Shader(即ShaderLab語法同名)。
4. 目前沒有辦法在發佈之後動態更新ShaderMap。
測試三:
準備兩個一樣的shader,設定好#ifdef FEATURE,其一使用multi_compile,其二使用shader_feature
準備四個材質,分別對應
·multi_compile FEATURE on
·multi_compile FEATURE off
·shader_feature FEATURE on
·shader_feature FEATURE off
全部shader打成一個ab, 全部material打成一個ab
在運行時切換4個材質。結果以下:
·multi_compile FEATURE off |
正常 |
·multi_compile FEATURE on |
正常 |
·shader_feature FEATURE off |
正常 |
·shader_feature FEATURE on |
錯誤(和shader_feature FEATURE off同樣) |
結論三:
·使用shader_feature的uber shader沒法熱更新!(結論已更新)
·若將shader存儲於自定義AB時,僅按照全部shader_feature都沒有定義的方式來編譯。而且不會彙報這個編譯過程當中的任何錯誤!(如:在shader中定義了shader_feature A B;而且依賴於A、B兩者任一必須定義的話,編譯就會出錯。)
·Unity並不會在發佈平臺上編譯缺失的變種。(直接拿個現有的變種湊數?)
測試四
放棄熱更新shader,檢查在使用shader_feature的時候,材質可否熱更新。即可否在熱更新時生成缺失的變種。
準備一個uber shader。再來3個材質,各使用不一樣的變種,並分別打成m1,m2,m3三個包。發佈時僅選擇m1發佈,而後在運行時熱更新,使用m2,m3替換m1,顯示效果達到預期。
這時候注意到m1,m2,m3體積分別爲11,9,11KB,應該不僅是存有shader引用和相關參數。所以再將m1,m2,m3打爲一個ab,體積爲11kb。
結論四:
·在shader進入mainAssets的前提下,材質能夠熱更新。
·shader cache隨material ab存儲,多個引用了一樣shader(變種)的材質會重複存儲cache。
更新測試五
使用ShaderVariantCollection,記錄全部用到的variant。
將SVC和shader打入一個ShaderAB。
將材質打成MaterialAB
運行時加載ShaderAB,取SVC,WarmUp,再加載MaterialAB。結果丟失部分variant
更換分包方式,SVC、shader和Material打成一個包。一切正常。
結論五:
·使用ShaderVariantCollection能夠作到帶變種的材質更新。
·目前版本(5.6.0和5.5.2)依然有bug,必須將SVC、shader和全部對應材質放在一塊兒才能作到可熱更新。
備註:
·爲了測試,我用HFS配置了局域網http服務器,只要在同一個無線網端下,pc和手機都能訪問,配合不一樣平臺的AB分文件夾管理,全部平臺都能同步看到效果。
·ab.Unload()會把ab設爲null!
·cginc頭文件修改後,全部用到的shader必須手動import一次以強制從新編譯!
參考資料:
·hfs使用介紹:http://bbs.feng.com/read-htm-tid-2234118.html
·ab模型:http://www.cnblogs.com/88999660/archive/2013/03/15/2961663.html
·www禁用cache方法:http://answers.unity3d.com/questions/209078/disable-cache-for-www.html