曹莉,宋学坤,张佩江
一种用于虚拟校园漫游的三维引擎的研究
曹莉,宋学坤,张佩江
在对三维引擎技术OSG进行分析和研究的基础上,提出并实现了一种用于虚拟校园漫游的三维引擎系统,该系统使用OSG所提供的类和接口,实现了虚拟校园场景的渲染以及漫游的交互控制功能,同时在引擎中添加了语音介绍功能,丰富了浏览者的视听感受。实践证明,该引擎具有良好的移植性与通用性,为虚拟校园漫游技术向虚拟校园系统和虚拟学习平台方面的迁移提供了支持。
开放式视景图形;三维引擎;虚拟校园;漫游交互
随着科学技术的迅猛发展,虚拟现实技术已经成为人机交互技术的新生力量,而虚拟漫游技术是虚拟现实技术的重要分支,在建筑、旅游、游戏、航空航天等多种行业发展的很快。而通过虚拟现实技术全面展示校园建筑物、校园设施以及校园地貌,可以为学校树立良好的形象,提高知名度,还可以作为校园规划的辅助工具。
三维引擎作为实现虚拟校园漫游的一种重要方式,它的主要作用是在计算机上创造出一个虚拟的校园环境,通过提供与该虚拟环境的交互体验,人们可以在个人PC上进入一个虚拟的校园环境,并在虚拟场景中进行实时浏览。
本文中三维引擎的目标是以OSG技术作为基础,并结合其他虚拟现实技术在计算机上模拟虚拟校园漫游的过程。
三维引擎作为虚拟现实的核心,多数采用OpenGL的开发接口进行开发,这种开发接口仍然停留在几何体级别的操作层面,是一种明显带有面向过程特征的API。OSG(Open Scene Graph)是一个跨平台的开源的图形开发包,它基于场景图的概念,提供一个构建于OpenGL函数库之上的面向对象的框架,为图形程序的开发提供场景管理和渲染优化功能,从而使系统开发脱离几何体操作,转而处理具体的场景,大大提升开发效率。也为虚拟校园漫游技术今后在虚拟校园系统、虚拟学习平台等方面的扩展提供充分的技术支持。
1.1 OSG的场景管理
OSG采用场景图结构来管理场景,它是一种自顶向下的,分层的树状数据结构,如图1所示:
图1 场景图结构
该结构以根节点表示整个三维场景;以组节点(osg::Group)表示物体属性信息,如矩阵变换、状态切换、细节层次等,在实现过程中,又以组节点为基类派生出变换节点(osg::Transform),开关节点(osg::Switch),细节层次节点(osg::LOD)等类,对属性信息进行分别管理;而叶子节点(osg::Geode)则代表物理对象本身或几何模型。这种结构既反映了场景的空间结构,也反映了对象的状态,便于提取数据节点之间的共有行为和属性。
场景图是一种中间件(middleware),它构建于底层API函数之上,提供了高性能3D程序所需的空间数据组织能力及其它特性。一个典型的OSG程序层次结构,如图2所示:
图2 3D程序层次结构
1.2 OSG的场景渲染
一个场景图系统执行绘图遍历时,所有几何体是以OpenGL指令的形式发送到硬件设备上。这种机制无法实现诸如细节层次、渲染特效等高级特性。为了实现动态的几何体更新,拣选,排序和高效渲染,OSG场景图形提供3个遍历过程:
更新:更新遍历允许修改几何体、渲染状态,或者节点参数,保证场景图形的更新对应当前帧;
拣选:在拣选遍历中检查可见性,将几何体和状态量置入新的结构(在OSG中称为渲染图形,render graph)之中。
绘制:在绘制遍历中(有时也称作渲染遍历),场景图形将遍历由拣选遍历过程生成的几何体列表,并调用底层API,实现几何体的渲染。
一般地,这3种遍历操作在每一个渲染帧中只执行一次,但有时一些渲染特例需要将同一个场景(不同或相同部分)在多个视口中进行同步显示,这样更新遍历仍然只执行一次,但拣选和绘制遍历则需要在每个视口内各执行一次,以保证多处理器和多显卡的系统实现并行场景图形处理。
2.1 场景的渲染
OSG所提供的Viewer类包含了在渲染OSG图形的代码,要实现场景渲染首先是Viewer实例化一个osgViewer::Viewer 对象,而后将一个场景图形关联到该对象,并进行渲染。osgviewer 在使用Viewer 类在实现场景的渲染的基础上增加了改变视口的功能。
Viewer类在内部创建了一个osg::Camera 摄像机对象来管理OSG 的模型-视图矩阵。设置Camera 对象的投影矩阵和观察矩阵为自定义的矩阵值,就可以保证用户程序能够完全控制视口的浏览动作,可以编写一个小的循环来代替Viewer::run(),循环中反复更新视口并渲染单帧图像。下面的代码段实现了直接控制Viewer 中的Camera 对象,从而改变每一帧的视口显示。
osgViewer::Viewer viewer;
viewer.setSceneData(osgDB::readNodeFile("xiaoyuan.ive"));
view
er.getCamera()->setProjectionMatrixAsPerspective(40.,1.,1.,10 0.);
// 创建矩阵,指定到视点的距离
osg::Matrix trans;
trans.makeTranslate(0.,0.,-12.);
// 旋转一定角度(弧度值)
double angle(0.);
while (!viewer.done())
{
// 创建旋转矩阵
osg::Matrix rot;
rot.makeRotate(angle,osg::Vec3(1.,0.,0.));
angle+=0.01;
// 设置视口矩阵(旋转矩阵和平移矩阵连乘)
viewer.getCamera()->setViewMatrix(rot * trans);
// 绘制下一帧
viewer.frame();
}
在渲染循环中,代码每帧都会更新Camera 的视口矩阵以增加旋转角度值。代码中使用setViewMatrix()方法来设置视口矩阵,Camera类还提供了与之相似setViewMatrixAsLookat() 方法。
2.2 漫游交互控制
OSG中的GUIEventHandler类为任何想要处理GUI事件的类提供了一个基本接口。处理事件的方法是:virtual bool handle(const osgGA::GUIEventAdapter& ea, osgGA::G UIActionAdapter& aa);方法中的第一个参数是GUI事件的供给者,第二个参数用于handle方法对GUI进行反馈, G UIEventHandler根据输入事件让GUI进行一些动作。如果要进行事件处理,可以从GUIEventHandler派生出一个新类,然后覆盖handle方法,在其中重写事件处理。osgProducer:: Viewer类维护一个GUIEventHandler队列,事件在这个队列里依次传递,handle的返回值决定这个事件是否继续让后面的GUIEventHandler处理,如果返回true,则停止处理,如果返回false,后面的GUIEventHandler还有机会继续对这个事件进行响应。
(1) 键盘控制
键盘控制是摄像机控制中的一个功能实现,要求键盘的“↑”“↓”“←”“→”来控制摄像机前后左右的移动,在OSG事件处理函数中实现。重载handle函数实现键盘上的方向键控制摄像机视口:
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; }
}
这是实现键盘响应函数的代码,getKey()是获取键盘响应值,使用OSG的事件响应函数完成的。系统中用到的语音,以及对语音的控制,同样用到了键盘控制。
(2)鼠标控制
鼠标控制实现的是对于摄像机视口上下左右的移动,同样用到了OSG的事件处理函数——鼠标响应事件。鼠标的移动用到矩阵运算来计算新的位置,鼠标左右键的响应用到的是事件处理函数。
①控制器鼠标移动函数主要是对于鼠标位置的设置。
double CMyManipulator::Mouse_Move(int wndWidth, int wndHeight)
②控制器鼠标旋转的实现,是在bool CMyManipulator::calcMovement()中利用旋转矩阵的运算实现的。具体如下:
osg::CoordinateFrame cf=getCoordinateFrame(_eye);
osg::Matrix rotation_matrix; //定义旋转矩阵
rotation_matrix.makeRotate(_rotation);
// 旋转矩阵运算
//利用矩阵相乘确定摄像机的三个变量的位置
osg::Vec3d up = osg::Vec3d(0.0,1.0,0.0) * rotation_matrix;
osg::Vec3d lv = osg::Vec3d(0.0,0.0,-1.0) * rotation_matrix;
osg::Vec3d sv = osg::Vec3d(1.0,0.0,0.0) * rotation_matrix;
③鼠标按键控制,包括旋转速度的实现
case(USE_MOUSE_Y_FOR_SPEED):
{double dy =
_ga_t0->getYnormalized();
_velocity = _modelScale*0.2f*dy;
break;
}
case(USE_MOUSE_BUTTONS_FOR_SPEED):
{
unsigned int buttonMask
= _ga_t1->getButtonMask();
if (button-
Mask==GUIEventAdapter::LEFT_MOUSE_BUTTON)
{_velocity += dt*_modelScale*0.01;
}
else if
(button-
Mask==GUIEventAdapter::MIDDLE_MOUSE_BUTTON ||
button-
Mask==(GUIEventAdapter::LEFT_MOUSE_BUTTON|GUIEv entAdapter::RIGHT_MOUSE_BUTTON))
{_velocity = 0.0;}
else if
(button-
Mask==GUIEventAdapter::RIGHT_MOUSE_BUTTON)
{_velocity -=
dt*_modelScale*0.01;
}
鼠标的控制,在漫游过程中,旋转操作是利用鼠标来控制摄像机视口,因为摄像机的位置的三个参数中包含视口的控制。鼠标的控制还要在摄像机控制中添加鼠标响应事件以及刷新鼠标事件,用来检查更新鼠标的状态。
2.3 添加语音介绍
虚拟校园漫游配以语音介绍,能让浏览者更全面的了解学校信息,将事先录制好的语音保存为mp3格式,将Direct Show中播放音乐的接口封装到一个自定义的类中,并重写这个媒体音乐的接口。实现如下:
(1)设置接口指针为空。
(2)通过一个媒体控制器m_pGraph,初始化接口库。
(3)创建filter graph manager 滤波器控制器,来控制音乐格式和各种格式的接口。
(4)定义接口对象,查询要使用的接口。
定义接口对象,可以完成的函数有:
run()播放音乐函数;
render(CMyPlayer * ptr)通过指针设置循环播放;
pause()和stop()暂停和停止音乐;
setStartPosition();设置语音初始播放位置;
setMp3FileName()和getMp3FileName()设置和获取语音文件名;
setVolume()和getVolume()设置和获取音量函数。
语音实现的具体过程:
class MusicCallBack : public osgGA::GUIEventHandler
{ public:
MusicCallBack()
{ myPlay
er.setMp3FileName(L"C:/jieshao.mp3");//设置语音文件名称
myPlayer.run(); //播放音乐
}
用键盘控制语音:
bool handle(const GUIEventAdapter& ea,GUIActionAdapter& aa, osg::Object*, osg::NodeVisitor*)
{ if(ea.getEventType() ==
GUIEventAdapter::KEYDOWN)
{
if(ea.getKey() == '=' || ea.getKey() == '+')
{ long lVolume = myPlayer.getVolume();//获取当前音量
lVolume += 100;
myPlayer.setVolume(lVolume);//增加音量后的新音量
return true;
}
else
if(ea.getKey() == '-' || ea.getKey() == '_')
{ long lVolume =
myPlayer.getVolume();
lVolume -= 100;
myPlayer.setVolume(lVolume); //减弱音量
return true;
}
else
if(ea.getKey() == '1' || ea.getKey() == '!')
{ myPlayer.pause(); //暂停
return true;
}
else
if(ea.getKey() == '2' || ea.getKey() == '@')
{ myPlayer.run(); //播放
return true;
}
else
{ CMyPlayer::render(&myPlayer); //设置循环播放
return false;
}
}
CMyPlayer::render(&myPlayer);
return false;
}
protected:
CMyPlayer myPlayer;
};
本文在对三维引擎技术进行分析和研究的基础上,提出并实现了一种用于虚拟校园漫游的三维引擎系统,该引擎具有良好的移植性与通用性,并配有有录制好的语音介绍,良好的可扩展性和结构设计使得数据的扩展并不需要修改引擎本身,因此可用于不同数据规模的应用。
[1] 曹莉.基于OSG的三维引擎关键技术研究[J].微型电脑应用,2013,29(10):31-34
[2] 熊磊.一种用于虚拟旅游体验的三维引擎的研究[D].武汉:华中师范大学,2007.
[3] 华艳.三维虚拟校园漫游系统的研究与应用 [J] .应用技术与研究,2013,(10):63-64.
[4] 温转萍,等.基于OSG的虚拟校园漫游系统的设计与实现[J].计算机技术与发展,2009,19(01):217-220.
[5] 王锐,等.OpenSceneGraph三维渲染引擎设计与实践[M].北京:清华大学出版社,2009.
[6] 叶伟,基于OSG的虚拟校园漫游系统研究与实现[D].南昌:南昌航空大学,2013.
The Research of 3D Engine for Virtual Campus Ramble
Cao Li, Song Xuekun, Zhang Peijiang
(Henan University of TCM, Zhengzhou450000, China)
After analyzing the 3D engine technology named OSG, it presents and realizes the 3D engine for campus ramble. Some classes and interfaces of OSG are used to accomplish the interaction control and rendering the scene of virtual campus in the system. In addition, the audio introduce function can enrich the audio-visual sensation of users. The practice has proved that the engine has portability and universality and it supplies support for the transfer of campus ramble technology to virtual campus system and the virtual learning platform.
Open Scene Graph (OSG), 3D Engine, Virtual Campus, Ramble Interaction
TP391.9
A
1007-757X(2014)10-0028-04
2014.07.16)
河南中医学院苗圃项目课题(MP2013-74);河南省教育厅2011年自然科学研究项目(No.2011A520027);河南省信息技术教育研究项目(No.ITE12088);2013河南省教育厅人文社会科学研究项目(No.2013-MFD-137);2014年度河南省教育厅科学技术研究重点项目(No.14A520070);2013河南中医学院教育教学改革研究项目(No.2013JX21);河南中医学院人才培养模式创新实验区建设项目(No.XJZLGC[2010]22)
曹 莉(1979-),女,汉族,河南新乡人,河南中医学院,实验师,研究方向:虚拟现实技术,郑州,450000宋学坤(1982-),男,汉族,河南商丘人,河南中医学院,讲师,研究方向:生物信息学、医学信息处理和智能计算,郑州,450000张佩江(1961-),男,汉族,河南开封人,河南中医学院,副教授,研究方向:基于Web的大型分布式数据库应用技术,郑州,450000