[ue4] HISM 大规模植被渲染解决方案

        HISM,即HierarchicalInstancedStaticMesh,是ue4中针对类似植被这样大规模重复物件渲染提供的一种解决方案。本文仅作为HISM设计的导读,具体细节需参照源码的实现。

ISM

        在讨论HISM之前,我们先来看一下ISM,即InstancedStaticMesh。简单来说它就是ue4提供的实例化渲染,通过Instance,绘制n个相同的物体,原来需要n次drawcall,现在只需要一次就可以了(更多的细节可以猛戳这里)。

        想象一下,如果我们想要渲染一束花,一盒鸡蛋,这看起来是一个非常不错的解决方案。但是,如果我们要渲染一望无际的植被,就会出现以下这些问题:

        (1) 视野中只会出现部分植物,但绘制时却需要提交整个植被;

        (2) 我们难以控制远、中、近景的精度(LOD);

        一个比较简单的思路是,可以把植被拆分成多个部分来管理,进行选择性的drawcall。

HISM

        为了解决上面提到的问题,ue4提供了HISM的解决方案,H对应的就是层级的意思。它的思路本质上也是把植被拆分,只不过更加复杂一些,针对每个StaticMesh,构建一个ClusterTree的树状结构来管理植被。它仅在创建物体时自动构造一次,因此只适用于静态的类植被的物体。

        首先,先不管这棵树的具体结构,我们来考虑一个问题:如何将instance物体拆分为多个部分,并分段提交?

        instance物体对应了一个instance buffer,它按顺序存储了每实例的structure数据,比如transform信息。如果我们希望将原instance物体拆分为k份,我们确实可以重新分配k个instance buffer,不过还有更加简单的方法。

        在绘制instance时,图形API提供了相关的接口,可以指定instancebuffer的起始位置和偏移值,以及设定实例数量,来控制最终绘制的实例。也就是说,我们只需要k个instance buffer的起始位置和偏移值,就能将原有的实例物体拆分为k部分。这样做的好处在于,我们不需要重新分配instance buffer。

将一个instance buffer拆分为多块,记录有效区间(蓝色部分)的起始位置和偏移值

        通过这样的方式,我们可以以一种较为粗糙的细粒度来控制植被的剔除以及LOD的切换。 

ClusterTree

        ClusterTree是一棵多叉树,它的每个节点包含了首尾Instance Id,对应一段区间。将父节点的区间切割后生成多个子节点。

        为了控制树的生成形状,我们可以设定预设剔除的粒度(越小剔除越精细)、预设至少生成多少实例等数据,ue4会根据这些设定计算出合适的BranchingFactor,作为当前节点是否要继续划分的依据。

        ClusterTree的构建方式:对所有实例在最大轴向上排序,然后一分为二,再对每个子区间继续划分。下图描述了这一过程:

        通过递归的划分,我们得到了多个子区间,但我们并没有得到一棵树。为了构建树,我们需要重复上图的递归过程,先构建叶节点,再将叶节点视为多个大的实例,构建其父节点,自底向上完成整个过程。

剔除

        在ue4的几何体绘制管线中,存在静态和动态两个流程,最终的绘制指令由MeshCommand封装。

        对于静态绘制而言,会在物体创建并添加到场景时构建StaticMeshBatch,并缓存下来,除非有必要才会重新生成MeshBatch。

        而对于动态绘制物体而言,需要每帧生成MeshBatch,再基于MeshBatch生成最终的MeshCommand,因为每帧的MeshBatch信息可能发生变化。

        由于镜头的转换后,我们需要绘制的植被部分可能会发生变化,因此在使用HISM后,就必须走动态绘制的流程。

        剔除的过程在InitViews中进行,这一过程实际上完成了两个操作,一个是可见性计算,此外是收集对应的MeshBatch并生成MeshCommand。HISM的剔除不在常规的可见性计算流程中,而是在收集动态MeshBatch流程中完成。

        在生成MeshBatch时,从ClusterTree根节点开始计算节点对应的包围盒是否在视锥体内,如果在则继续查询子节点。将可见节点对应的首尾instance id记录到数组中(必要时合并连续区间),得到多段首尾instance id。

        仔细观察MeshBatch的结构,会发现它包含了一个Element的数组,但是一般静态物体只会用到一个Element。而对于HISM而言,则是一段区间对应一个Element,这个结构似乎就是专门为了兼容HISM这类数据的提交而实现的。

        当我们把HISM的数据拆分为k段提交后,我们将在MeshBatch下得到k个Element,它们将进行k次drawcall。这里就存在一个取舍问题:拆分得越精细,意味着剔除越精细,但drawcall有可能提升;反之剔除越粗糙,drawcall将降低。

LOD

        LOD也就是根据相机到物体距离的远近选择不同精度模型(对植被而言,最远处切换为公告牌)。在剔除过程中计算视锥体和节点对应的包围盒相交的过程中,我们也同时计算包围盒中心到相机的距离,并决定这一区间的绘制层级,然后将首尾instance id记录到对应LOD层级的数组中。

  • 8
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值