APP下载

基于OSG的三维引擎关键技术研究

2013-05-11

微型电脑应用 2013年10期
关键词:视图引擎摄像机

曹 莉

0 引言

目前,在虚拟现实领域中,人们多数采用OpenGL的开发接口进行3D应用程序开发。这种开发接口仍然停留在几何体级别的操作层面,是一种明显带有面向过程特征的API,这使得系统在开发后期变得庞大而复杂,从而给系统维护和修改带来难度。

OSG(Open Scene Graph)是一个跨平台的开源的图形开发包,它基于场景图的概念,提供一个构建于OpenGL函数库之上的面向对象的框架,为图形程序的开发提供场景管理和渲染优化功能,从而使系统开发脱离几何体操作,转而处理具体的场景。

1 三维引擎概述

三维引擎作为一个名词已存在了很多年,但即使是一些专业的引擎设计师,也很难就它的定义达成一个共识。通常来说,三维引擎可以把它看成是对3D API的封装,并包含有对一些图形通用算法以及底层工具的封装。

从功能的角度来定义三维引擎,更能确切的表达出一个三维引擎的真实含义。一个三维引擎最基本的功能应该包括:

(l)对三维场景的数据管理:这里的数据管理是一个比较广泛的定义,不同的三维引擎也许会拥有其中一个或多个功能。这些功能包括:场景管理,对象系统,序列化,数据与外部工具的交互,底层三维数据的组织和表示。

(2)功能合理的渲染器:由于一个引擎的渲染能力是由多方面决定的,因此在设计初期需考虑到不同的开发项目有着不同的需求,在渲染器的功能选择上也应有所侧重。比如一款以实时游戏作为目标的游戏,会选择基于光栅化的渲染算法。

(3)与虚拟世界的交互能力:交互性是虚拟现实也是三维引擎的一个必需的要素,只有通过与虚拟世界的交互,才能使虚拟现实看上去更逼真,使人们产生沉浸感。

任何包含了上面三种功能的引擎,就可以称为三维引擎。而开发功能强大的引擎,则需要实现更多更庞大的功能。

2 OSG的基本理论

2.1 OSG场景管理

OSG采用场景图结构,如图1所示:

图1 场景图结构

来管理场景,它是一种自顶向下的,分层的树状数据结构。该结构以根节点表示整个三维场景;以组节点(osg::Group)表示物体属性信息,如矩阵变换、状态切换、细节层次等,在实现过程中,又以组节点为基类派生出变换节点(osg::Transform),开关节点(osg::Switch),细节层次节点(osg::LOD)等类,对属性信息进行分别管理;而叶子节点(osg::Geode)则代表物理对象本身或几何模型。这种结构既反映了场景的空间结构,也反映了对象的状态,便于提取数据节点之间的共有行为和属性。

场景图是一种中间件(middleware),它构建于底层API函数之上,提供了高性能3D程序所需的空间数据组织能力及其它特性,表现了一个典型的OSG程序层次结构,如图2所示:

图2 3D程序层次结构

2.2 OSG场景渲染

一个场景图系统执行绘图遍历时,所有几何体是以OpenGL指令的形式发送到硬件设备上。这种机制无法实现诸如细节层次、渲染特效等高级特性。为了实现动态的几何体更新,拣选,排序和高效渲染,OSG场景图形提供3个遍历过程:

更新:更新遍历允许修改几何体、渲染状态,或者节点参数,保证场景图形的更新对应当前帧;

拣选:在拣选遍历中检查可见性,将几何体和状态量置入新的结构(在OSG中称为渲染图形,render graph)之中。

绘制:在绘制遍历中(有时也称作渲染遍历),场景图形将遍历由拣选遍历过程生成的几何体列表,并调用底层API,实现几何体的渲染。

一般地,这3种遍历操作在每一个渲染帧中只执行一次,但有时一些渲染特例需要将同一个场景(不同或相同部分)在多个视口中进行同步显示,这样更新遍历仍然只执行一次,但拣选和绘制遍历则需要在每个视口内各执行一次,以保证多处理器和多显卡的系统实现并行场景图形处理。

2.3 智能指针

在OSG中,智能指针(Smart pointer)的概念指的是一种类的模板,它针对某一特定类型的对象(即Referenced类及其派生类)构建,提供了了一种内存自动释放的机制,即,场景图形中的每一个节点均关联一个内存计数器,当计数器的计数减到零时,该对象将被自动释放。

由于OSG中与场景图形有关的大多数类均派生自Referenced类,因此OSG大量使用了智能指针来实现场景图形节点的管理。当用户希望释放整个场景图形的节点时,只需要删除根节点,则根节点以下的所有分支节点均会被自动删除,从而避免内存泄漏错误。

3 基于OSG的三维引擎关键技术

3.1 将OSG嵌入MFC

