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

書接上文

前文介紹了Unity內置 Terrain 刷草的一些缺陷,而且介紹了3款插件:git

  1. uNaturegithub

  2. Advanced Terrain Grassapp

  3. Nature Renderer工具

下面就簡單介紹一下這幾款插件的作法,以及咱們的選擇。優化

如何刷草

Unity內置的刷草工具仍是很好用的,Advanced Terrain GrassNature Renderer 沿用 Terrain 的刷草,只是接管了渲染。ui

參考一下 TerrainData 的API,咱們能夠經過腳本獲取刷草信息,而後本身來作渲染。編碼

沿用 Terrain 的刷草方式有兼容性上的好處,可是這裏就強迫你必須選擇 Terrain 來作地表了。插件

uNature 和上面兩個插件不太同樣,做者本身提供了刷草工具,刷草的對象不侷限於 Terrain,也能夠是 普通模型orm

好比下圖,我不但在地表刷了草,也在Cube上刷了草。cdn

GPU Instancing

渲染大面積草,GPU Instancing 是很是合適的。

然而,Unity的渲染方案是把地表分紅一個一個的 Patch,每一個 Patch 的草合併成一個大的Mesh,以此來下降 Drawcall,可是 多個Patch 的渲染是沒法經過 GPU Instancing 提速的。

咱們看一下 GPU Instancing 須要知足的條件:

Use GPU Instancing to draw (or render) multiple copies of the same Mesh at once, using a small number of draw calls. It is useful for drawing objects such as buildings, trees and grass, or other things that appear repeatedly in a Scene.

這裏每一個 Patch 生成的Mesh顯然是不一樣的......

固然,咱們能夠突破這個限制。

既然要求 相同的Mesh,那咱們能夠 把Mesh的計算從CPU移到GPU:把影響 Mesh差別 的因素 ( 好比 Noise高度 ) 編碼到紋理,而後在 頂點着色器 採樣紋理再把這些差別應用到頂點。

這樣咱們就能夠用相同的Mesh來渲染,即知足 GPU Instancing 的開啓條件,又能夠知足表現上的多樣性,順帶把前文提到的 運行時合併Mesh產生的CPU峯值 也優化掉了。

uNature 爲例,場景依然會被柵格化,以下圖:

這裏的 藍色格子 相似 TerrainPatch,處於同一個 紫色格子 內的藍色格子是能夠經過 GPU Instancing 來渲染提速的。

若是不考慮 LOD密度 的差別,每一個 藍色格子 的Mesh是同樣的,最終表現上的差別被編碼到了 頂點uv 以及 GrassMapHeightMap 這2張紋理中去了。

HeightMap 一覽:

具體的編碼方式我就不細說了,你們能夠參考源碼。

事實上,Unity在 2018.3 及之後的版本,對 Terrain 的渲染也加了 GPU Instancing 的支持,原理和我上面說的差很少:

When enabled, Unity transforms all of the heavy terrain data, like height maps and splat maps, into textures on the GPU. Instead of constructing a custom mesh for each terrain patch on the CPU, we can use GPU instancing to replicate a single mesh and sample the height map texture to produce the correct geometry. This reduces the terrain CPU workload by orders of magnitude, as a few instanced draw calls replace potentially thousands of custom mesh draws.

不過,一直到我目前在用的版本 2019.3,Unity對於 地形草(Terrain Detail) 的渲染方式仍是老樣子......

GPU Instancing 的 API

關於 GPU Instancing,若是經過腳原本操做,Unity提供了以下2個接口:

  • Graphics.DrawMeshInstanced

  • Graphics.DrawMeshInstancedIndirect

考慮到移動設備的兼容性,咱們通常會選擇 Graphics.DrawMeshInstanced 這個接口,不過 Graphics.DrawMeshInstanced 有一個最大數量 1023 的限制:

Note: You can only draw a maximum of 1023 instances at once.

若是咱們以每一株草爲單位來渲染,很容易就會突破這個限制。

Advanced Terrain Grass 就是這麼作的,因此最後他用了 Graphics.DrawMeshInstancedIndirect 接口。

uNature 則是對草先作必定程度的 Mesh合併,回想一下這張圖的 藍色格子,咱們能夠經過控制格子的粒度,從而把每一個 紫色格子 內的 藍色格子 數控制在 1023 之內,而後就能夠經過 Graphics.DrawMeshInstanced 接口一次完成渲染。

Nature Renderer 的做者並沒提供源碼,不過從反編譯的結果來看,他也是用了 Graphics.DrawMeshInstanced 這個接口,只是對 GPU InstancingDrawcall 作了更細緻的管理,以下圖:

每一個相同顏色的格子屬於同一個 Drawcall,和 uNature9宮格 管理方式並不相同。

咱們的選擇

好了,插件就介紹到這裏。

最後,說一下咱們的選擇:基於 uNature 作改進。

  • 不選擇 Advanced Terrain Grass,主要由於它是基於 Graphics.DrawMeshInstancedIndirect 的實現。此外,若是你想實現相似 塞爾達的割草 效果,整個 ComputeBuffer 的數據都要重建,這個開銷在運行時難以承受。

  • 不選擇 Nature Renderer 的緣由則更簡單,做者並不提供源碼。

不過 uNature 自己的問題也很多,若是你們要用這個插件,你得有內心準備:

  • 做者已經好久沒有更新了。
  • 代碼有很多bug。
  • 針對移動端還要作不少優化。

不管如何,二次開發是必不可少的。

不過,有了 GPU Instancing,大面積的草海已經變得可行了。下面會繼續介紹草海的其餘渲染技巧以及模仿 塞爾達 的一些好玩的效果。

我的主頁

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

好了,拜拜。

相關文章
相關標籤/搜索