1.背景介绍
1.1 OSG介绍
OSG的全称:OpenSceneGraph,它是一个开放源码,跨平台的图形开发包,它为诸如飞行器仿真,游戏,虚拟现实,科学计算可视化这样的高性能图形应用程序开发而设计。
它基于场景图的概念,它提供一个在OpenGL之上的面向对象的框架,从而能把开发者从实现和优化底层图形的调用中解脱出来,并且它为图形应用程序的快速开发提供很多附加的实用工具。
基于OSG的典型应用:Delta3D(美国海军研究院开发的全功能游戏与仿真引擎)、osgEarth(三维数字地球引擎库)
1.2 LOD介绍
是指根据物体模型的结点在显示环境中所处的位置和重要度,决定物体渲染的资源分配,降低非重要物体的面数和细节度,从而获得高效率的渲染运算。在OSG的场景结点组织结构中,专门提供了场景结点osg::LOD来表达不同的细节层次模型。其中,osg::LOD结点作为父节点,每个子节点作为一个细节层次,设置不同的视域,在不同的视域下显示相应的子节点。
主要3个过程: 生成,切换,选择。
最主要的问题:如何实现平滑过渡。
实现方式:最差的方式是后台准备多个模型,根据距离直接切换,缺点是切换时感觉突兀。
(1)线性混合方式:对两个离散层级的模型进行混合,缺点是加大系统内存和计算开销
(2)透明度混合:在临切时,一个逐步透明,另一个逐步显示,淡入淡出。
(3)最优方式:实时算法,将相邻顶点或边进行合并,靠算法进行模型数据坍处理,逐步坍塌顶点,离得越远,模型越小。
一般LOD都是根据距离来做切换过渡的,少数基于时间和投影面积等其他特定需求的.
2.渲染框架
2.1 帧渲染机制
- Update 更新 Cull 筛选 Draw 绘制
- 渲染帧:每一次场景更新,筛选,绘制的过程。
- advance():一帧经历的时间、帧数以及弃用对象的删除,获取帧率及显示状态信息等;
- eventTraversal():执行用户设置的EventCallback,为所有的用户交互和系统事件提供一个响应的机制; 它必须在每一帧的仿真过程中,取出已经发生的所有事件,摒弃那些对场景不会有助益的(例如,在视口以外发生的鼠标移动事件和胡乱点击),依次交付给各个事件处理器 ( EventVistor事件遍历器),最后清空现有的事件队列eventQueue ,等待下一帧的到来。
- updateTraversal():处理用户的更新回调对象之外,还要负责更新摄像机的位置,并且更新分页数据库DatabasePager 和图像库ImagePager 的内容。
- 更新回调与事件回调最大的不同在于:每当一个用户交互或系统事件产生时,每一个节点(以及Drawable 对象)的事件回调都会被调用一次;而节点(以及Drawable 对象)的更新回调只会在每帧中被调用一次( NodeVistor 节点遍历器)。这一区别决定了我们应当在什么时候使用事件回调,以及在什么时候使用更新回调。
- renderingTraversals():场景的渲染遍历工作。
2.2 更新机制
2.3 视景管理
2.4 数据分页管理
分为DatabasePager管理和ImagePager管理。
在三维场景中可以采用数据分页的方式进行动态调度。这里“分页”的意思是随着视口范围的变化,场景只加载和渲染当前视口范围内数据,并将离开视口范围内的数据清除内存(可以设定不同的数据卸载策略),不再渲染。保证内存中只有有限的数据量,场景的每一帧也只有有限的数据被送到图形渲染管道,从而提高渲染性能。
执行时机
在每一帧刷新的节点更新回调中处理
动态调度
OSG源代码中提供PagedLOD来进行模型的动态调度。在不同的视域下,PagedLOD动态读取不同细节层次的结点模型,实现了分页LOD显示。OSG内部采用osgDB::DatabasePager类来管理场景结点的动态调度,场景循环每一帧的时候,会将一段时间内不在当前视图范围内的场景子树卸载掉,并加载新进入到当前视图范围的新场景子树。OSG采用了多线程的方式来完成上述工作。
具体工作步骤:
- 在每一帧中通过一个UpdateSceneGraph()来进行动态调度。
- DatabasePager::removeExpiredSubgraphs:用于去除已经过期的场景子树;
- DatabasePager::addLoadedDataToSceneGraph:用于向场景图形中添加新载入的数据。osgDB::DatabasePager 类执行的是这一工作:每一帧的更新遍历执行到updateSceneGraph 函数时,都会自动将“一段时间之内始终不在当前页面上”的场景子树去除,并将“新载入到当前页面”的场景子树加入渲染,这里所说的“页面”往往指的就是用户的视野范围。这些分页和节点管理的工作如果由渲染循环来完成的话,恐怕是费时又费力的,对于场景的显示速度有较大的影响,因此,DatabasePager 中内置了专用于相关工作处理的DatabaseThread 线程。
2.5 DatabaseThread工作机制
场景主线程交互逻辑
2.6 渲染消息处理流程
使用EventQueue::TakeEvents函数,把GraphicsWindow图形窗口对象gw的事件队列保存到指定的变量 gw_events中。