查看: 5249|回复: 1

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

[复制链接]

该用户从未签到

发表于 2008-9-8 13:24:22 | 显示全部楼层 |阅读模式
当前位置:osgUtil/SceneView.cpp第670行,osgUtil:: SceneView::cull ()
我们在进行用户程序的开发时,最常用到的场景管理方式是“场景节点树”的结构,场景树顶端的叶节点(osg::Geode)包含了各种需要渲染的几何体的顶点和渲染状态信息;而组节点(osg::Group)及其派生出的各种特殊功能节点则作为场景树的各个枝节节点,它们也可以拥有不同的渲染状态;有且只有一个节点可以直接作为整个场景的根节点,使用setSceneData将其设置给场景的视景器系统,即等同于将整个场景树传递给OSG的渲染和显示系统。

而保存节点和几何体的各种渲染属性(osg::StateAttribute,例如纹理,雾效,材质,Alpha校验等)和模式开关,则使用节电所附带的渲染状态集(osg::StateSet)。一个状态集中可以包含多种不同的渲染属性和开关,处于场景树顶端的节点将继承并综合各级父节点的渲染状态,实现几何形状的正确渲染。

OSG渲染后台的主体是场景视图(SceneView),它同样实现了“树状结构”的管理方式,并据此实现了多个专用于渲染工作的内部类。那么在深入介绍场景视图之前,我们先来认识一下OSG渲染后台的几个“幕后英雄”:

osgUtil::CullVisitor:“筛选访问器”。虽然同样是继承自osg::NodeVisitor,不过这个访问器在整个OSG系统中可是起了举足轻重的作用。当我们使用它遍历场景图形的各个节点时,CullVisitor将会对每一个遇到的节点执行场景筛选的工作,判断它是否会超出视截锥体范围,过于渺小,或者被遮挡节点(OccluderNode)挡住,从而将无助益于场景浏览的物体筛选并剔除,降低场景绘制的资源消耗。我们甚至可以使用SceneView::setCullVisitor来构建和指定使用自己设计的筛选访问器,不过在系统渲染后台之外的环境使用CullVisitor通常并无用处。

osg::RenderInfo:“渲染信息”管理器。这个类负责保存和管理与场景绘制息息相关的几个重要数据:当前场景的视景器,当前场景对应的所有摄像机,以及当前所有OpenGL渲染状态和顶点数据(使用第十七日所述的osg::State类保存)。这些数据将在场景筛选和渲染时为OSG系统后台的工作提供重要依据。

osgUtil:: StateGraph:“状态节点”。我们可以对比场景树的组节点(Group),将StateGraph理解为OSG渲染后台的组节点。它的组织结构与场景图形的节点结构类似,但是状态树的构建主要以节点的渲染状态集(StateSet)为依据:设置了StateSet的场景节点,其渲染状态会被记录到“状态节点”中,并保持它在原场景树中的相对位置;状态节点采用映射表std::map来组织它的子节点,同一层次的子节点如果渲染状态相同,则合并到同一个“状态节点”中。

osgUtil::RenderLeaf:“渲染叶”。我们可以把RenderLeaf理解为OSG渲染后台状态树的叶节点。但是,状态树的叶节点绝非等同于场景树的Geode节点;事实上,“渲染叶”的工作主要是记录场景树中存在的各种Drawable对象(以及与之相关的投影矩阵,模型视点矩阵等信息)。

每个“状态节点”中都包含了一个渲染叶的列表(StateGraph::_leaves),不过只有最末端的“状态节点”会负责记录场景中的“渲染叶”。

osgUtil::RenderStage:“渲染台”。OSG的渲染后台除了使用“状态树”来组织和优化节点的渲染状态之外,还有另外一种用于场景实际渲染的组织结构,我们称之为“渲染树”,“渲染树”的根节点就是“渲染台”。

通常来说,由于OSG后台只有一个渲染树结构,因此应当也只有一个“渲染台”存在;不过OSG还提供了“设置摄像机渲染顺序”的功能,即Camera::setRenderOrder。设置为PRE_RENDER的摄像机子树将在主摄像机之前执行渲染,通常我们可以由此实现诸如“纹理烘焙”(Render To Texture)的高级功能(参见osgprerender例子);设置为POST_RENDER的摄像机子树将在主摄像机之后执行渲染,一些必须在最后进行渲染的场景对象,例如HUD显示牌,可以置为这类摄像机的子节点。

