前文介绍了Unity内置 Terrain 刷草的一些缺陷,而且介绍了3款插件:git
uNaturegithub
下面就简单介绍一下这几款插件的作法,以及咱们的选择。优化
Unity内置的刷草工具仍是很好用的,Advanced Terrain Grass 和 Nature Renderer 沿用 Terrain 的刷草,只是接管了渲染。ui
参考一下 TerrainData 的API,咱们能够经过脚本获取刷草信息,而后本身来作渲染。编码
沿用 Terrain 的刷草方式有兼容性上的好处,可是这里就强迫你必须选择 Terrain 来作地表了。插件
uNature 和上面两个插件不太同样,做者本身提供了刷草工具,刷草的对象不局限于 Terrain,也能够是 普通模型。orm
好比下图,我不但在地表刷了草,也在Cube上刷了草。cdn
渲染大面积草,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 为例,场景依然会被栅格化,以下图:
这里的 蓝色格子 相似 Terrain 的 Patch,处于同一个 紫色格子 内的蓝色格子是能够经过 GPU Instancing 来渲染提速的。
若是不考虑 LOD 和 密度 的差别,每一个 蓝色格子 的Mesh是同样的,最终表现上的差别被编码到了 顶点uv 以及 GrassMap 和 HeightMap 这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,若是经过脚原本操做,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 Instancing 的 Drawcall 作了更细致的管理,以下图:
每一个相同颜色的格子属于同一个 Drawcall,和 uNature 的 9宫格 管理方式并不相同。
好了,插件就介绍到这里。
最后,说一下咱们的选择:基于 uNature 作改进。
不选择 Advanced Terrain Grass,主要由于它是基于 Graphics.DrawMeshInstancedIndirect 的实现。此外,若是你想实现相似 塞尔达的割草 效果,整个 ComputeBuffer 的数据都要重建,这个开销在运行时难以承受。
不选择 Nature Renderer 的缘由则更简单,做者并不提供源码。
不过 uNature 自己的问题也很多,若是你们要用这个插件,你得有内心准备:
不管如何,二次开发是必不可少的。
不过,有了 GPU Instancing,大面积的草海已经变得可行了。下面会继续介绍草海的其余渲染技巧以及模仿 塞尔达 的一些好玩的效果。
本文的我的主页连接:baddogzz.github.io/2020/01/16/…。
好了,拜拜。