您的位置:首页 > 其它

针对ParticleUniverse的特效优化以及其他OGRE优化杂谈

2012-08-18 08:12 288 查看
http://blog.csdn.net/nuiuyi/article/details/6665904





0.序

对于使用ParticleUniverse***的Ogre游戏来说,特效效率问题是很严峻的。很久没有发文章了,这里将结合我最近遇到的问题简单分享一下我面对的问题以及我个人的解决思路。

1. 引子,关于ParticleUnverse的visual particle quota

PU自带了这个参数,他的本意是限制一定的可见粒子数来方便在粒子池中处理循环粒子,避免过多的创建和删除的操作。而我们实际运用中针对PU所做的特效编辑器虽然给出了这一参数,但是没有与美术***人员事先强调这个值的作用,因此他们在***过程中没有对这个参数进行一定的重视,一直使用的是PU默认的值500(这个太多了。。)。等真效率有问题时我特意测试了一下,对于公告板粒子系统来说,quota设置太大是没什么影响的,因为ogre自己就有BB的pool,太多的数量只会导致内存占用。不过对于PU另外支持的Entity等渲染器来说,如果quota设置过大会直接导致sceneNode创建过多,严重影响性能。

OGRE根据sceneNode机制来控制待渲染物体添加到渲染队列中,太多的sceneNode首先会进行多余的场景管理裁剪,而且无论他所attach的movableObject是否visible,都会在addRenderqueue后调用receiveShadow(大致是这个)函数,整套流程也是很卡的。大家可以在sample里创建500个sceneNode各attach一个Entity,即使只渲染一个帧速也会非常之低,原因就在这里。

当然这并不是PU的问题,也不能算作优化,只是简单的讨论下这个参数的重要性以及大致的原理。我具体的做法是最后直接干掉了这个参数,而是在创建PU System的时候根据粒子数量动态设置的quota,编辑器里索性去掉这个参数。如此修改后查看以前一些使用ribbonTrail和Entity的粒子系统效率以及有大幅度提高了。

2.真正的优化,PU的update裁剪

场景渲染后,所有的PU特效都加载了进来并且每帧都在进行逻辑更新,而事实上我们看不见的特效大多是没有必要更新的。当OGRE把看不见的pu所挂接的场景节点剔除出渲染队列以外之后,这个PU就应该暂停更新。但是PU内部默认使用了Ogre的Controller机制,全部是强制每帧update。所以在场景管理的内部addRenderqueue的时候才应该告知PU更新。这样做之后可以大范围减少逻辑更新的数量以及对应的CPU Bound,经过测试帧速的提升也是非常之大的。

3.PU的外部包围盒设置

每个粒子特效可能包含很多个特效元素,比如火焰和对应的光晕等。随着粒子的发射会导致包围盒在动态变化,PU也是随着进行包围盒的更新。一方面粒子系统包围盒的频繁重复计算是没有必要的;另一方面粒子系统实际的包围盒可能要比我们看到的应有效果的包围盒要大很多(比如光晕也许很大,但是其实人们关注的就是中间的一点点火焰),影响了裁剪。所以我采用的方法就是使用一个指定的外部包围盒,这个可以是一个适中大小的默认值,也可以是美术根据希望手动设置的。

4.尽可能合并PU的System为子technique

PU的每个System都是一个MovableObject,在有些情况下我们因为特殊需要创建的都是单独的System,每个System对应一个technique。如果把能合并的System给合并成technique,效率也可以提高。

5.再回到场景节点

抛开PU来看,实际游戏项目中很多情况下都会有这样的情况:一部分物体可以属一类,而且他们的特点是共同显示和共同隐藏。比如换装中人物身上各个子节点,比如一个特效中的各个特效元素(不止PU,也可能有另外的)。但是也许是为了引擎中更好的管理,也许就是为了使用场景节点的层级几何机制,原本理应共同隐藏和共同显示的物体却被分了N个SceneNode。过多的SceneNode当然都是要参与OGRE的场景管理的,比如对于Octree来说,局部过密的场景节点则必然导致效率损耗。但实际上这是完全没有必要的,因为对于这样的情况我们只需要让OGRE计算一个sceneNode的裁剪即可。如果这个节点应该被纳入渲染队列,那么他旗下所有节点都应该被同时纳入。这本不能完全说是OGRE自身的问题,而是大家普遍对SceneNode的滥用(其实是对它的内部机制没有完全理解)确实造成了诸如此类的性能损耗。对于已经成型的引擎来说,改变对sceneNode的使用是很困难的,我在实际中的做法就是更改了OctreeSceneManager的相关代码,添加了类似FinalParent的特殊SceneNode,在需要使用这种优化的地方设置一个标记(我用的是setUserAny),设置完成后所有由此节点派生的所有子节点都为FinalSon。实际八叉树或者其他场景裁剪的过程中如果遇到FinalParent这样的节点则直接判断是否应该裁剪,如不裁剪,则将其所有子节点的MovbleObject全部插入渲染队列。而遇到FinalSon节点,则全部拒绝即可。当然实现方法仁者见仁智者见智,我这么做也是为了不修改OgreMain的代码。

6.跋

其实大多数情况下我们使用的库都已经是库设计者的智慧结晶以及谨慎测试后的劳动成果。如果有一些大的效率问题,很多时候都是使用者没有正确对这个库进行理解或者索性就是使用不当造成的。遇上性能问题时首先不是痛骂这个库效率怎么差,而是认认真真通过分析库的源码(如果有的话)来思考我们整个使用的流程是否有有不合理的地方。比如对OGRE的SceneNode的滥用就是一个典型例子,当然我们可以优化,但是清清爽爽的代码岂不是更好?

当然不否认即使是OGRE这样的第三方库也是有不少设计不合理的地方的,比如他对UV动画和序列帧等的控制使用的是他的Controller,这本是个非常灵活值得赞赏的设计,但是OGRE也是全部每帧都进行更新。如果某个场景的UV和序列帧使用比较多,同时全部更新也是一个值得思索的性能损失。还有众所周知的渲染队列过于繁琐以及没有有效对批次进行合并之类的缺点等等。当然,瑕不掩瑜,OGRE依然是值得我们学习的优秀开源作品。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: