Urho3D 1.7.1 源代码分析(五)

2019-12-01  本文已影响0人  RonZheng2010

1. Renderer子系统

1.1 Node、Component/Drawable

1.1.1 基本概念

Component是绘制元件。Component接口定义了虚拟函数OnSceneSet()和OnMarkedDirty()。

Drawable是可见的绘制元件。StaticModel是从建模文件得到的模型。 它是可见的,从Drawable派生。Light是光照,是一种特殊的Drawable。Camera是摄像头,不可见,直接从Component派生。

每个Component实例都要绑到一个Node上,每个Node下的一组Component合作完成一个任务。Node的成员components_保存这组实例。

所有Node组成一棵树,Scene是Node树的根,它是一种特殊的Node。

DRAWABLE_TYPE 指定drawable的类型,在C++对象向上转型时使用。DRAWABLE_LIGHT是光照,DRAWABLE_GEOMETRY是其他一般类型。

Octant和Octree是特别为Drawable元件准备的,它们按照空间位置给Drawable实例分组。Drawable实例要放置在一个Octant空间中。Octant的成员drawables_保存从属于它的这组Drawable实例。

Octant(卦限)是数学中的一个基本概念。在空间立体几何中,由相互垂直的坐标轴X轴、Y轴、Z轴,把整个空间划分成八个部分,其中每一部分称为一个卦限(Octant)。

这里的Octant差不多也是这个意思。 Octant的成员children_是一个Octant数组。Octant是嵌套的,所有的Octant组成一棵树。Octree就是这棵树的根,它是一个特殊的Octant。

Octant的成员worldBoudingBox_指定一个长方体空间,Drawable的成员boundingBox也指定一个长方体空间。向Octant实例中加入Drawable实例时,根据它俩的相对大小和位置决定,是直接加到这个Octant,还是加到这个Octant的子Octant中。

下图描述了Node、Component和Octant的从属关系。

1.1.2 创建Octant

将Drawable绑到Octant时,是否需要创建Octant的规则如下图所示。(Octant的空间是三维的,这里画的是二维的,道理一样)。

创建子Octant的规则如下。

1.1.3 创建Component/Drawable

Scene::CreateComponent()用于创建Component。这里以Drawable为例说明。Drawable实现了虚拟函数OnSceneSet()和OnMarkedDirty(),进行操作Octant相关操作。

1.2 创建StaticModel元件

以Urho3D的例子StaticScene为例。

在StaticScene::Start()中,

调用StaticScene::SetViewport()设置视口。

SourceBatch在它的成员geometry_中保存顶点数据。Drawable的成员batches_是一组SourceBatch实例。

调用StaticModel::SetModel()设置Model。

1.3 Renderer::Update()

Viewport负责将Scene,Camera和RenderPath组织在一起。

View与Viewport对等,它从Viewport分担了渲染的功能。调用Viewport::AllocateView(),可以从Viewport实例创建View实例。

Renderer持有一组Viewport/View实例,并将主要的渲染工作委托给View。

在Renderer::Update()中,从Renderer的成员viewports_创建View实例,保存在成员views_中。

1.3.1 View::Update()

View::Update()从Octant树收集Drawable实例,然后从它们创建BatchGroup实例,保存到BatchQueue中。后面就可以绘制这些BatchGroup实例了。

View::Update()的步骤如下图所示。

1.3.2 View::GetDrawables()

View::GetDrawables()按照FrustumOctreeQuery指定的过滤条件查询Octant树中的所有Drawable实例,将结果保存在成员geometries_中,这是一个Drawable数组。

1.3.3 Octree::GetDrawables()

Octree::GetDrawable()得到Octant树中的所有Drawable实例。

GetDrawable()调用GetDrawableInternal()。

Octree::GetDrawable()的参数是一个OctreeQuery实例,找到的Drawable实例保存在它的成员result_中。

OctreeQuery是个查询接口,定义了TestDrawable()和TEstOctant()两个虚拟函数。FrustumOctreeQuery和BoxOctreeQuery则是这个接口的实现,它们实现不同的过滤方式。FrustumOctreeQuery使用摄像头的锥头体,就是Frustum,裁剪Drawable实例。

Frustum定义了锥头体,它的成员planes_指定6个面,成员vertices_指定8个顶点。BoudingBox是长方体,Sphere是球体。

1.3.4 View::ProcessLights()

LightQueryResult的成员包括一个Light实例light_,以及从属于它的一组Drawable实例litGeometries_。light_是光照,光照可以只应用于litGeometries_中的特定顶点,也可以应用于所有顶点。

View::ProcessLights()根据View的成员lights_将View::geometries_中的Drawable实例分组,保存到成员lightQueryResults_中,这是一组LightQueryResult实例。

1.3.5 View::GetLightBatches()

Batch用于一次绘制操作的对象,从Drawable的成员batches_构造,batches_是一个SouceBatch数组。

BatchGroup从Batch构造,加入多实例化数据InstanceData。

BatchQueue保存一组BatchGroup,这是一个从BatchGroupKey到BatchGroup的映射。BatchGroupKey支持操作符 == 和 != ,所以可以用作映射的key。

LightBatchQueue包括与光照有关的几个BatchQueue,如litBaseBatches_和litBatches_。

View::GetLightBatches()遍历成员lightQueryResults_,对其中每个实例,又遍历LightQueryResult的成员litGeometries_。对其中保存的每个Drawable实例, 创建BatchGroup实例,保存到View::lightQueues_的成员litBaseBacthes_或litBatches_中。

GetLitBatches()的工作是:

AddBatchToQueue()的工作是:

1.4 Renderer::Render()

Renderer::Render()遍历成员views_中的实例,调用View::Render()进行渲染。

而View::Render()主要调用View::UpdateGeometries()和View::ExecuteRenderPathCommand()。

1.4.1 View::UpdateGeometries()

UpdateGeometries()负责渲染前的准备工作。

1.4.2 View::ExecuteRenderPathCommand()

URhoD将渲染过程组织成多条指令,也就是RenderPathCommand。

RenderTargetInfo是渲染目标。RenderPath将RenderTargetInfo和RenderPathCommand组合在一起。

View::ExecuteRenderPathCommand()遍历成员renderPath_中的指令,根据指定类型分别处理。

这里处理CMD_FORWARDLIGHTS指令。

1.4.3 BatchQueue::Draw()

BatchQueue::Draw()做真正的绘制工作。

BatchGroup::Draw()的工作如下:

这里的Graphics::Draw()使用glDrawElements()绘制。

1.5 数据流动

如下是按照以上渲染过程画的数据流动图。

相关链接
Urho3D 1.7.1 源代码分析 (一)
Urho3D 1.7.1 源代码分析 (二)
Urho3D 1.7.1 源代码分析 (三)
Urho3D 1.7.1 源代码分析 (四)
Urho3D 1.7.1 源代码分析 (五)

上一篇 下一篇

猜你喜欢

热点阅读