标签:
Unity原生渲染方案
作者:3dimensions
本文为原创内容,转载请注明出处。
做这个的动机是想在原生代码中使用Unity的材质系统绘制,同时由原生代码提供绘制数据,省掉动态模型数据“非托管内存→ 托管内存→ 非托管内存”的传输过程。适用于有大量动态模型数据生成的情况,注意,如果不使用Unity的材质系统,并不需要按这个方案做。方案是我在Miloyip的建议下完成的。
一、目标
在Unity中,动态生成三维模型需要把数据填入Mesh对象中,当中Unity内部需要分配内 存及做数据转换,效率不佳。而且如果要编写Unity原生插件去生成三维模型,模型数据需要 经“非托管内存→ 托管内存→ 非托管内存”的传输过程,浪费很多内存频宽及时间,特别是每 帧都需要更新的串流数据。所以,我们希望能绕过这数据传输过程,直接进行原生渲染。本文总结的原生渲染方案,目标是令原生插件继续使用Unity的材质系统,然后在插件内生成顶点数据并提交Draw-call。
二、方案
大致的想法是希望让Unity设置好OpenGL绘制状态后,在原生插件中获取这些状态并 加以利用。此方案暂不考虑多线程渲染及多步骤材质(Multi-pass material),并针对Open GL (Windows)及OpenGL ES(iOS和Android)进行测试。
2.1 进入原生渲染的时机──选项A:GL.IssuePluginEvent
首先我们考虑使用Unity的GL.IssuePluginEvent,但这个方法还存在问题,在PC上测试 的结果显示,通过GL.IssuePluginEvent进入原生渲染后,一些绘制资源(如纹理)已经被解 绑,Unity中设置好的绘制状态被破坏了。所以此方案不可行。
2.2 进入原生渲染的时机──选项B:直接调用原生API
另一个方法是在Unity脚本中先设置好材质,然后直接调用原生插件。首先,在Unity中调 用Material.SetPass()设置材质,但这个命令并不一定会被Unity立即执行,此时若直接进入 原生渲染,可能会发现绘制状态不正确。我们找到一个可行的方法是,在调用SetPass()后紧 随执行DrawMeshNow()去绘制一个小网格,此时整个OpenGL的绘制状态已经被正确设置,然 后才调用原生插件,在原生渲染器中查询到的绘制状态,便是相应材质对应的正确绘制状态。
2.3 如何在Native plugin中利用Unity的绘制状态
如何利用Unity设置好的绘制状态,对于PC和Android有一些区别:
2.4 参与Unity的视锥裁剪
为了使原生渲染器正确的参与视锥裁剪,我们需要在原生渲染器的GameObject设置和原生 几何体相同的包围盒。 具体方法为如下。首先,在挂载原生渲染器的GameObject上关联Renderer及MeshFilter。 然后,在MonoBehaviour.Update()时,从原生渲染器中读取原生几何体的包围盒,再把一个 拥有相同包围盒的Mesh设置到这个GameObject的MeshRenderer,然后Mesh重新计算包围盒引 发MeshRenderer重新计算包围盒。最后,在OnWillRenderObject()时,记录GameObject是否 通过了视锥裁剪。而在OnRenderObject()时,根据是否通过了裁剪来决定是否调用原生渲染 器.
2.5 多个摄像机及多个原生调用
我们测试了多个摄像机的情况,以及在Material.SetPass()后紧随多次原生调用的情况, 原生渲染器均能正确绘制。
2.6 绘制次序
原生渲染器的绘制次序通过Unity的层(Layer)控制,原生渲染器对应不同于场景其他物 体的Layer,原生渲染器的层对应独立的摄像机。摄像机之间依据其Depth属性决定绘制顺序。
三、原生渲染器的性能
测试了一个4W粒子的原生粒子系统,材质为Unity内置的“Particles/Alpha Blended”。测试结果:
四、其他事项
4.1 注意事项
在PC上运行Unity时要给予命令行参数-force-opengl, 强制在PC上选择OpenGL作为绘 制API。内置材质“Particles/Multipy”在PC上效果不正确,原因未明。 OpenGL状态是全局的,在原生渲染器中对OpenGL状态的改变,除原生渲染器申请的自 有资源不需要释放,其他改变如bind/unbind必须在返回Unity时还原回进入原生渲染器时的初 始状态,否则可能导致崩溃。 测试过程中遇到一些问题,如Nexus 10(Mali T604)的驱动在执行Draw-call后会造成内 存泄漏,运行一段时间后驱动便会占用超过1GB内存,令malloc分配新空间时出现内存不足。 在别的机型上没有遇到这种情况,应属于驱动问题。
4.2 未考虑的事宜
4.3 Bu?er Object
把数据上传到GPU有一些需要注意的地方,如果上传新数据时Bu?er object仍然被绘制 占用,可能会引发隐式同步(Implicit synchronization),等待绘制完成。通过使用多个Bu?er object,在帧之间循环使用不同Bu?er object可以降低这方面的开销。在GPU负载较大时会有一些性能提升,缺点是可能会增加了驱动的内存占用。
标签:
原文地址:http://www.cnblogs.com/3dimensions/p/5428963.html