查看: 3606|回复: 1

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

[复制链接]

该用户从未签到

发表于 2008-9-10 14:12:40 | 显示全部楼层 |阅读模式
当前位置:osgUtil/SceneView.cpp第670行,osgUtil:: SceneView::cull ()
有了前面两日的基础之后,我们从今日开始阅读场景视图SceneView的源代码。

场景的筛选函数cull主要完成了以下几个工作:
  • 1、初始化必要的SceneView类成员变量,包括该视图的渲染信息(_renderInfo),筛选访问器(_cullVisitor),状态树根节点(_stateGraph)和渲染树根节点(_renderStage)。此外还有局部渲染状态_localStateSet的更新(SceneView::updateUniforms),更新的主要内容是一些内设的osg::Uniform着色器变量(osg_FrameNumber,osg_FrameTime,osg_DeltaFrameTime,osg_SimulationTime,osg_ViewMatrix,osg_ViewMatrixInverse)。我们可以在编写GLSL程序时调用这些变量获取OSG提供的一些场景和时间方面的信息。
  • 2、如果之前我们设置了立体显示的选项(参见第三日的相关内容),那么此时OSG会针对左/右眼(osg:: DisplaySettings:: LEFT_EYE/RIGHT_EYE)以其它各种设置做出适当的处理,相关的函数包括SceneView类成员computeLeftEyeProjection,computeLeftEyeView,computeRightEyeProjection,computeRightEyeView等,这里不再做深入的研究。
  • 3、执行SceneView::cullStage函数,它也是场景视图筛选工作的核心函数。
  • 4、执行CullVisitor::clampProjectionMatrix,根据远/近平面的取值,重新设定场景视图的投影矩阵。由于远/近平面是由筛选访问器计算出来的,有的时候我们可能不希望按照它的计算值来进行视图的处理,此时可以使用setClampProjectionMatrixCallback设置SceneView的投影矩阵计算回调,自己编写相关的处理函数。


当前位置:osgUtil/SceneView.cpp第805行,osgUtil:: SceneView::cullStage ()
这个函数的工作流程如下:

1、首先统计场景中的遮挡节点(OccluderNode),并使用CollectOccludersVisitor访问器遍历场景中的所有节点。代码如下:
  1. for(unsigned int i=0; i< _camera->getNumChildren(); ++i)
  2.   _camera->getChild(i)->accept(*_collectOccludersVisitor);
复制代码
注意,SceneView::_camera的值取自视景器的主摄像机,视景器的setSceneData函数中,会自动场景根节点设置为各个主/从摄像机的子节点(使用View::assignSceneDataToCameras函数),因此这里访问器的accept函数将遍历场景中所有的节点(815-856行)。

2、将筛选所需的数据送入筛选访问器(CullVisitor),包括筛选设置(CullSettings),状态树根节点(StateGraph),渲染树根节点(RenderStage),渲染信息(RenderInfo)。注意此时状态树和渲染树还没有生成,我们还要设置渲染树构建所需的各种信息(860-910行)。

3、下一步,首先将上一日提到的“全局状态节点”和“局部状态节点”追加到状态树中。这里用到了两个重要的函数CullVisitor::pushStateSet和popStateSet。马上我们就对它们加以介绍。

4、使用筛选访问器遍历场景中的节点,在遍历过程中将筛选出那些无法被用户看到的对象,并将它们裁减掉,从而提高场景绘制的效率。因此,下面这段代码可以说是整个场景筛选过程的核心了:
  1. for(unsigned int childNo=0; childNo<_camera->getNumChildren(); ++childNo)
  2.   _camera->getChild(childNo)->accept(*cullVisitor);
复制代码
对它的深入研究也将随后进行。

5、依次执行RenderStage::sort和StateGraph::prune函数,对筛选访问器执行之后得到的渲染树内容进行排序和精简(构建过程中可能有些空节点需要剔除)。

