這是第132篇UWA技術知識分享的推送。今天我們繼續為大家精選了若干和開發、優化相關的問題,建議閱讀時間10分鍾,認真讀完必有收獲。
UWA 問答社區:answer.uwa4d.com
UWA QQ群:465082844(僅限技術交流)
骨骼動畫
Q1:我所在的項目的骨骼規范已經確定,cs骨骼和bone骨骼加起來有60根。現正在調研捏臉的方案,基本確定通過增加面部骨骼來進行捏臉。先描述下我對面部骨骼的理解,如有錯誤請大家指正。
增加的骨骼與原有的骨骼同時導出制作Avatar供骨骼動畫使用,但是骨骼動畫並不控制這些骨骼。玩家捏臉后的數據,也就是骨骼的縮放和旋轉,直接應用到這些面部骨骼上,完成臉部的定制。實際應用到項目里,有如下幾個問題:
1、現有項目中,導入時Optimize Game Object選項是勾選的,這樣就無法在C#層拿到骨骼信息,Expose出來的骨骼是“只讀”的,沒辦法把捏臉數據直接應用到運行時的骨骼上。 暫時只能通過“使用額外的未開優化的Avatar動態生成目標Mesh”的方法來繞過,不知道還有沒有其他更直接的方法?
2、已有的骨骼規范沒有臉部的骨骼,如果要加上,就需要重新導出之前所有的動畫。
有沒有不修改現有骨骼的方案呢?
頭部和身體分離,不過接縫處可能會穿幫。
另外還腦洞了一個方案:專門為頭部做GPU Skin。導入模型時做預處理,將頭部的骨骼和權重添加Mesh的uv2和uv3上(代碼里區分一下是否是頭部骨骼,身體部分的全是零就好了),另存為asset。然后每幀傳骨骼矩陣到Shader里,在vs里面修改頂點的坐標。不過這樣會占用額外的帶寬。
感謝凱奧斯@UWA問答社區提供了回答
接樓上,頭和身體分離,QualitySettings->Blend Weights改成4,可以解決大部分接縫穿幫問題。其實現在游戲中,大部分頭發也會有動畫,所以頭發也要分離。捏臉后的Mesh保存下來,在游戲中和身體拼接的時候,用保存的Mesh(大部分游戲臉部沒有動作)。實際游戲中,並不需要我們捏臉時的骨骼。
感謝鄭驍@UWA問答社區提供了回答
離線骨骼采樣的時候不需要勾Optimize Game Object。等整合出臉了以后再,把臉型貼回去。或者只限定臉部的骨頭可讀寫。
感謝馬古斯@UWA問答社區提供了回答
該問答來自UWA問答社區,歡迎大家轉至社區交流:
https://answer.uwa4d.com/question/5bbc2dba17780b3478091926
UI
Q2:在Profiler中,我們能否看到重建的具體是哪些網格呢? 目前我只能通過VB Uploads的大小來判斷是否引起了網格重建,但是並不知道一個界面中哪些元素會引起網格重建。
這里我有個測試案例,image放進動態canvas和不放進canvas里,這個上傳的繪制內容會有很大的變化,但是具體是哪些東西如果每個東西都去測試一遍,感覺很耗時。
用VB Uploads來判斷重建可能不太直觀,因為SkinnedMesh和粒子系統應該也會引起這一項的變化。
在UGUI里,網格重建分為兩個部分:Rebuild和Rebatch。其中Rebuild是以UI元素為單位的,比如一個Image的Color改變了,那么這個Image就會發生Rebuild操作,開銷是包含在Canvas.SendWillRenderCanvases中的,只要是改變了UI元素的頂點屬性的操作都會引起SendWill的開銷。至於是哪一個UI元素發生了Rebuild,從Profiler里確實是看不出來的。
而Rebatch是以Canvas為單位的,只要Canvas里有任何一個UI元素發生了變化(也包含位置變化),那么這個Canvas就要進行Rebatch,開銷是包含在Canvas.BuildBatch和子線程中的Canvas.SortJob、Canvas.GeometryJob等函數中的。至於是哪一個Canvas發生了Rebatch,可以通過Canvas.BuildBatch函數來確定,在Profiler里選中Canvas.BuildBatch,然后在右側顯示相關的對象,里面列出的就是當前幀發生Rebatch的Canvas的名字,但這個Rebatch是由哪些UI元素的變化引起的,在Profiler里也是看不出來的。
該回答由UWA提供,歡迎大家轉至社區交流:
https://answer.uwa4d.com/question/5bbb2aa217780b34780918ed
渲染
Q3:發現Unity內部的光照貼圖分配方式有一些問題。很多同材質的物體,因為光照貼圖被分配到不同的Atlas上,導致無法合並批次。
比如下面三個連續的DrawCall,實際MeshRenderer的材質完全一樣,偏偏由於光照貼圖在幾個不同的Atlas上,而無法實現合並。
經觀察,如果能夠將出現這種情況的物體實現合並,場景的Draw Call消耗可能降低超過1/3。
所以,現在希望有一個辦法,能夠優化光照貼圖的分配流程,實現同材質物體盡量被分配到同一張Atlas上。
兩種做法:
1)自定義LightmapParameters,通過設置Bake tag,相同tag的物件,會烘焙到同一張Lightmap中。Terrain_1_1是新創建的LightmapParameters,替換掉默認的Pamameters。
2)通過Renderer的uv,從完整的Lightmap貼圖中獲取對應的貼圖,然后把這些貼圖合並生成一個新的Lightmap貼圖(可以用tinyexr結合stb的庫來讀寫exr)。
感謝江南@UWA問答社區提供回答,歡迎大家轉至社區交流:
https://answer.uwa4d.com/question/5bbc460690b1a8347db6dd46
內存管理
Q4:我在優化項目的時候發現當刪除人物模型,其材質和網格與Texture2D依然存在內存中。使用的刪除方法是(DestroyImmediate)。查看Texture2D的引用如圖:
發現其被一個實例化的Material引用,進一步查找之后,如圖:
為什么會有這么多被引用項呢?還是我查找的方式不對?請問如何釋放這部分內存?(人物模型確認被刪除了)
材質和貼圖如果要立即釋放,也要手動Destroy才行,否則就是當沒有其他引用的時候,調用Resources.UnloadUnusedAssets才行,不過Resources.UnloadUnusedAssets會造成卡頓,建議還是手動管理材質和貼圖。
感謝旭軍@UWA問答社區提供回答,歡迎大家轉至社區交流:
https://answer.uwa4d.com/question/5bbc6bb290b1a8347db6dd4a
序列化
Q5:ScriptableObject的加載速度,同插件MessagePack,或者一般Json插件反序列化的速度相比,大概在什么水平?現在自己測試的結果,Unity直接用C#序列化 / 文本讀取再Parse / MessagePack插件,三者的加載同樣的一堆散碎配置文件的時間比大概是 10:4:3。
但是即使是最快的MessagePack插件的讀取時間也不令人滿意。所以,希望能知道 ScriptableObject 或者 Sqlite是否可以比MessagePack更快地加載配置文件。
Sqlite其實讀取數據是最快的,但是重點是數據結構,可以使用反射進行反序列化,但是效率上沒有MessagePack高。個人認為還是從設計上解決問題,用空間換時間,提前解析數據,占用內存,使用時直接讀取內存數據比較合理。
感謝鄭曉@UWA問答社區提供回答,歡迎大家轉至社區交流:
https://answer.uwa4d.com/question/5bb8a578afd2174f6fe946b1
今天的分享就到這里。當然,生有涯而知無涯。在漫漫的開發周期中,您看到的這些問題也許都只是冰山一角,我們早已在UWA問答網站上准備了更多的技術話題等你一起來探索和分享。歡迎熱愛進步的你加入,也許你的方法恰能解別人的燃眉之急;而他山之“石”,也能攻你之“玉”。