這是第132篇UWA技術知識分享的推送。今天我們繼續爲大家精選了若干和開發、優化相關的問題,建議閱讀時間10分鐘,認真讀完必有收穫。

骨骼動畫

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問答社區提供了回答

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提供

渲染

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問答社區提供回答

內存管理

Q4:我在優化項目的時候發現當刪除人物模型,其材質和網格與Texture2D依然存在內存中。使用的刪除方法是(DestroyImmediate)。查看Texture2D的引用如圖:

發現其被一個實例化的Material引用,進一步查找之後,如圖:

爲什麼會有這麼多被引用項呢?還是我查找的方式不對?請問如何釋放這部分內存?(人物模型確認被刪除了)

材質和貼圖如果要立即釋放,也要手動Destroy纔行,否則就是當沒有其他引用的時候,調用Resources.UnloadUnusedAssets纔行,不過Resources.UnloadUnusedAssets會造成卡頓,建議還是手動管理材質和貼圖。

感謝旭軍@UWA問答社區提供回答

序列化

Q5:ableObject的加載速度,同插件MessagePack,或者一般Json插件反序列化的速度相比,大概在什麼水平?現在自己測試的結果,Unity直接用C#序列化 / 文本讀取再Parse / MessagePack插件,三者的加載同樣的一堆散碎配置文件的時間比大概是 10:4:3。

但是即使是最快的MessagePack插件的讀取時間也不令人滿意。所以,希望能知道 ableObject 或者 Sqlite是否可以比MessagePack更快地加載配置文件。

Sqlite其實讀取數據是最快的,但是重點是數據結構,可以使用反射進行反序列化,但是效率上沒有MessagePack高。個人認爲還是從設計上解決問題,用空間換時間,提前解析數據,佔用內存,使用時直接讀取內存數據比較合理。

感謝鄭曉@UWA問答社區提供回答

今天的分享就到這裏。當然,生有涯而知無涯。在漫漫的開發週期中,您看到的這些問題也許都只是冰山一角,更多的技術話題等你一起來探索和分享。歡迎熱愛進步的你加入,也許你的方法恰能解別人的燃眉之急;而他山之“石”,也能攻你之“玉”。

查看原文 >>
相關文章