Unity中Shader是否可以熱更新的測試

轉自:https://www.cnblogs.com/cpxnet/p/6439706.html

在unity的資源中,shader是比較特殊的一類。主要有下面幾個疑問

1. Shader算是代碼,並且需要編譯。那麼是否可以熱更新?

2. AB中加載進來的shader是否可以通過shader.Find(名稱)來索引?

3.在使用shader_feature關鍵字後,build時忽略的變種是否要在運行時編譯?

4.預編譯shader cache的存儲位置究竟在哪裏?

 

直接補充最終結論:

1. shader可以熱更新。

2. 使用multi_compile生成Shader Variant時,材質可以直接熱更新。

3. 使用shader_feature生成Shader Variant時,可以使用ShaderVariantCollection來記錄所有使用到的變種,實現材質更新。(目前仍有bug,必須將shader、SVC和所有材質放在一個AB中)

4. 不要用Shader.Find找自己包裏的shader,使用AssetBundle.LoadAsset<Shader>()

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一次以強制重新編譯!

測試二用到的工程(AB和熱更新)

 

參考資料:

·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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章