刘 钊,徐富新
(中南大学 物理与电子学院,湖南 长沙 410000)
伴随着高等教育事业的发展,大学物理实验公共平台扩大,实验种类、数量、仪器数目、参与实验的人次数均有大幅度增加。在平台发展过程中,由于条件限制,学生难以随时接触到实验仪器。在有限的实验时间,学生也容易因不熟悉而损坏实验仪器。建立虚拟仿真实验室,可以较好地解决上述矛盾。虚拟实验是利用现有的虚拟现实技术[1-2]去模拟真实的实验环境[3],作为传统实验教学一种有效的辅助手段,在现代教学过程中发挥着越来越重要的作用。国内外许多大学很早就开始了虚拟实验室的研发,以满足教学需要。随着计算机硬件水平的提升和软件技术的发展,逼真度更好的三维虚拟实验室的开发变得可行,而仿真度和交互性则不断进步。
光栅光谱[4]实验是光学实验的一种。而虚拟仿真实验可以虚拟出实验所需的仪器,实验现象的模型及实验操作的整个流程,以达到提高实验教学质量的目的,弥补光学实验这方面的不足。虚拟实验环境可以满足实验严苛的要求,有利于创造真实的实验条件,可以打破时间和空间的限制。在进入真正实验之前,利用虚拟仿真实验加强光栅光谱实验操作,从而有效地解决了这些问题。光栅光谱虚拟实验系统可以把抽象复杂的实验简单化,调节虚拟仪器,实现操作化训练,还可以让实验者直观地看到实验原理的现象,通过直观感受会进一步加强实验者的理解,激发更大的兴趣,提高物理实验教学质量。
为了体现实验3D真实的效果,选择Unity 3D[5-6]和3D Max[7]作为开发软件。在虚拟实验建设方面,很多高校都创建了相应的虚拟仿真实验平台,大部分都是利用Flash、Labview等技术。但是,这类平台存在自身的不足,它们的仿真效果并没有达到很高的三维逼真度,不易创建出真实的实验环境和实验交互的效果。Unity 3D是一套由Unity Technologies公司开发的三维开发引擎,支持Javascript,C#,BOO语言的开发扩展,可以使用插件,可支持3D Max或Maya等建模软件的输出格式,包括三维模型、骨骼动画等。凭借精简直观的制作流程、功能强大的组件,拥有物理系统、粒子系统、动画系统等,它能够创建出逼真度更好的虚拟实验环境和仪器,可以完全满足物理实验教学的要求。
文中采用模块化开发了光栅光谱仪虚拟实验室系统,采用Bmob后端云服务器,基于MVC分层思想进行登录注册系统的设计,3D实验环境交互功能设计以及利用网格通过代码来创建动态图表的绘制设计,以满足实验学习的要求。
光栅光谱虚拟实验室系统的设计主要参考现实中实验室进行,根据需求设计系统各个模块,系统结构如图1所示。虚拟实验系统由两大模块构成,一是登录注册模块,二是实验仿真模块。其中,登录注册模块采用基于Bmob移动服务器;实验仿真模块主要分为两种模式:一是学习模式,主要是通过视频、文本等学习有关实验的内容和步骤等,二是操作模式,让实验者进行相应的实验操作。
图1 系统结构
在创建Unity项目前需要准备资源,包括人物模型、动画、纹理、音频特效、功能脚本以及其他类扩展插件等等。首先对虚拟实验室所需的资源进行信息采集,利用3D Max制作相关的实验仪器模型,然后以.FBX文件导出,打开Unity软件,新建项目,创建3D场景,导入资源,在Unity中进行相应的对象贴图烘焙处理,根据不同用途来设置图片类型,如纹理、反射贴图等设置格式使物体对象达到真实逼真的效果。从系统模型构建看,主要分两部分,一个是UI为主体的2D场景,另一个就是3D场景。
1.2.1 三维模型的建立
建模采用的是3D Max。主要制作3D模型,包括有光栅光谱仪(含光电倍增管、电箱、计算机系统)、高压电源、准直氦灯、氢灯、白炽灯等。为了提高质感和视觉效果,实验中对所需仪器逐一建模生成,并通过渲染器进行烘焙渲染、贴图,增强真实感,最后将3D Max导出时选中嵌入媒体复选框。然后打开Unity应用程序,在所开发的项目中,导入Unity会自动生成该FBX模型配套贴图的文件。在Unity中,根据图片资源的用途不同设置相应的格式来达到最佳效果,如作用于光源的Cookie,选择此类型,纹理适用于灯光对象。在创建3D场景过程中,为了提高资源利用率以及开发的效率,往往会制作Prefabs(预设体),Prefabs是一个对象及其组合的集合。根据实验室要求创建好3D实验室,此时,实验室中所有的实验模型都只是静态的,根据需要添加各种组件来给予相应的功能,比如说提供了多个物理模拟的组件,通过修改相应参数,从而使对象表现出与现实相似的各种物理行为,给每个模型一个网格,这样人物就不可以穿过去,为对象添加刚体(RigidBody)组件,对象便可以接受外力才会受到重力的影响等等。
1.2.2 虚拟仿真UI设计
UI场景在实验中主要涉及到两个功能模块,即登录注册模块和实验操作模块中的学习模式。在Unity5以上的版本中,UGUI在易用和功能上一点不逊于NGUI,使用UGUI可以快速地建立UI界面,它的最主要的组件就是画布(Canvas),每一个GUI控件必须是画布的子对象。在Canvas组件下有三种渲染模式:覆盖、摄像机以及世界空间模式。在覆盖模式下,Canvas下的UI会独立于其他场景元素。而在常见的摄像机模式下,其渲染的绘制图像会产生在摄像机的镜头下;世界空间模式下,就与其他场景对象一样。
如图2所示,它分为两部分,一种是可视化组件,有文本、图像、遮罩等,如text(文本)用来显示非交互文本,可以作为标题,Image(图像)可用作装饰、图标、背景图等等。另一种就是互动性组件,部分组件中可以添加代码与其相互响应,比如Button(按钮)响应来自用户的单击事件,如果鼠标在单击释放之前离开Button控件,则动作不会发生。在这个画布下根据要求用到所需的UI元素,通过组合这些UI控件来完成UI场景。
在实际的Unity场景制作中,为了使对象在场景中层次不会错乱,可以创建不同的工作层来管理对象,使得场景更加分明,对象之间操作也更加方便。
创建一个空对象UI来管理整个UI场景,向场景中添加Canvas组件,通过Rect Transform组件将其调整至合适位置。在Canvas组件下添加一个Image组件和一个空对象,Image作为整个背景图,而空对象Window用来管理注册和登录的窗口。在空物体Window下分别创建Panel,取名Regist和Logon,这样形成两个不同的窗口,在Panel下添加文字、图片、按钮等等UI元素,根据它们的需求创建不同的功能,最后形成了登录和注册的窗口。
2.1.1 MVC动态加载登录窗口
利用UI架构,设置场景管理器来完成这一部分的功能。MVC[8-9]思想是将一个应用分成三个基本部分:Model(模型)、View(视图)和Controller(控制器),这三个部分以最少的耦合协同关系工作,从而提高应用的可扩展性及可维护性。UI是所有UI*的基类,包括两个子类,其中一个为UIScene,它是所有场景UIScene*的基类,它的子类UISceneLogonCtrl,是一个场景UI的控制器,UIWindow是所有窗口UIWindow*的基类。
登录视图LogonView,只负责与UI交互,而控制器负责把Model的数据显示在View上或者从View上采集数据。MVC框架中,控制器控制视图,且把Model的数据显示在View上。比如登录模式,view=UILogOnView视图,只负责和UI交互,控制器负责把Model的数据显示在View上,或者从View采集数据,打开或者关闭窗口,点击按钮之后,账户控制器就会接收到按钮接收到的数据,然后数据就会通过Model传输给服务器。Model中包含业务数据和业务逻辑,业务数据通过网络通讯模块传输给服务器。Model还有本地数据管理,包括数据读取和本地存储,如图3所示。
2.1.2 基于Bmob实现登录、注册功能
通过Bmob[10]可以随时存储和读取自己在平台上的实时数据,很好实现了客户端通过网络与后端云的数据连通。使用轻量级的Bmob C# SDK开发包,将Bmob-Window文件引用到项目工程,进而实现用户登录和注册的功能。免去了服务器维护、数据存储互通等繁琐的工作,大幅度提高开发效率,减少研发时间和成本。
图3 MVC动态加载结构
首先需要下载相对应的Bmob-unity文件放入工程的libs下,然后将BmobUnity组件挂载在摄影机下,在Bmob移动云官网中注册账号,将申请账号下的Application ID和Rest key分别写入相对应的BmobUnity中。然后创建相应的类继承BombUser类,重写其中的login()和register()函数。在这个过程中需要从数据库中读取或者写入数据。也有相应的函数,只要调用就行,十分方便。
注册代码如下:
MyBmobUser user=new MyBmobUser();
user.username=userName.text;
user.password=pwd.text;
Bmob.Signup
if(excp !=null)
{
tishi.text="注册失败";
return;
}
else
{
tishi.text="注册成功";
}
});
任何一项实验操作都有一定的操作流程,这对实验仪器的保护、实验人员的安全以及实验的成功至关重要。在操作过程中采用了分步式的实验操作过程。操作者可以自行纠正操作的错误,加强实验仪器的操作,熟练实验流程,避免仪器的损害,进而达到训练的目的,为以后实时操作打下坚实的基础[11-12]。
交互的实现主要通过程序代码对物体之间的触发来完成。在Unity中,操作者通过键盘、鼠标来控制虚拟实验中的人物,而虚拟世界中的人物与3D物体之间的交互(如碰撞、触发等)则需要函数等程序指令来实现。以单色仪定标过程为例,打开计算机(主机、显示器),开启光栅光谱仪电箱电源,启动光栅光谱仪控制系统程序进行初始化操作,检查并设置参数使其满足实验需要,开启氦灯电源,将灯对准光栅光谱仪狭缝,执行“工作/单程扫描”,执行“读取数据/寻峰”,收集数据,关闭仪器。
在虚拟实验操作场景中,实验者以第一人称视角,用鼠标操作执行相应的内容。在操作场景中可以操作仪器做出相应的动作,同时包含有对该步骤的文字说明,便于加深使用者对实验操作的理解[13]。
比如,提前做好一个关于错误信息的预设体,如果出现错误步骤,就会响应函数方法,代码如下:
GameObject errPre=Instantiate(errObj,tipSystem.transform.position,tipSystem.transform.rotation) as GameObject;
errPre.transform.SetParent(tipSystem.transform);
errPre.GetComponentInChildren
当点击计算机按钮时,按钮会由白色变为红色,这是提醒电源已开,表示已经触发激活了电脑屏幕,此时启动光栅光谱仪控制系统程序,会有提示提醒先打开电箱电源开关和单色仪。这样就会按照提示,一步步进行试验,而不会出现操作步骤的错误从而导致试验的失败,如图4中(a)和(b)所示。
图4 提示图
同时,为了进一步体现实验的交互性,还设计了放大功能,在做实验的过程中,可以进一步观看实验仪器,如图4(c)所示。此时需要鼠标来完成,鼠标输入的相关事件包括鼠标移动、按键的单击等,鼠标位置的变量类型为Vector3,其中x分量对应水平坐标,y分量对应垂直坐标,z分量始终为零。GetMouse ButtonDown需要传入参数来判断鼠标按键,0代表左键,1代表右键,2对应中间。其关键代码如下:
if(Input.GetMouseButtonDown(1) && showTips)
{
flag=true;
tmp=Instantiate(prefab,cam.transform.position,cam.transform.rotation) as GameObject;
tmp.transform.position +=cam.transform.forward * 1.0f;
tmp.transform.position+=cam.transform.up*0.1f;
tmp.transform.Rotate(Vector3.forward *Input.GetAxis("MouseX") * -speed);
tmp.transform.Rotate(Vector3.right *Input.GetAxis("Mouse Y") * speed);
}
而showTips判断的依据是鼠标是否经过物体。主要通过鼠标与物体之间的触发函数,其中点击鼠标右键以第一人称视角为中心。在脚本中,预设一个新的放大物体距人物一定距离,方便操作者仔细观察实验仪器,为实验者带来较为真实且操作方便的体验感[14]。
光栅光谱仪实验中最主要的部分是获取光谱曲线,包括单色仪定标、测量氢光谱、氦光谱等。
主要做法有:(1)根据以往在真实实验中所测量的光谱图,将其制作为图片,导入unity中生成UI图片,以图片的方式呈现给操作者;(2)运用unity3D的图表插件GraphMaker动态数据生成的图表。这两种方法都能产生光谱图,可是对于实验来说,光谱图波长的产生是根据光栅在转动的过程中,复合光通过光栅产生衍射形成单色光,波长并不是一次就出来的,这样给操作者的真实体验就差一些。
在Unity中Graphic是所有可视化UI组件的基类。在创建可视UI组件时,从这个类继承。其在继承了MaskableGraphic的子类关键方法OnPopulateMesh中重写该方法,向OnPopulateMesh方法中传入对应的VertexHelper类型的数据,即可构建出自己想要的组件。VertexHelper是UnityEngine.UI一个实用程序类,是一个包含了网格数据中的顶点数据和三角面对应顶点的序号,可以帮助生成UI的网格。以下通过两个点来制作矩形。首先获取两点之间的长度,分别将两点在X、Y方向的斜率作为单位,创建UIVertex,重新设置四个顶点vertex的位置,从而得到矩形。关键代码如下:
protected UIVertex[] Quad(Vector2 startPoint,Vector2 endPoint)
{
float length=Vector2.Distance(startPoint,endPoint);
float xS=(endPoint.y-startPos.y)/length;
float yS=(endPoint.x-startPos.x)/length;
UIVertex[] vertex=new UIVertex[4];
vertex[0].position=new Vector3(startPoint.x-xS,startPoint.y-yS);
vertex[1].position=new Vector3(startPoint.x+xS,startPoint.y+yS);
vertex[2].position=new Vector3(endPoint.x+xS,endPoint. y+yS);
vertex[3].position=new Vector3(endPoint.x-xS,endPoint. y-yS);
return vertex;
}
这个矩形就相当于一个点,线段就是由许多点构成,光谱图动态显示则是根据已知的实验数据转换为一组坐标,使用协同逐点显示连线。在设计虚拟仿真实验系统之前,已经做过有关实验,获得相关的实验数据。在设计和构建了实验的仿真界面过程中,包括仪器参数设置和仿真实验结果等,把数据进行本地化存储。根据之前得到的真实实验数据,在虚拟仿真实验系统中本地存储不同的光谱图,与此同时在读取数据时也是根据之前实验数据加入了部分随机函数,使虚拟仿真实验系统的数据更真实、合理。图5为用已知波长的氦灯光谱线对光栅单色仪定标界面。
图5 氦光谱
根据实验的具体要求和实验者的一些操作行为来得到相应的实验结果,虚拟仿真实验系统效果良好[15]。
Unity拥有强大的图形引擎和功能,为开发者提供了平台,用于开发丰富的2D、3D、VR及AR交互式内容。拥有直观的工作流程和功能强大的工具集,支持多种资源格式导入,可以将3D模型、图像、视频、声音等进行结合快速实现创意,并结合Unity内部强大的系统组件功能,如专业的物理引擎系统模拟包含刚体、柔体、重力、碰撞等各种物理行为;快速的UGUI界面设计,强大的可视化编辑器,可以满足各种界面制作需求等。这些功能可以轻松完成三维互动开发,创建出虚拟仿真内容。通过Build Setting中的PC选项就可以将内容部署轻松送达至PC平台,生成可执行文件及其数据文件包。图6为系统模块展示。
图6 系统模块展示
图6(a),进入登录注册窗口完成登录后,在实验主场景中有两种模式选择,分别对应实验学习模式和实验操作模式,可以自己选择进入相应的场景进行学习。进入学习模式后,包括实验简介、实验原理、实验步骤,如图6(b);用户可以自己选择进行最基础的理论学习。学习完成后进入实验操作场景进行实验操作,如图6(c);如果对实验仪器感兴趣,可以去仪器场景,将仪器打开,观察仪器内部结构,如图6(d)。
在光学实验教学方面,设备昂贵且易损,实验环境要求高,因此将Unity3D和3D Max技术引进光学实验教学训练中。就其自身优点来讲,它精简直观的制作流程、功能强大的组件,突破了传统的学习方式,可以提前让学生学习有关实验的基础知识,通过各种交互功能,熟悉操作实验流程,加深对实验仪器组成部件的认识和操作,丰富了学生的学习内容。学生可以在任何时间任何地点多次进行实验操作,加强学生的实验学习,有效地减少了实验设备损害,提高了实验利用效率。因此,该系统具有很好的应用价值。