前言
我们在进行osg程序的开发时,最常用到的场景管理方式是“场景节点树”的结构,
a 场景树底端的叶节点(osg::Geode)包含了各种需要渲染的几何体的顶点和渲染状态信息;
b 组节点(osg::Group)及其派生出的各种特殊功能节点则作为场景树的各个枝节节点,它们也可以拥有不同的渲染状态;
c 整个场景的根节点(root)有且只有一个节点可以直接作为,使用setSceneData 将其设置给场景的视景器系统,即等同于将整个场景树传递给 OSG 的渲染和显示系统
d 节点所附带的渲染状态集(osg::StateSet)用来保存节点和几何体的各种渲染属性(osg::StateAttribute,例如纹理,雾效,材质,Alpha校验等)和模式开关,一个状态集中可以包含多种不同的渲染属性和开关,处于场景树底端的节点将继承并综合各级父节点的渲染状态,实现几何形状的正确渲染
场景视图(SceneView)成员简介
通过上面几节关于renderingTraversals的介绍,我们大概了解到OSG 渲染后台的主体是场景视图(SceneView),而场景视图(SceneView)同样实现了“树状结构”的管理方式,并据此实现了多个专用于渲染工作的内部类。所以我们先介绍一下场景视图(SceneView)中重要的集中内部类。
1、osgUtil::CullVisitor:“筛选访问器”。虽然同样是继承自 osg::NodeVisitor,不过这个访问器在整个 OSG 系统中可是起了举足轻重的作用。当我们使用它遍历场景图形的各个节点时,CullVisitor 将会对每一个遇到的节点执行场景筛选的工作,判断它是否会超出视截锥体范围,过于渺小,或者被遮挡节点(OccluderNode)挡住,从而将这些无助益于场景浏览的物体筛选并剔除,降低场景绘制的资源消耗。我们甚至可以使用 SceneView::setCullVisitor 来构建和指定使用自己设计的筛选访问器,不过CullVisitor只能在系统渲染后台的环境中使用。
2、osg::RenderInfo:“渲染信息”管理器。这个类负责保存和管理与场景绘制息息相关的几个重要数据:当前场景的视景器,当前场景对应的所有摄像机,以及当前所有 OpenGL 渲染状态和顶点数据(使用第十七日所述的 osg::State 类保存)。这些数据将在场景筛选和渲染时为 OSG 系统后台的工作提供重要依据。
3、osgUtil:: StateGraph: “状态节点”。我们可以对比场景树的组节点(Group),将 StateGraph理解为 OSG 渲染后台的组节点。它的组织结构与场景图形的节点结构类似,但是状态树的构建主要以节点的渲染状态集(StateSet)为依据:设置了 StateSet 的场景节点,其渲染状态会被记录到“状态节点”中,并保持它在原场景树中的相对位置;状态节点采用映射表std::map 来组织它的子节点,同一层次的子节点如果渲染状态相同,则合并到同一个“状态节点”中 。
4、osgUtil::RenderLeaf:“渲染叶”。我们可以把 RenderLeaf 理解为 OSG 渲染后台状态树的叶节点。但是,状态树的叶节点绝非等同于场景树的 Geode 节点;事实上,“渲染叶”的工作主要是记录场景树中存在的各种 Drawable 对象(以及与之相关的投影矩阵,模型视点矩阵等信息)。每个“状态节点”中都包含了一个渲染叶的列表(StateGraph::_leaves),不过只有最末端的“状态节点”会负责记录场景中的“渲染叶”。
5、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 显示,简单鹰眼图等功能时,应当向场景树中添加新的摄像机节点,并设置与主摄像机不同的观察矩阵和投影矩阵。
6、osgUtil::RenderBin:“渲染元”。它是 OSG 渲染树的分支节点,不过对于没有特殊要求的场景渲染来说,更多的渲染树分支也许并不需要:场景中需要渲染的元素及其渲染属性被保存到各个“状态节点”和“渲染叶”当中;渲染树只要按照遍历的顺序,把这些数据记录到作为根节点的“渲染台”当中(即分别保存到 std::vector 成员量 RenderBin::_stateGraphList和 RenderBin::_renderLeafList 当中,注意 RenderStage 派生自 RenderBin),就可以执行场景的绘制工作了。
自定义渲染顺序处理
很多时候我们需要某些几何体在其它对象之前被绘制,比如天空总是要被任何飞过的物体所遮挡;很多时候我们也需要在大部分对象绘制完成之后才绘制某个几何体的数据(例如 HUD 文字总是显示在所有对象之上)。这种情况下,就有必要对“渲染台”中的数据进行排序,甚至为其创建新的分支“渲染元”,以实现这种复杂的渲染顺序处理。
在程序中,渲染顺序通过 StateSet::setRenderBinDetails 实现设置。这个函数有两个传入参数,
参数1:整型数表示渲染的顺序,以 0 为标准,小于 0 的渲染状态集(亦即包含了这个StateSet的StateGraph状态节点)将排列在前,大于0的则排列在后;
参数2:字符串参数“RenderBin”或者“DepthSortedBin”作为名称时有特殊含义,其中“RenderBin”表示在渲染树中新建分支进行渲染,“DepthSortedBin”表示新建分支,并且所有要渲染的数据将按照深度值降序进行排序。
注意,当字符串参数不为“RenderBin”或“DepthSortedBin”时,渲染顺序的设定也是无效的;当字符串参数和整型参数均有效时,OSG 系统将尝试寻找同类型的渲染元节点并将 StateSet 记录到此“渲染元”中,或者创建新的“渲染元”节点。
1
2
3
4
5
6
7
8
9
10
11
|
// 缺省渲染方式,渲染顺序 0,此时状态节点直接置入“渲染台” stateSet->setRenderBinDetails( 0, “” ); // 渲染顺序-1(先渲染),此时渲染树中将新建一个“渲染元”节点 stateSet->setRenderBinDetails( -1, “RenderBin” ); // 渲染顺序 10,此时将新建一个“渲染元”,并按深度值降序排序各元素 stateSet->setRenderBinDetails( 10, “DepthSortedBin” ); |
为了简化操作,用户程序还可以使用 StateSet::setRenderingHint 来设置渲染的顺序,这个函数的传入参数可以为枚举量 OPAQUE_BIN 或 TRANSPARENT_BIN。前者可以指定该渲染状态用于不透明物体的渲染,后者则指定该渲染状态用于透明物体的渲染,此时 OSG自动将其渲染顺序置后,并设置它所管理的“状态节点”和“渲染叶”数据按照深度值降序进行排序。关于 setRenderBinDetails 与 setRenderingHint 的关系,也可以这样解释:
1
2
3
4
5
6
7
8
9
|
stateSet->setRenderingHint ( OPAQUE_BIN ); stateSet->setRenderingHint ( TRANSPARENT_BIN ); //分别等价于: stateSet->setRenderBinDetails( 0, “RenderBin” ); stateSet->setRenderBinDetails( 10, “DepthSortedBin” ); |