移動端草海的渲染方案(一)

塞爾達草海的模仿

塞爾達的草海 讓人印象深入,忍不住又要背誦臺詞:html

我想起那天下午夕陽下的奔跑,那是我逝去的青春。git

好了,如今回來。github

若是嘗試用unity內置的地形草來還原上圖效果,咱們會發現有點力不從心:首先,內置草的shader不支持 高光數組

下圖是前項目咱們用傳統的 Blinn-Phong 光照模型添加的高光:性能

這裏在計算光照時讓 草的法線向上,大體能夠模擬出塞爾達草海的高光形狀。優化

不過,塞爾達的草可遠不止於此:隨風擺動,碰撞彎曲,可破壞,可點燃......插件

下面的視頻是咱們用unity對上述效果的高仿:3d

塞爾達高仿--騰訊視頻連接cdn

是否是有點帥,:)視頻

本文以及後續的幾篇文章陸續會介紹咱們的實現方式,以及一些改進方案。

Unity地形草的侷限

咱們的目標是要能在 中高端移動設備 跑得起 至少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個:

  1. 不支持 GPU Instancing。

  2. Patch 進入視野後合併模型的CPU開銷很大。

這裏介紹3款插件,都號稱解決了咱們的痛點:

  1. uNature

  2. Advanced Terrain Grass

  3. Nature Renderer

固然這幾個插件也並不是十全十美,咱們是須要作二次開發的。

下篇文章會介紹一下這幾個插件的實現原理,以及咱們的選擇。

我的主頁

本文的我的主頁連接:baddogzz.github.io/2020/01/14/…

好了,拜拜。

相關文章
相關標籤/搜索