Unreal Engine地形系统辨析(一)
Unreal Engine的地形系统称之为Landscape,每个level里面可以存放多个Landscape的Actor(但不能对它们进行连续编辑,也就是说相邻两个landscape是不能被同一个brush修改的)。地形被均匀的切割成多个小的地块,这些地块名为Landscape Component,每个Component可以持有最多2x2个SubSection。而每一个SubSection是由多个quad组成的,这些quad是地形topology的最小单位。由于地形mesh的顶点信息记录在heightmap里,也就是顶点纹理中,所以它的分辨率尽量是二的指数幂。因此quad行和列的数量肯定是一个奇数,例如31x31,63x63等,对应的顶点数量为32x32个和64x64个。相邻两个Component的边界处的顶点信息是一致的,它可以保证在渲染时不会产生crack,当然vertex shader中还有相应的stitch算法能够消除由于邻接的patch使用不同lod而导致的t-junction现象。根据地形的视觉误差计算结果,每个LandscapeComponent会自动的选择对应的lod,高等级的lod会比低一个等级的lod所对应mesh少一半的quad。所以63x63的quad,只会有6个lod(63x63,31x31,15x15,7x7,3x3,1x1),如果想增加lod的数量,只能去增加subsection的quad数量,换句说每个Component最多只能退化成2x2个quad。这对于比较大的地形来说drawcall数量就可能变得很多,因为地形culling的最小单位是Component,如果是一个subsection的quad数量为63x63,且有2x2个subsection的component,假设一个quad是1m的大小,那么一个component最多可以覆盖126米的范围。随着lod的升高,mesh会越来越稀疏(即三角形数量变少),当使用最高等级的lod时,126米的区域会用4个quad来表示。当前component如果可见,最多会形成4个drawcall(如果这四个subsection的screen size差异不是很大,它会自动合并成一个drawcall)。在视锥内且不没有被遮挡的Component一旦很多,那么相应的drawcall也会暴涨。通过调高subsection的quad的数量来减少drawcall似乎不是一个好的办法,因为如果culling粒度太大,会降低culling的效率,导致很多不在视锥内或者被遮挡的三角形依然会被处理。而且由于不同的lod,它对应的mesh的稀疏程度不同,导致drawcall里面处理的顶点数量也不尽相同。
ue4的实现是基于金字塔状的geometry mipmapping的平铺结构,而unity的地形是基于quadtree的层级结构,它们各有各的优势。首先ue4的地形块之间数据耦合度很小(每个component拥有自己的weightmap和heightmap,这些数据不需要跨component进行读取),它可以为每个component计算当前帧的lod值,互相比较独立,完全能够并行处理,只是收尾阶段要获取四个邻居的lod值,把它们传给shader用来做stitch。但是unity的实现需要从根部开始遍历这颗四叉树,如果当前的等级满足要求就不再往下递归,直接把当前等级的顶点信息提取出来,应用到一个固定的17x17个顶点的topology上。这样就保证了每个drawcall处理的顶点数量都是一样的,只是由于邻居的lod可能与自身的lod不同,需要选取不同的indexbuffer而已。为了减少topology的模式缓存数量,lod算法要保证相邻两个patch之间的lod差不能超过一,否则就会出现crack。而ue4的stitch算法似乎没有这些顾虑,弹性也因此更大。
由于unity的地形patch是用quadtree管理的,所以为了并行化视锥culling,它需要把quadtree遍历阶段的结果先缓存起来,放入到一个线性数组中,等到后面再与其他的renderer一起处理。反观ue4的地形,虽然每个component单独管理一个patch比较灵活,但是drawcall的数量会相对较多,尤其是那些较为平坦的地块,或者是距离摄像机较远的地块,基于quadtree的方法可能就只会使用到一个16x16quads的patch去表达,而ue4的方案如前所述,由于component覆盖的区域面积是固定的,所以它会生成更多的drawcall,甚至要处理更多的三角形。不仅如此,由于mesh退化程度有限,远处的mesh在光栅化后可能会出现overshading。
现代的桌面显卡和图形api对于drawcall和顶点处理似乎不那么敏感了,但是像素的处理由于量级太大,所以很难忽略它对性能的影响,尤其是纹理的采样和overdraw。ue4方案利用shader的static permutation的功能,有效的降低了材质中纹理的采样数量。不过如果unity能早日祭出virtual texture(不需要运行时混合多层的纹理)这一利器就另当别论了。
页:
[1]