6、最后,计算出场景中动态对象(DYNAMIC)的数目,并保存到SceneView的成员变量_dynamicObjectCount中。如果您还记得我们在第十八日中讨论的内容的话就会知道,这个变量将随后被SceneView::getDynamicObjectCount函数获取,并用于多线程模式下渲染线程与场景更新的协调控制。

当前位置:osgUtil/CullVisitor第103行,osgUtil::CullVisitor::pushStateSet()
状态树与渲染树的构建都是在pushStateSet和popStateSet函数中完成的。而CullVisitor::apply函数(在遍历节点时调用)则负责根据不同的节点类型,在不同的时机调用这两个函数。那么下面我们就看一下pushStateSet函数的实现过程。

pushStateSet完成的主要工作有(传入参数StateSet* ss):

1、状态树的构建。判断传入的渲染状态ss是否已经存在于某个状态节点中,并将状态树的当前位置(CullVisitor::_currentStateGraph)转到那个节点或者新建一个包含了ss的状态节点(StateGraph::find_or_insert的工作),即:
  1. _currentStateGraph = _currentStateGraph->find_or_insert(ss);
复制代码
2、渲染树的构建。创建新的渲染树节点(渲染元)有三个条件:一是渲染状态没有采用覆盖渲染细节(OVERRIDE_RENDERBIN_DETAILS)的方式(由setRenderBinMode函数设置),二是使用setRenderBinDetails设置了渲染细节,三是渲染细节的字符串名称不为空(事实上也不能随意写,只能为“RenderBin”或“DepthSortedBin”)。如果不满足这些条件的话,渲染树的当前位置(CullVisitor::_currentRenderBin)就不会发生变化;否则将尝试转到指定的节点或者新建一个渲染元(RenderBin::find_or_insert的工作),并使用堆栈记录上一次在渲染树中的位置,即:
  1. _renderBinStack.push_back(_currentRenderBin);
  2. _currentRenderBin = _currentRenderBin->find_or_insert(ss->getBinNumber(),ss->getBinName());
复制代码
此外,渲染树的构建过程中只生成空的渲染元(RenderBin)节点,向其中纳入状态节点和渲染叶的任务将在后面的工作中完成。

popStateSet的任务正好与pushStateSet相反:
1、从堆栈中取出上一次渲染树中所处的渲染元节点,并跳转到这一位置,即:
  1. _currentRenderBin = _renderBinStack.back();
  2. _renderBinStack.pop_back();
复制代码
2、状态树从当前位置跳转到其父节点,即:
  1. _currentStateGraph = _currentStateGraph->_parent;
复制代码
想象一下,如果我们在遍历场景节点树时,使用pushStateSet将某个节点的渲染状态置入,然后再将它的子节点的渲染状态置入,如此反复……结束这个子树的遍历时,则依次使用popStateSet弹出_currentRenderBin和_currentStateGraph,直到返回初始位置为止。如此即可在遍历节点子树的过程中构建起渲染后台的状态树和渲染树;并且,假如在筛选(CULL)过程中我们判断某个节点(及其子树)应当被剔除掉时,只要跳过pushStateSet和popStateSet的步骤,直接返回,就不会在渲染时留下节点的任何蛛丝马迹。

没错,这就是CullVisitor的工作了,也是我们下一日将要介绍的内容。

解读成果:
SceneView::cullStage,CullVisitor::pushStateSet,CullVisitor::popStateSet。
悬疑列表:
什么是“渲染目标实现方式”?Operation对象在线程中的应用时机是什么?

该用户从未签到

发表于 2008-9-27 17:29:11 | 显示全部楼层
您需要登录后才可以回帖 登录 | 注册

本版积分规则

OSG中国官方论坛-有您OSG在中国才更好

网站简介:osgChina是国内首个三维相关技术开源社区,旨在为国内更多的技术开发人员提供最前沿的技术资讯,为更多的三维从业者提供一个学习、交流的技术平台。

联系我们

  • 工作时间:09:00--18:00
  • 反馈邮箱:1315785073@qq.com
快速回复 返回顶部 返回列表