UE3中如何使用Instance技术

前言

Instance技术其实就是将一个模型实例化成多份的技术,只要向GPU提交一份模型顶点数据和一堆Transform矩阵,就能创建多份相同的模型。因为只需要一个Drawcall,所以如果需要渲染多个相同的模型,用这个方式可以大大提高性能。其原理其实就是向GPU提高两个VertexBuffer,一个是模型的顶点数据,另一个就是Transform矩阵,DX和OpenGL各有各的API来设置第二个VertexBuffer每隔一个Transform矩阵的内存大小分配给一个Instance作为顶点数据。然后调用DX和OpenGL渲染指定个数的Instance。需要注意的是,如果想一个Drawcall搞定,前提是渲染状态要相同,也就意味着相同模型不同材质,没法在一个Drawcall内搞定,就得当两个模型处理。

UE3中的实现

UE3中用到Instance技术的地方其实在它的植被系统中用到,也就是编辑器左侧的树叶按钮对应的系统。最终我们就会找到代码其实是在InstancedFoliage.h当中。

一个模型其实应该对应一个FFoliageMeshInfo,这部分数据记录在EngineFoliageClasses.h的AInstancedFoliageActor当中,由植被系统编辑器维护。当我们想对模型添加一个实例时,只要拿到模型对应的FFoliageMeshInfo,调用AddInstance,就能创建一个实例。

FFoliageMeshInfo其实还维护了FFoliageInstanceCluster数组,FFoliageInstanceCluster的作用就是创建一个簇,一个簇对应一个Drawcall,它维护了一个区域内的所有实例,因为实际应用中,我们不可能把整个世界的实例都在一个Drawcall里渲染,因为这样很多都会在屏幕外,增加了顶点数据传输的浪费。所以才需要有簇,尽可能划分出合理的范围,将一个范围内的实例一起渲染。

所以FFoliageMeshInfo调用AddInstance方法的第一步就是根据位置找到符合条件的FFoliageInstanceCluster,也就是保证不会超过FFoliageInstanceCluster最多的实例上限,以及距离FFoliageInstanceCluster的BoundBox中心在限定范围内并且尽可能近,BoundBox由FFoliageInstanceCluster之前添加的实例算出。当找不到最符合的FFoliageInstanceCluster时,则创建一个FFoliageInstanceCluster,它的精髓就是它带了一个ClusterComponent,它是一个UInstancedStaticMeshComponent对象,作用就是渲染Instance用的。所以新创建的FFoliageInstanceCluster就需要给它指定UStaticMesh作为渲染的模型(代码里没有指定材质,所以自定义的话需要自己实现这部分代码去添加材质,添加材质的方法同UStaticMeshComponent)。

实例的信息记录在FFoliageInstance当中,它的基类FFoliageInstancePlacementInfo,记录了实例的旋转位移和缩放。AddInstance方法开头可以看到它会先将实例信息存储在FFoliageMeshInfo的Instances当中,这么做其实是为了移除和选择实例方便而做的,并且FFoliageInstanceCluster的InstanceIndices也存一份。最后UInstancedStaticMeshComponent也给它的PerInstanceSMData创建一个实例,指定实例的Transform,因为实际渲染是它在起作用,所以这一步肯定是必须的。而FFoliageInstanceCluster的InstanceIndices其实是为了想删除某个实例时,能够根据下标去删除PerInstanceSMData对应下标的实例,因为PerInstanceSMData和InstanceIndices其实是一一对应的。

删除实例的时候会去调用FFoliageMeshInfo的RemoveInstances,会先判断多个实例分别在哪些FFoliageInstanceCluster当中,然后就是遍历需要删除实例的FFoliageInstanceCluster,然后遍历FFoliageInstanceCluster的每个实例,判断每个实例是否有在要删除的实例列表中,把要删除的实例从FFoliageInstanceCluster中删除完之后,判断是否有FFoliageInstanceCluster需要删除,再遍历所有FFoliageInstanceCluster将实例个数为0的FFoliageInstanceCluster删除。

拓展

如果想实现Prefab的Instance的话,基本上就得对上面这一套进行复制重写了,因为上面这一套和植被编辑器耦合得很严重。而且如果想更实用的话也得进行拓展,因为相同模型可能用不同的材质,也就意味着得根据模型和材质去对应FFoliageMeshInfo。

Instance顶点数据相关的东西在InstancedStaticMesh.cpp文件中,因为隔得太久了,对这部分的记忆已经淡忘了,加上现在也没有实际需求驱动我去看,所以我也没打算再看一遍,留给大家自己去看吧。

它相关的Shader在MeshInstancedVertexFactory.usf当中,我记得UE3对于Instance的选中是有bug的,我也忘了当时怎么修的了

既然提到模型相关的优化,那么也顺便介绍下另一个优化性能的方式,就是将相同材质的模型合到一个模型当中。这样的话,也能减少Drawcall,但合并顶点数据本身是一个比较耗时的操作,最好放在离线进行处理,之前试过放异步线程处理,会崩溃。其实也合理,VertexBuffer确实也不是线程安全的。UE3其实也提供了模型合并这个操作,不过藏得比较隐秘。这个黑科技其实放在UnStaticMeshBuild.cpp当中,是将多个UStaticMeshComponent合成一个UStaticMesh的操作,如果Component带了多个材质,那么UStaticMesh也会分成多个材质ID,也就对应相同个数的Drawcall。尽管合并模型能做到Instance做不到的事情,例如相同材质不同模型的模型可以合到一起一个Drawcall搞定,但是合并之后顶点数据的总量其实是比Instance多得多的。试想一下多个相同模型的情况,Instance只有一份模型顶点数据和多个Transform,而合并则是n份模型顶点数据合到一起然后1个Transform,高下立判,所以实际情况还得考虑顶点数据传输的开销。还有另一个很大的缺点就是合并模型之后顶点数据占内存,存到本地的话也特别占硬盘空间。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值