from http://blog.sina.com.cn/s/blog_5e3213f30100zu1d.html
【 OGRE的渲染循环框架】
OGRE通过WinMain或main调用go再通过Root调用startRendering进行消息循环,然后调用renderOneFrame,通过RenderSystem的_updateAllRenderTargets方法,更新所有的RenderTarget。RenderTarget通过update方法更新与之关联的Viewport并产生FPS统计信息。而Viewport则调用与之关联的Camera的_renderScene方法进行渲染,Camera此时把“球”踢给SceneManager。进入SceneManager的renderScene成员函数中后,在经过计算后,把需要渲染的场景送给RenderSystem去做真正的渲染,此时我们可以看到熟悉的_breginFrame和_endFrame。 一直下去经过RenderQueue、RenderQueueGroup、RenderPriorityGroup、QueuedRenderableCollection再通过访问者到达QueuedRenderableVisitor的子类SceneMgrQueuedRenderableVisitor,最终又回到SceneManager,由SceneManager再到RenderSystem完成整个渲染过程
以下是渲染循环的时序图:
如图,可以清晰明确的看出来Ogre 的渲染循环是怎样的了,其自从startRendering以后就一直以RenderSystem为基点开始渲染循环,,因为Render部分操作比较多而中间涉及了从Renderable到RenderOpretion(个人觉得可以看作是从Ogre引擎的层面到基本图形绘制层面的转化,所以是很重要的一环)的过渡,所以理解Ogre 的渲染顺序是十分重要的,这样可以理解Ogre怎样通过内部循环实现了逐帧按序渲染,而且不失为理解OGRE 的系统结构的一个好方法。
【Ogre中的Renderable(可渲染对象接口)】
Renderable对象是OGRE中所有可渲染对象的抽象接口。这个接口的实现类必须是基于单一的材质、单一的世界矩阵(或者是一组通过权重混合的世界矩阵,以及单一的渲染操作。
通过这个说明。要明确的是Renderable封装了3D世界中被渲染对象的基本属性而后数据,包括:渲染操作,材质属性,光照信息,变换矩阵(四元组),LOD信息,渲染方式等信息。这些信息在渲染循环中被取出,并应用在图形渲染管线中。需要注意的是RenderOperation(渲染操作)对象,它封装了图形硬件的顶点数据和渲染的图元类型,在最终的渲染操作中使用这些数据来进行图元渲染,值得注意的是RenderOpration中的保存的顶点信息的类VertexData中是抽象的,它包含了一组硬件顶点缓存数据对象-HardwareVetexBuffer,这个类是从HardwareBuffer中继承过来的,这个类包括了Hardware buffer中的抽象操作方法,这里使用了模板方法和抽象工厂的设计模式,每个具体的渲染系统可以实现自己的硬件缓存数据。当然,这其中也有HardwareBufferManager,而且具体的渲染系统也要实现这个类,来创建具体的渲染系统相关的数据缓存。
【Ogre内部几个重要对象】
l RenderSystem:此抽象类定义出3D渲染系统的基本功能,同时实现了一些通用的方法,各个不同的渲染平台实现此类,OGRE内部交互由此抽象类负责,从而实现了渲染平台无关性。应用一般不会与此对象直接进行交互,在OGRE内部的其他重要对象,如SceneManager与它进行交互,整个过程是透明的。
l SceneManager:组织场景中的对象并将对象送入渲染系统中渲染。这个类定义了场景管理器的基本功能,应用客户端根据自己需要重新实现场景的组织功能。这个对象掌控了所有的可渲染对象。
l RenderTarget:接受渲染操作结果的画布,此抽象类定义了基本的渲染目标属性和操作的功能。渲染目标可以是窗口、屏幕、或者离屏表面,如,渲染到一张纹理图等等。每个具体的渲染引擎需要实现自己的RenderTarget,如:D3D9RenderWindow,D3D9RenderTexture等,这个抽象类包含了一个到多个的viewport对象,在渲染时会迭代更新每个viewport。
l ViewPort:定义:一块渲染目标区域。
说明:视口是相机和渲染表面的交集,并把这个结果放在整个渲染表面或者 表面的一部分,每个视口都有一个相机作为源,一个目标作为目标, 一个相机只有一个视口,但是一个渲染目标可以有多个视口。视口有 z序属性,如果渲染目标有多个视口,要对视口进行Z排序。
l SceneNode:这个类从Node继承过来,除了自身是一个树形结构之外,它通过附加多个可移动对象(MoveableObject)可以与世界中的所有可移动对象(比如实体对象)关联起来。
l Entity:定义了一个离散的、基于mesh的可移动对象的实例
OGRE通常将可渲染对象分为2组,一是在世界中移动的离散的及其相关小对象,一种是大规模的杂乱的通常组成静态场景的对象。
Mesh和Submesh处理哪些在离散的可移动对象中使用的图元定义。Entity在世界中是真正的基于这种图元对象的实例,因此对于一个汽车来说它就是一个独立的网格集合,但是在世界中可以有多个基于相同网格集合的实体,这些实体可以改变网格的外在表现,例如通过改变材质属性(这样你就可以再相同的图元数据上用不同的纹理来拥有不同的小汽车)。为了这个目标,因为Mesh被分成了多个SubMesh,所以实体类是一个分组的类(与Mesh类很相似)并且相关的独立的个体改变的细节信息被保存在SubEntity类中,这是1:1的关系,Entity与SubEntity及其关联的Mesh和SubMesh。
实体和子实体都不会被直接的创建出来,用SceneManager的CreateEntity(传递一个模型的名字)方法来创建它们。
实体是被关联它们的场景节点对象包含在场景中的,使用attachEntity方法关联,参照SceneNode类的说明获取完整的信息。
l RenderQueue:定义了渲染队列,此队列中包含了按照材质排序的可渲染对象,这样会使得渲染状态的切换最少,它包含了一组RenderQueueGroup对象。
l RenderQueueGroup:渲染队列组,按照渲染的优先级别排列的渲染对象列表。
l RenderPriorityGroup:渲染优先级分组,包含的所有的渲染对象都具有相同的优先级。分出透明对象和非透明对象及其他渲染方式对象,使得渲染状态切换最小。
【Renderable详解】
首先Ogre引擎会在启动时根据配置创建一个合适的场景管理器(SceneManager),SceneManager会自动创建一个根节点对象(SceneNode),有了这个root node我们就可以在这个root node下创建场景节点并将3D渲染实体附加到节点上。应用程序可以在任意节点下创建子节点,创建之后将3D渲染实体附加到此节点上。在SceneNode内部,保存着一个MoveableObject列表,由于Entity是从MovableObject继承过来,所以可以把Entity附着在SceneNode上,同时,Entity内部包含一个SubEntity的列表,而SubEntiy是从Renderable继承过来,这样在场景更新的时候,首先调用SceneNode的_addToRenderQueue方法,这个方法内部迭代MovableObject列表的每一个项,调用MovableObject的_updateRenderQueue抽象方法,每个从MovableObject继承的类都要实现此方法,比如Entity,在Entity的这个方法内部,迭代subEntity列表的每一项(SubEntity从Renderable继承),将每一个Subentity送入渲染队列中去,这样就完成了渲染队列的更新工作(其中还有进行可视判断,是否透明物体,是否为骨骼节点等等,但主要的流程是不变的。
RenderQueue的类和数据类型关系图:
【RenderQueue结构图】
具体来说,OgreRenderQueue是如下的一个类(RenderQueue本身隶属于场景管理器(SceneManager)对象,一个场景管理器拥有一个RenderQueue对象):
class _OgreExport RenderQueue : public RenderQueueAlloc
{
public:
typedef map< uint8, RenderQueueGroup* >::type RenderQueueGroupMap;
typedef MapIterator<RenderQueueGroupMap> QueueGroupIterator;
typedef ConstMapIterator<RenderQueueGroupMap> ConstQueueGroupIterator;
class _OgreExport RenderableListener
{
public:
RenderableListener() {}
virtual ~RenderableListener() {}
virtual bool renderableQueued(Renderable* rend, uint8 groupID,
ushort priority, Technique** ppTech, RenderQueue* pQueue) = 0;
};
protected:
RenderQueueGroupMap mGroups;
ushort mDefaultRenderablePriority;
……
RenderableListener* mRenderableListener;
public:
RenderQueue();
……
RenderableListener* getRenderableListener(void) const
{ return mRenderableListener; }
……
};
}
可以看到其内部有一个RenderQueuGroup的Map,RenderQueueGroup表示具有固定编号的渲染队列组,其固定编号通过OGRE中的RenderQueueGroupID枚举变量定义如下:
enum RenderQueueGroupID
{
/// Use this queue for objects which must be rendered first e.g. backgrounds
RENDER_QUEUE_BACKGROUND = 0,
/// First queue (after backgrounds), used for skyboxes if rendered first
RENDER_QUEUE_SKIES_EARLY = 5,
RENDER_QUEUE_1 = 10,
RENDER_QUEUE_2 = 20,
RENDER_QUEUE_WORLD_GEOMETRY_1 = 25,
RENDER_QUEUE_3 = 30,
RENDER_QUEUE_4 = 40,
/// The default render queue
RENDER_QUEUE_MAIN = 50,
RENDER_QUEUE_6 = 60,
RENDER_QUEUE_7 = 70,
RENDER_QUEUE_WORLD_GEOMETRY_2 = 75,
RENDER_QUEUE_8 = 80,
RENDER_QUEUE_9 = 90,
/// Penultimate queue(before overlays), used for skyboxes if rendered last
RENDER_QUEUE_SKIES_LATE = 95,
/// Use this queue for objects which must be rendered last e.g. overlays
RENDER_QUEUE_OVERLAY = 100,
/// Final possible render queue, don't exceed this
RENDER_QUEUE_MAX = 105
};
(可以看出按照优先级从大到小(枚举ID从小到大排序)下来的渲染顺序和图形学中算法是一致的)。每一个RenderQueueGroup对象拥有一个上述枚举变量中的ID,用来标识这个渲染队列组的处理优先级(知道为什么用MAP了吧)。
从结构图上可以看出QueuedRenderableCollection其实来自于QueuedRenderableCollection,而RenderableCollection也同RenderQueue类似仅仅是一个RenderableList的管理器,其中根据Renderable的pass或者与camera的distance分组,形成最后真正提供给OPENGL或者D3D进行渲染的图元包含在RendrableOperation的实例中。