OSG不能直接嵌入MFC窗口,要用摄像机类来支持。OSG使用第三方的Producer接口来实现MFC方面的GUI。

Producer接口提供有Camera类,它的原理,如图3所示:

图3 osgProducer接口实现嵌入图

Render Surface是Camera的一个成员对象,它相当于计算机的屏幕。摄像机拍到的影像就投放在该面的投影矩阵上。要将OSG窗口嵌入到MFC中,也同样用Render Surface来设置。使用Camera对象的getRenderSurface()方法可以获取到Render Surface,并返回一个RenderSurface*的对象指针。在OSG中如果该对象是引用类派生出来,则可以使用osg::ref_ptr<>模版类来创建一个智能指针对象。

新版的OSG中,已经取消了关于Producer接口的应用,但在实现将OSG窗口嵌入到MFC过程中,用到的原理是相似的,只是更加简单的通过一个自己定义的类cOSG来实现。实现过程如下:

(1)初始化操作器

由void cOSG::InitManipulators(void)实现,具体过程是首先创建一个trackball的控制器驱动,并设置它的高度。再创建一个控制器转换器,之后将trackball的控制器添加到转换控制器中,初始化转换器,在这个程序中选择用第一个模式的控制器,值为0。

(2)初始化场景图

由void cOSG::InitSceneGraph(void)实现,具体过程是首先初始化主要的节点,组,之后完善模型,再将模型添加到场景中。

(3)初始化摄像机

由void cOSG::InitCameraConfig(void)实现,具体过程是首先获取本地的窗口大小矩阵,创建windows的视图,之后在视图中添加一个事件处理控制。然后获取对话框的宽高,利用智能指针初始化GraphicsContext Traits。再引用窗口句柄获取MFC窗口,并设置窗口大小。利用Traits设置视图矩阵,设置窗口场景,把OSG窗口绑定到viewer中。创建场景上下文,设置画图属性。

(4)配置摄像机

初始化一个新的摄像机对象,为摄像机定义场景上下文,设置视口接口。然后将摄像机添加到视图中,将摄像机的控制器加到视图中。

3.2 摄像机控制

OSG的摄像机操作,由osgGA::MatrixManipulator的派生类来完成。要实现摄像机控制,关键是实现osgGA::MatrixManipulator类的以下5个纯虚函数:

(1)virtual void setByMatrix(const osg::Matrixd&matrix);

这个函数在从一个摄像机切换到另一个摄像机时调用,用来把上一个摄像机的视图矩阵传过来,这样就可依此设定自己的初始位置了。重载setByMatrix函数实现过程:

void CMyManipulator::setByMatrix(const osg::Matrixd&matrix)

{//设置摄像机初始位置

_eye=matrix.getTrans();

_rotation=matrix.getRotate();

}

(2)virtual osg::Matrixd getMatrix()const;

SetByMatrix方法需要的矩阵需要用这个方法得到,用来向下一个摄像机传递矩阵。重载getMatrix函数实现过程:

osg::Matrixd CMyManipulator::getMatrix()const

{

return

osg::Matrixd::rotate(_pitch,1.0,0.0,0.0)*osg::Matrixd::rotate(_r otation)*osg::Matrixd::translate(_eye);

}

(3)virtual osg::Matrixd getInverseMatrix()const;

这个是最重要的方法,在每帧中都会被调用,它返回当前的视图矩阵。重载getInverseMatrix函数实现过程:

osg::Matrixd CMyManipulator::

getInverseMatrix()const

{

return

osg::Matrixd::translate(-_eye)*osg::Matrixd::rotate(_rotation.in verse())*osg::Matrixd::rotate(-_pitch,1.0,0.0,0.0);}

其中osg::Matrixd::translate(-_eye)表示平移矩阵,Matrixd::rotate(_rotation.inverse())表示水平旋转矩阵,Matrixd::rotate(-_pitch,1.0,0.0,0.0)表示竖直旋转矩阵。

(4)virtual void setByInverseMatrix(const osg::Matrixd&matrix);

这个方法是当在外部直接调用Viewer的setViewByMatrix方法时,把设置的矩阵传过来,让摄像机记录新更改的位置,本系统中不需要实现。

(5)virtual bool handle(const osg-GA::GUIEventAdapter&ea,osgGA::GUIActionAdapter&us)

这个方法是进行事件的处理,方法中第一个参数是GUI事件的供给者,第二个参数用来使handle方法对GUI进行反馈,它可以让GUIEventHandler根据输入事件让GUI执行一些动作。如果要进行事件处理,可以从GUIEventHandler派生出自己的类,然后覆盖handle方法,在其中进行事件处理。osgProducer::Viewer类维护一个GUIEventHandler队列,事件在这个队列里依次传递,handle的返回值决定这个事件是否继续让后面的GUIEventHandler处理,如果返回true,则停止处理,如果返回false,后面的GUIEventHandler还有机会继续对这个事件进行响应。重载handle函数实现键盘上的方向键控制摄像机视口,效果如图4所示:

