Unity3D研究院GPU Instancing實戰(九十七)
最近將GPU Instancing應用在遊戲中,遇到了一些坑和大家分享一下,我們用的版本是unity2017。前面說了GPU Instancing目前適合遊戲中的草、石頭等元素較多的地方。
首先需要開發一個刷地表的編輯器,試想一下如果場景上的草每個都要美術手動的去擺這得多麻煩啊,如下圖所示,根據網格和材質動態的在地形上刷地表。
QQ[email protected]" class="alignCenter"/>
刷地表的程式碼如下所示
void OnSceneGUI() { Event e = Event.current; if (e == null) { return; } HandleUtility.AddDefaultControl (GUIUtility.GetControlID (FocusType.Passive)); Ray worldray = HandleUtility.GUIPointToWorldRay (e.mousePosition); if (e.type == EventType.MouseDown && !e.alt && e.button == 0) { //點選種草 } if (e.type == EventType.MouseDrag && !e.alt && e.button == 0) { //拖動種草 } if (Physics.Raycast (worldray, out m_Hit,500f,1 << LayerMask.NameToLayer("GInstances"))) { //種草橢圓區域 Handles.color = new Color (1f, 1f, 1f, 0.5f); Handles.DrawSolidDisc (m_Hit.point, m_Hit.normal, m_Radiua.floatValue); Handles.color = Color.red; Handles.DrawWireDisc (m_Hit.point, m_Hit.normal, m_Radiua.floatValue); SceneView.RepaintAll (); } }
刷出來的元素就是每個不同的遊戲物件,編輯模式下可以還可以單獨的調整它們。不過這又帶來另一個問題,如果美術刷了幾萬個草元素,總不能執行時也管理這些遊戲物件吧。上篇文章我們也講過,GPU Instancing使用遊戲物件的方式效率是最低的,所以我們需要使用Graphics.DrawMeshInstanced()一次性將元素畫出來,不需要遊戲物件。
所以在編輯模式下我們還需要一個儲存的功能,就是將美術刷出來的草元素每個的位置、旋轉、縮放、頂點色、序列化在本地,我是將遊戲物件的矩陣序列化在本地的。如下圖所示,執行時就不需要遊戲物件了,使用Graphics.DrawMeshInstanced()一次畫出來,只佔用一個drawcall。
Graphics.DrawMeshInstanced()這方法還有兩問題
1.一次最多畫1023個元素,如果超出就會報錯,所以需要將草進行分類管理。
2.它不提供裁切的功能,也就是說攝像機看不到的地方,這些草是不會被剔除掉的,依然會被渲染。
解決這個問題,為了避免執行時暴力的for迴圈來判斷是否在視野內,我採取的方法是預先將場景分成20X20若干個格子(可根據遊戲的可視範圍而定)根據玩家的位置,始終只渲染周圍9個格子內的草元素,這樣將大幅度減少執行時for迴圈的次數。
如果每個草的頂點色是不一樣的怎麼辦呢?接著我們看看C#這邊如何將引數傳到shader中,如下程式碼所示,建立MaterialPropertyBlock以後就可以將引數以及對應的值傳遞給shader中了。
for (int i = 0; i < m_Matrixs.Count; i++) { MaterialPropertyBlock prop = new MaterialPropertyBlock(); prop.SetVectorArray("_LightMapUV", m_Lightmaps[i]); prop.SetColor("_Color", Color.white); var item = m_Matrixs[i]; Graphics.DrawMeshInstanced(m_InstanceMesh, 0, m_InstanceMaterial, item, prop, ShadowCastingMode.On, true); }
Shader中可以接受這些值,在vs和ps中處理 。
UNITY_INSTANCING_BUFFER_START(Props) UNITY_DEFINE_INSTANCED_PROP(float4, _Color) UNITY_DEFINE_INSTANCED_PROP(float4, _LightMapUV) UNITY_INSTANCING_BUFFER_END(Props)
畢竟opengl es2.0的手機是不支援的,所以我們還需要做個容錯機制。 可以判斷出來手機是否支援 SystemInfo.supportsInstancing
最後如果有什麼建議或者意見歡迎在下面留言!