注意,把视景器(Viewer)的主/从摄像机设置为PRE_RENDER或POST_RENDER往往是没有意义的。所谓从摄像机组(View::_slaves),其功能主要是实现同一场景的分窗口以及分屏幕显示(参见osgcamera例子)。如果您希望实现诸如HUD显示,简单鹰眼图等功能时,应当向场景树中添加新的摄像机节点,并设置与主摄像机不同的观察矩阵和投影矩阵。

osgUtil::RenderBin:“渲染元”。它是OSG渲染树的分支节点,不过对于没有特殊要求的场景渲染来说,更多的渲染树分支也许并不需要:场景中需要渲染的元素及其渲染属性被保存到各个“状态节点”和“渲染叶”当中;渲染树只要按照遍历的顺序,把这些数据记录到作为根节点的“渲染台”当中(即分别保存到std::vector成员量RenderBin::_stateGraphList和RenderBin::_renderLeafList当中,注意RenderStage派生自RenderBin),就可以执行场景的绘制工作了。

但是,很多时候我们需要某些几何体在其它对象之前被绘制,比如天空总是要被任何飞过的物体所遮挡;很多时候我们也需要在大部分对象绘制完成之后才绘制某个几何体的数据(例如HUD文字总是显示在所有对象之上)。这种情况下,就有必要对“渲染台”中的数据进行排序,甚至为其创建新的分支“渲染元”,以实现这种复杂的渲染顺序处理。

在用户程序中,渲染顺序通过StateSet::setRenderBinDetails实现设置。这个函数有两个传入参数,整型数表示渲染的顺序,以0为标准,小于0的渲染状态集(亦即包含了这个StateSet的StateGraph状态节点)将排列在前,大于0的则排列在后;字符串参数“RenderBin”或者“DepthSortedBin”作为名称时有特殊含义,其中“RenderBin”表示在渲染树中新建分支进行渲染,“DepthSortedBin”表示新建分支,并且所有要渲染的数据将按照深度值升序进行排序。

注意,当字符串参数不为“RenderBin”或“DepthSortedBin”时,渲染顺序的设定也是无效的;当字符串参数和整型参数均有效时,OSG系统将尝试寻找同类型的渲染元节点并将StateSet记录到此“渲染元”中,或者创建新的“渲染元”节点。相关的示例代码如下:
  1. // 缺省渲染方式,渲染顺序0,此时状态节点直接置入“渲染台”
  2. stateSet->setRenderBinDetails( 0, “” );
  3. // 渲染顺序-1(先渲染),此时渲染树中将新建一个“渲染元”节点
  4. stateSet->setRenderBinDetails( -1, “RenderBin” );
  5. // 渲染顺序10,此时将新建一个“渲染元”,并按深度值升序排序各元素
  6. stateSet->setRenderBinDetails( 10, “DepthSortedBin” );
复制代码
为了简化操作,用户程序还可以使用StateSet::setRenderingHint来设置渲染的顺序,这个函数的传入参数可以为枚举量OPAQUE_BIN或TRANSPARENT_BIN。前者可以指定该渲染状态用于不透明物体的渲染,后者则指定该渲染状态用于透明物体的渲染,此时OSG自动将其渲染顺序置后,并设置它所管理的“状态节点”和“渲染叶”数据按照深度值升序进行排序。
关于setRenderBinDetails与setRenderingHint的关系,也可以这样解释:
  1. stateSet->setRenderingHint ( OPAQUE_BIN );
  2. stateSet->setRenderingHint ( TRANSPARENT_BIN );
复制代码
分别等价于:
  1. stateSet->setRenderBinDetails( 0, “RenderBin” );
  2. stateSet->setRenderBinDetails( 10, “DepthSortedBin” );
复制代码
状态树,渲染树,还有那些名字看起来都差不多的类……一口气出现这么多新事物,您是不是有点晕头转向了?没关系,也许下一日的一个场景实例就可以稍微帮您理清思路:

解读成果:
Camera::setRenderOrder,StateSet::setRenderBinDetail,StateSet::setRenderingHint。
悬疑列表:
什么是“渲染目标实现方式”?Operation对象在线程中的应用时机是什么?全局渲染状态_globalStateSet有什么作用?

该用户从未签到

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

本版积分规则

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

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

联系我们

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