图4 方向键控制摄像机视口的效果

bool handle(

const osgGA::GUIEventAdapter&ea,

osgGA::GUIActionAdapter&us)

{

case(GUIEventAdapter::KEYDOWN/KEYUP):

{

if(ea.getKey()=GUIEventAdapter::KEY_Space)

{//返回初始位置

flushMouseEventStack();

home(ea,us);

return true;}

//实现上,下,左,右方向键控制视口

else if

(ea.getKey()=osgGA::GUIEventAdapter::KEY_Up)

{return true;}

else if

(ea.getKey()=osgGA::GUIEventAdapter::KEY_Down)

{return true;}

else if

(ea.getKey()=osgGA::GUIEventAdapter::KEY_Left)

{return true;}

else if

(ea.getKey()=osgGA::GUIEventAdapter::KEY_Right)

{return true;}}

3.3 碰撞检测

在虚拟环境中,由于用户的交互和物体的运动,不可避免会发生碰撞,为避免穿透现象的发生,系统必须进行碰撞检测,并计算相应的碰撞反应,更新绘制结果。OSG中提供了osg::LineSegmen,用于表示一个包含一个起点和一个终点的线段类;osgUtil::Int ersectVisiotr是一个接受线段的类,用于判别与节点的交集,其中的函数addLineSegment(line.get())用来添加一条线段到列表当中;osgUt il::IntersectVisitor::HitList可以得到相交点的具体位置,从而计算出距离。本系统中采用基于视点向前线段探测的碰撞检测方法,用OSG实现算法的代码:

//定义一个交集访问器对象

osgUtil::IntersectVisitor iv;

iv.setTraversalMask(_intersectTraversalMask);

//创建线段

osg::ref_ptrsegForward=new osg::LineSegment;

//设置线段起点和终点

segForward->set(_eye,_eye+lv*(signedBuffer+distanceToMove));

//将线段添加到交集访问器中

iv.addLineSegment(segForward.get());

//将场景中节点与交集访问器绑定

_node->accept(iv);

//线段与节点如果相交,则发生碰撞,同时创建碰撞列表

if(iv.hits())

{//创建碰撞列表

osgUtil::IntersectVisitor::HitList&hitList=iv.getHitList(segForward.get());

}

3.4 天空盒

一般地,OSG导入的三维场景是不带天空的,本系统实现天空的显示,是将要显示的场景,放到一个正六方体的盒子中,在盒子的内壁贴上天空贴图,如图5所示:

图5 天空贴图

来模拟天空,当摄像机运动的时候,能够看见场景远处的天空,实现天空盒的过程,如图6所示:

图6 加载天空盒的三维场景

(1)创建一个正六方体的盒子。

(2)加载6张连续的无缝拼接天空照片。

(3)将6张照片按一定的顺序,贴在正六方体的内壁。

(4)动态更新贴图,创建一个贴图回调对象,该对象由osg循环自动调用。

(5)创建一个动态更新的控制器,用来动态的移动天空。

5 结束语

在综合考虑开发成本和开发周期的基础上,采用开源的图形开发包OSG开发了一个简单的三维引擎,通过分析OSG的场景图结构及渲染过程,实现了三维场景的交互功能,并研究了将OSG嵌入MFC的原理及技巧,碰撞检测的实现方法以及天空盒特效。

OSG本身提供众多高效能的渲染特性,本系统仅实现了几个基本的功能,在功能的改进和扩充方面还有很大的提升空间。随着研究的不断深入,今后还需在辅助工具的开发方面加大力度。

[1]申闫春,朱幼虹,曹莉,等.基于OSG的三维仿真平台的设计与实现[J].计算机仿真,2007,24(06):207—211

[2]熊磊.一种用于虚拟旅游体验的三维引擎的研究[D].武汉:华中师范大学,2007.

[3]曹莉,李绍彬,申闫春.基于OSG的镜面反射特效的实现[J].计算机仿真,2009,26(08):208-211.

[4]徐凌.基于OpenSceneGraph引擎的漫游系统的研究与实现[D].武汉:武汉理工大学,2008.

[5]温转萍,申闫春.基于OSG的虚拟校园漫游系统的设计与实现[J].计算机技术与发展,2009,19(01):217-220.

[6]王锐,钱学雷.OpenSceneGraph三维渲染引擎设计与实践[M].北京:清华大学出版社,2009.

猜你喜欢

视图引擎摄像机
新海珠,新引擎,新活力!
三生 三大引擎齐发力
蓝谷: “涉蓝”新引擎
5.3 视图与投影
视图
Y—20重型运输机多视图
SA2型76毫米车载高炮多视图
摄像机低照成像的前世今生
新安讯士Q6155-E PTZ摄像机
如何消除和缓解“摄像机恐惧症”