《OGRE的主要渲染流程》由会员分享,可在线阅读,更多相关《OGRE的主要渲染流程(5页珍藏版)》请在金锄头文库上搜索。
1、ogre的主要道染流程很早以前就想写一些关于OGRE勺文章了,一直没机会。理解一个道染引擎,我觉得最重要的是先抓住了它的主架构,它的主线,道染流程,不然的话,一个引擎几万行,甚至几十万行的代码,光是打开solution就能吓你一跳了,OGR也有十几万行的代码量,我一开始看它的时候也是无从下手,感觉代码太多了,不知道从哪开始看好,这个class看看,那个class看看,由于对整个引擎没有一个活晰的认识,看过了也印象不深,所以,最后,还是决定先找出它的主线,了解它的道染流程,这样才能有机地把各个部分联系起来。这篇短文也是对OGRE勺主要道染流程的一个介绍,可能对一些class不会太多地去介绍具体的
2、实现细节。我所用的代码都是取自于OGRffi最新的CVS版本。读者最好对OGR审一定的了解,至少得看懂它的example,不然可能一些东西理解起来比较困难。对D3DOPENGL一定了解更好。如果你看过D3DSDK中带的例子,你一定知道一个比较简单的3D程序要运行起来,至少都会涉及以下的几部分:首先是数据的来源,包括顶点数据,纹理数据等,这些数据可以从文件中读取,也可以在程序运行时生成。策下来,我们会建立顶点缓冲区把顶点保存起来,建立texture对象来表示texture,对顶点组成的物体设置它在世界坐标系下的坐标,设置摄像机的位置,视点,设置viewport的位置和大小,然后就可以在道染循环中
3、开始调用道染操作了,经过了frontbuffer和backbuffer的交换,我们就能在屏幕上看到3D图形了,伪代码如下:setupVertexBuffersetWorldTransformsetCamerasetProjectionTransformsetViewportbeginFramesetTexturedrawObjectendFrame以下就是道染一个物体的主要步骤,在我看来,这就是3D程序的主线,同样道理,无论你多复杂的道染引擎,都得实现上述的这些步骤,其他的一些效果如阴影,光照等,都是附着在这条主线上的,所以,如果你能在你所研究的道染引擎上也活晰地看到这条主线,可能对你深入地研
4、究它会大有帮助,下面,我们就一起来找到OGR冲的这条主线。OGRIffl道染循环都是起源于Root:renderOneFrame,这个函数在OGR16带的example中是不会显式调用的,因为example都调用了Root:startRendering,由startRendering来调用renderOneFrame,如果你用OGR床写真正的游戏,或者编辑器,你可能就需要在的消息主循环中调用renderOneFrame了,顾名思义,这个函数就是对整个OGR进行一帧的更新,包括动画,道染状态的改变,道染api的调用等,在这个函数中,会包括了我们上述伪代码的几乎全部内容,所以是本文的重点所在。进入
5、renderOneFrame,可以看到头尾两个fire函数,这种函数在OGR中经常出现,一般都是fire?start和fire?end起出现的,在这些函数中,可能会处理一些用户自定义的操作,如_fireFrameStarted就会对所以的frameListener进行处理,这些fire函数可以暂时不用理会,继续看_updateAllRenderTargets,在这个函数中,会委派当前所用的renderer对所有创建出来的rendertarget进行update,rendertarget也就是道染的目的地,一般会有两种,种是rendertexture,种是renderbuffer,接着进入Ren
6、derSystem:_updateAllRenderTargets,可以看至U在rendersystem中,对创建出来的rendertarget是用RenderTargetPriorityMap来保存的,以便按照一定的顺序来对rendertarget进行update,因为在道染物体到renderbuffer时,一般会用到之前道染好的rendertexture,所以rendertexture形式的rendertarget需要在renderbuffer之前进行更新。进入rendertarget的update,可以看到,它仍然把update操作继续传递下去,调用所有挂在这个rendertarget上
7、的viewport的update0Viewport其实就是定义了rendertarget上的一块要进行更新的区域,所以一个rendertarget是可以挂多个viewport的,以实现多人对战时分屏,或者是画中画等效果,可以把OGR畔的viewport看成是保存camera和rendertarget这两者的组合,把viewport中所定义的camera所看到的场景内容道染到viewport所定义的rendertarget的区域里。Viewport还有一个重要信息是ZOrder,可以看到RenderTarget中的ViewportList带有一个比较函数,所以在RenderTarget:upda
8、te中,ZOrder越小的,越先被道染,所以,如果两个viewport所定义的区域互相重叠了,而且ZOrder乂不一样,最终的效果就是ZOrder小的viewport的内容会被ZOrder大的viewport的内容所覆盖。继续进入Viewport:update,就像前面所说,它调用它所引用的camera来道染整个场景,而在Camera:_renderScene中,是调用SceneManager:_renderScene(Camera*camera,Viewport*vp,boolincludeOverlays)oSceneManager:_renderScene里就是具体的道染流程了。从函数名
9、称还有参数也可以看出来,这不闲数的作用就是利用所指定的camera和viewport,来把场景中的内容道染到viewport所指定的rendertarget的某块区域中。根据camera,我们可以定出viewmatrix,projectionmatrix,还可以进行视锥易U除,只道染看得见的物体。注意,我们这里只看标准的SceneManagert勺方法,不看BspSceneManage派生类的方法,而且,我们会抛开跟主线无关的内容,如对shadow的setup,骨骼动画的播放,shader参数的传递等,因为我们只注重道染的主流程。在SceneManager:_renderScene中所应看的第
10、一个重要函数是_updateSceneGraph,OGREt场景的组织是通过节点树来组织的,一个节点,你可以看成是空间中的某些变换的组合,如位置,缩放,旋转等,这些变换,会作用到挂接在这些节点上的具体的物体的信息,也就是说,节点保存了worldtransform,对具体的物体,如一个人,在空间中的定位,都是通过操作节点来完成的。同时节点还保存了一个世界坐标的AABB这个AABB八容纳所有它所挂接的物体的大小,主要是用于视锥裁减的,如果当前摄像机看不见某个节点的AABB那么说明摄像机看不见节点所挂接的所有物体,所以在道染时可以对这个节点视而不见。_updateSceneGraph的内部处理比较繁
11、琐,我们只需知道,经过了_updateSceneGraph,场景节点树中的每个节点都经过了更新,包括位置,缩放,和方位,还有节点的包围盒。继续回到SceneManager:_renderScene,接下来要看的是setViewport,它会调用具体的renderer的setviewport的操作,设置viewport中所挂接的rendertarget为当前所要道染的目标,viewport中的区域为当前所要道染的目标中的区域。接下来要碰到OGRE!染流程中的一个重要的概念,RenderQueue这个东西实在内容比较多,还是以后有机会单独提出来说吧,你可以简单把它想成是一个容器,里面的元素就是re
12、nderable,每个renderable可以看成是每次调用drawprimitive函数所道染的物体,可以是一个模型,也可以是模型的一部分。在RenderQueue中,它会按材质来分组这些renderable,还会对renderable进行排序。在每一次调用SceneManager:_renderScene时,都会调用SceneManager:prepareRenderQueue来活理RenderQueue然后再调用SceneManager:_findVisibleObjects来把当前摄像机所能看见的物体都加入至URenderQueue中。SceneManager:_findVisible
13、Objects是一个递归的处理过程)它从场景的根节点开始,先检查摄像机是否能看见这个节点的包围盒(包围盒在_updateSceneGraph时已经计算好了),如果看不见,那么这个节点,还有它的孚节点都不用管了。如果能看见,再检测挂在这个节点上的所有MovableObject,如果当前所检测的MovableObject是可见的,就会调用它的_updateRenderQueue方法,一般在这个方法里就可以把和这个MovableObject相关的renderable送入RenderQueueT。这里要说说MovableObject,MovableObject主要是用于表示场景中离散的物体,如Enti
14、ty,顾名思义,能移动的物体,不过它的“能移动”这个能力是要通过SceneNode八实现的,所以MovableObject来能显示出来,首先得先挂接在某个场景节点上,通过场景节点来定位。你可以控制MovableObject的一些屈性,如某个MovableObject是否要显示,是否要隐藏,都可以通过MovableObject:setVisible方法来实现。检测完该节点上的MovableObject之后,就继续调用所有子节点的_findVisibleObjects方法,一直递归下去。这样,就能把场景中所有要道染的renderable所加入至URenderQueue中了。至此,我们就拥有了要渲染
15、的物体的信息了,接下来就是对这些物体进行道染了,你会发现跟D3D或OpenGL勺代码很类似的调用:mDestRenderSystem-clearFrameBuffermDestRenderSystem-_beginFramemDestRenderSystem-_setProjectionMatrixmDestRenderSystem-_setViewMatrix_renderVisibleObjects();mDestRenderSystem-_endFrame();这些api的作用和D3D中的类似调用的作用都差不多,这里再说一下_renderVisibleObjects(),在这个函数中,会
16、对RenderQueue中的每个renderable进行道染,用的是visitor模式来遍历操作每个renderable,最终在SceneManager:renderSingleObject中取出每个renderable所保存的顶点,索引,世界矩阵等信息,来进行道染。这其中还包括了查找离该renderable最近的光源等操作,比较复杂。到这里,SceneManager:_renderScene的流程基本走完了,也就是说,OGRF帧中的道染流程差不多也纭束了,你应该也发现,这个流程跟你用D3D写一个简单程序的流程基本是一样的,在这个流程的基础上,再去看具体的实现,如怎么样设置纹理,怎么样调用你熟悉的D3D或O