array 发表于 2008-9-18 13:34:47

OSG原创教程:最长的一帧(25)

当前位置:osgUtil/RenderBin.cpp第387行,osgUtil::RenderBin::drawImplementation ()
根据上一日所述,我们首先得到OSG渲染后台中渲染树(RenderStage/RenderBin),场景树(StateGraph/RenderLeaf),状态机(State),渲染属性(StateAttribute的诸多派生类)和几何体(Drawable)之间的关系图,如下所示:
附图1

图中浅蓝色的箭头表示状态机对象中保存的各种OpenGL状态,即渲染属性的数据(例如Alpha检测,纹理,雾效等),模式数据(种种使用glEnable/glDisable开启或关闭的模式),以及顶点坐标、法线坐标、颜色坐标、纹理坐标,以及数据索引的数据。这些OpenGL编程中经常用到的概念在OSG中被良好地封装起来,而osg::State类就是它们的具体实现者。

如果我们需要自己创建新的派生自Drawable的对象(就像osgText中所实现的),或者自己创建一种新的渲染属性(派生自StateAttribute),那么图中同样介绍了一些值得注意和借鉴的地方:Drawable几何体对象的具体实现在于drawImplementation函数(事实上是通过draw函数间接调用的);而渲染属性的具现函数为StateAttribute::apply(State&),所有的渲染属性都重写了这一函数,以实现自己的功能。

了解了这些概念之后,事实上我们已经对OSG的渲染流程有了大体的认识,即:

[*]1、渲染树的作用是遍历各个渲染元(RenderBin),并按照指定的顺序执行其中各个渲染叶的渲染函数(RenderLeaf::render)。
[*]2、状态树保存了从根节点到当前渲染叶的路径,遍历这条路径并收集所有的渲染属性数据(StateGraph/moveStateGraph),即可获得当前渲染叶渲染所需的所有OpenGL状态数据。
[*]3、渲染叶的渲染函数负责向状态机(osg::State)传递渲染状态数据,进而由渲染属性类本身完成参数在OpenGL中的注册和加载工作;渲染叶还负责调用几何体(Drawable)的绘制函数,传递顶点和索引数据并完成场景的绘制工作。


下面我们很快地浏览一遍代码,并即将骄傲地宣布对于OSG场景绘制模块的解读告一段落。

当前位置:osgUtil/RenderBin.cpp第387行,osgUtil::RenderBin::drawImplementation ()
执行流程如下:
1、首先判断当前RenderBin在渲染树中的位置,并在此位置临时插入一个新的渲染状态RenderBin::_stateset。对于透明渲染元(TRANSPARENT_BIN),此渲染状态会自动设置一个Alpha检测属性(osg::AlphaFunc),以便自动剔除绘制结果中颜色Alpha分量为0的像素。因此,我们可以直接指定某个几何体的StateSet为TRANSPARENT_BIN,从而自动实现背景透明的效果(如果纹理或者颜色的Alpha值设置正确的话)。

2、遍历所有的子渲染元(RenderBin::_bins),其中渲染顺序号小于0的渲染元将在这里执行它们的RenderBin::draw函数,由于draw函数内部调用了drawImplementation,因此这构成了一个递归调用,直至渲染树遍历至末端节点。在用户程序中,渲染顺序号的设置使用StateSet::setRenderBinDetails函数。

3、遍历当前RenderBin所保存的所有渲染叶(RenderBin::_renderLeafList),执行RenderLeaf::render函数,实现场景的绘制。通常只有被设置为“DepthSortedBin”的渲染元会选择保存渲染叶而非状态节点(StateGraph),因为这样便于按照深度值排序对象。

4、遍历当前RenderBin所保存的所有状态节点(RenderBin::_stateGraphList),获取其中保存的RenderLeaf对象(保存为StateGraph::_leaves),并执行其render函数。

5、遍历所有的子渲染元(RenderBin::_bins),其中渲染顺序号大于0的渲染元此时才执行它们的RenderBin::draw函数。

由此可知,渲染树中最先被绘制的将是那些顺序号小于0的末端RenderBin节点,其次则依次是顺序号等于0的末端节点,大于0的末端节点,小于0的倒数第二级节点……而作为渲染树根节点的RenderStage中保存的数据将最后被渲染。

如果在渲染树的同一层中顺序号小于0(或大于0)的渲染元不止一个,那么它们会按照顺序号从小到大的顺序依次被渲染,这是由于RenderBin::_bins变量是std::map的类型,在顺序遍历时会自动进行数据的排列。

渲染树同一层中不可能存在渲染顺序号相同的渲染元,因为使用setRenderBinDetails设置了相同数字参量的StateSet对象被构建成状态节点(StateGraph)之后,将插入到同一个RenderBin中。

当前位置:osgUtil/RenderLeaf.cpp第20行,osgUtil::RenderLeaf::render ()
前文中已经反复提到,渲染叶RenderLeaf是OSG渲染后台中几何体(Drawable)对象的唯一管理者;而节点树的构建,开关、变换和LOD等节点类型的应用,渲染状态的设置等工作,最终都要归结到几何体的渲染上来。而这里的render函数主要负责获取之前保存的Drawable指针,投影矩阵,模型视点矩阵,深度值等信息(传递这些信息的是第二十二日中提到的CullVisitor::addDrawableAndDepth函数),并将它们传递给负责渲染状态处理的State类,以及执行Drawable::draw函数。

它的工作流程概括如下:

[*]1、使用State::applyProjectionMatrix传递投影矩阵。
[*]2、使用State::applyModelViewMatrix传递模型视点矩阵。
[*]3、如果当前渲染叶与上一次处理的渲染叶父节点不同,则需要遍历状态树中相应的路径,并更新State状态机中保存的渲染状态数据(采用std::map类型,分别名为_modeMap和_attributeMap)。用于更新的函数为StateGraph::moveStateGraph,它负责清除上一次使用的各种渲染状态,再沿着状态树中的路径,依次添加当前渲染叶所需的数据。最后执行函数State::apply(const StateSet*),由OSG状态机处理并执行相应的OpenGL指令。
[*]4、如果当前渲染叶与上一次处理的渲染叶有相同的父节点(StateGraph对象),则不改变传入State状态机的状态数据,直接执行State::apply函数。
[*]5、执行此渲染叶所保存的Drawable对象的draw函数,完成几何体的绘制。Geometry对象将在这一函数中(实际上是Drawable::drawImplementation)向状态机传递顶点和索引数据,并交由状态机对象来完成几何数据的绘制。


解读成果:
RenderLeaf::render,RenderBin::drawImplementation。
悬疑列表:
Operation对象在线程中的应用时机是什么?

shengrendan2 发表于 2008-9-28 13:03:37

:Q
页: [1]
查看完整版本: OSG原创教程:最长的一帧(25)