塞爾達的草海 讓人印象深入,忍不住又要背誦臺詞:html
我想起那天下午夕陽下的奔跑,那是我逝去的青春。git
好了,如今回來。github
若是嘗試用unity內置的地形草來還原上圖效果,咱們會發現有點力不從心:首先,內置草的shader不支持 高光。數組
下圖是前項目咱們用傳統的 Blinn-Phong 光照模型添加的高光:性能
這裏在計算光照時讓 草的法線向上,大體能夠模擬出塞爾達草海的高光形狀。優化
不過,塞爾達的草可遠不止於此:隨風擺動,碰撞彎曲,可破壞,可點燃......插件
下面的視頻是咱們用unity對上述效果的高仿:3d
是否是有點帥,:)視頻
本文以及後續的幾篇文章陸續會介紹咱們的實現方式,以及一些改進方案。
咱們的目標是要能在 中高端移動設備 跑得起 至少60米 視野範圍的草海。
若是用Unity提供的 Terrain 來刷草的話,下面的2個參數咱們必須很是注意:
爲了記錄刷草的信息,Unity會把咱們的 Terrain 柵格化,Detail Resolution 指定了格子的 劃分粒度:這個值越大精度越高,同時須要的內存也越高。
好比咱們把 Detail Resolution 設置成 512,那麼地表會被劃分紅 512 x 512 個格子,每一個格子是刷草的最基本單位,草的 密度 決定了每一個格子草的數量。
Unity的 TerrainData 給咱們提供了一個接口用於獲取刷草信息:
public int[,] GetDetailLayer(int xBase, int yBase, int width, int height, int layer);
這裏 GetDetailLayer 返回的是一個二維數組,數組長寬的最大值和 Detail Resolution 是對應的。
考慮到大面積草的渲染,若是以每一個格子裏的單株草爲單位,那 drawcall 會很是高。
對此,Unity作了它的優化:把必定數量的格子合併成一個 Patch,以 Patch 爲單位來渲染,這樣 drawcall 就能大幅度下降,Detail Resolution Per Patch 決定了每一個Patch包含的格子數量。
好比咱們把 Detail Resolution Per Patch 設爲 16,那麼每一個 Patch 就包含了 16 * 16 = 256 個格子,Unity會把這 256 格里的草合併成一個大的Mesh,用於最終的渲染。
這個時候,你可能和我有同樣的疑問,GPU Instancing 跑哪去了?
按照Unity的實現方案,每一個 Patch 生成的Mesh是 獨立且各不相同 的,GPU Instancing 的條件並不知足......
沒有 GPU Instancing 就算了,你很快會發現另外一個問題,新的 Patch 進入視野時,有嚴重的 CPU性能開銷,看一下下圖的峯值:
Patch 在運行時 合併Mesh 的開銷很大。
這個時候,你可能已經心灰意冷,嘗試着尋找 Terrain刷草 的替代方案了。
事實上,國內用 Terrain 作地表的手遊彷佛也很少,更別說用 Terrain刷草 了。
不過了解 Terrain 的作法仍是必要的,咱們至少知道了Unity內置刷草的大體實現方式和存在的問題,以此爲基礎,再去理解一些第三方插件就容易得多了。
咱們剛纔的痛點主要有2個:
不支持 GPU Instancing。
Patch 進入視野後合併模型的CPU開銷很大。
這裏介紹3款插件,都號稱解決了咱們的痛點:
固然這幾個插件也並不是十全十美,咱們是須要作二次開發的。
下篇文章會介紹一下這幾個插件的實現原理,以及咱們的選擇。
本文的我的主頁連接:baddogzz.github.io/2020/01/14/…。
好了,拜拜。