项目的需求,开放视角要考虑DrawCall的开销,所以要是实现这个功能,这个功能已经完成一段时间了,在这里做个记录,由于项目是商用,故不能贴出源代码,只贴出实现思路,供大家交流,如有不对的地方还请指正。
GPU Instancing已经不是什么新技术了,其目的主要是为了提升绘制大量物体的渲染效率,从CPU和GPU层面减少重复性的工作,具体GPU Instancing的技术细节说明,本文不再赘述,感兴趣的同学可以在查看本文最后的扩展链接。
Unity在5.4版本开始引入了GPU Instancing,分为自动和手动两种方式,原生的 surface shader和stand shader都提供GPU Instancing的自动选项,只要在材质界面上勾选Enable GPU Instancing选项,无论手动或自动,大的前提是要Mesh和材质相同。
我们知道,任何通用的解决方案背后都会有或多或少的限制,这在程序设计上是说得通的,因此才会体现定制的价值。Unity也不例外,自动的GPU Instancing会有很多限制,比如:不支持static batch,不支持lightmap,等等。关于Unity GPU Instancing说明以及限制。请参照官方文档https://docs.unity3d.com/Manual/GPUInstancing.html
在项目的实际应用中,我们的场景中存在大量的静态植被都需要lightmap烘焙,如果自动不支持,显然我们就要考虑手动的方式,这正是本文的目的,Unity提供了两个底层的API来进行手动的GPUInstancing,分别是Graphics.DrawMeshInstanced和Graphics.DrawMeshInstancedIndirect。
有了实现的入口,接下来就是实现的思路和流程,首先要做lightmap的支持,如果不考虑shadowmask的情况下,这个问提还是很好解决的,就是把模型对应的lightmapScaleOffset传给shader做lightmap采样,在shader走uniry 的gi流程,模型行本身不做GPUInstancing处理,考虑到混合光照的情况,还要对shadowmask进行采样,所以就需要把shadowmask贴图和光照贴图都传给shader。
这里面在项目实际操作中,遇到了两个坑:
1:如果shadowmask是多张的情况,还要通过shadowmask对对象分组,就是在一张shadowmask贴图上都有哪些对象,而最终的分组长度则会直接影响GPUInstancing的批处理数量。
2:很多项目在Quality设置选项中,都会将shadow distance的值设置的很小,这是为了保证阴影的精度。如果绘制对象过于密集,由于是按shadowmask进行分组Instancing,单张shadowmask中对象在场景中很分散,对象所对应的阴影距离超出shadow distance设置的值,表现得效果就是,不分对象没有进行shadowmask采样,这个问题在目前还没有找到很好的解决办法,有哪位小伙伴有更好解决方案可以在下面留言。
最后再通过Graphics.DrawMeshInstanced或Graphics.DrawMeshInstancedIndirect每帧绘制对象,这里还有一个问题要注意就是官方对于Graphics.DrawMeshInstanced绘制对象的个数是有限制的,最高上限上限是1023,强迫症的表示很不爽。
这是1023颗草的效果,36个DrawCall。
最后要感谢leonwei博主,起初我就看了他的文章,才有了思路,原文在这
http://blog.csdn.net/leonwei/article/details/73274808