前言工业互联网、物联网、可视化等名词在我们现在的信息化背景下已经耳熟能详。日常生活交通、出行、吃穿等都可能用到信息在传统的可视化监控领域,一般采用WebSCADA前端技术实现二维可视化监控。本系统使用Hightopo的HTforWeb产品构建轻量级的3D视觉场景。3D场景从正面展示了一个地铁站的实景,包括地铁实时运行、地铁上下行、视频监控、烟雾报警、电梯运行等,帮助我们直观了解当前的地铁站。为了帮助用户更直观、友好地浏览当前地铁站,系统提供三种交互方式:第一人称——操作类似于行人或汽车移动的效果,前进后退可通过键盘控制和鼠标。自动巡视模式——该模式下,用户无需任何操作,场景自动前后移动巡视当前地铁站场景。鼠标操作方式——左键旋转场景,右键平移场景。本文通过讲解地铁站可视化场景的搭建、动画代码的实现、交互方式的原理分析、主要实现功能点。预览地址:HTML5基于WebGL的地铁车站3D可视化系统http://www.hightopo.com/demo/...地铁运行效果界面介绍及效果预览地铁从站外行驶到站内的效果为透明度逐渐增加,速度逐渐降低。漫游效果以上是自动巡检的漫游效果,场景自动前进旋转。监控设备交互效果当我们点击场景中的监控设备时,可以查看设备当前的运行状态、运行数据等信息。场景构建系统中的大部分模型都是通过3dMax建模生成的。此建模工具可以导出obj和mtl文件。在HT中,3d场景中的所有复杂模型都可以通过解析obj和mtl文件生成。当然如果一些简单的模型可以直接使用HT绘制,会比obj模型更轻量,所以大部分的简单模型都使用了HT的轻量级HTML5/WebGL建模方案用于Web产品。具体分析代码如下://分别是obj文件的地址和mtl文件的地址ht.Default.loadObj('obj/metro.obj','obj/metro.mtl',{center:true,//模型是否居中,默认为false,如果设置为true,会移动模型的位置,使其内容居中r3:[0,-Math.PI/2,0],//旋转改变参数格式为[rx,ry,rz]s3:[0.15,0.15,0.15],//尺寸改变参数,格式为[sx,sy,sz]finishFunc:function(modelMap,array,rawS3){if(modelMap){ht.Default.setShape3dModel('metro',array);//注册一个名为metro的模型}}});上面在加载obj模型后注册了一个名为metro的模型,然后如果要使用这个模型,可以使用如下代码实现:varnode=newht.Node();node。s({'shape3d':'metro'});上面的代码创建了一个新的节点对象。通过设置style对象的shape3d属性,可以在节点对象上将模型名称作为metro,然后就是我们在场景中看到的模型地铁列车。动画代码解析地铁动画代码解析的实现分析场景中地铁的运行是通过HT提供的调度插件实现的。调度的具体使用请参考HTforWeb的调度手册。这个调度主要用于指定时间间隔处理的函数回调,回调函数的第一个参数是dataprimitive,即3D场景中的model节点,我们可以判断当前数据是否是我们刚才的metro节点为后续操作创建,场景中模拟一条向左开的地铁和一条向右开的地铁,两条地铁会交替出现。3D场景中必须有一个坐标系。在HT中,分别用x、y、z来表示三个轴,所以地铁的运动必须改变地铁在坐标系中的位置,才能实现地铁的运行。地铁的坐标如下图所示:地铁在3D场景中的坐标系由上图可知。如果要移动地铁,只需要将地铁移动到图中红色箭头的方向,也就是x轴的方向,使用setX方法即可。不断修改地铁的位置,以达到地铁出行的目的。代码使用getSpeedByX和getOpacityByX不断获取此时的列车速度和列车透明度。下面是关键代码实现:letmetroTask={interval:50,//每隔50秒执行一个动作:(data)=>{//也就是上面说的回调函数//判断节点是否通过in当时是一个地铁列车节点if(data===currentMetro){//获取此时地铁的X轴位置和行进方向letcurrentX=data.getX(),direction=data.a('方向');//根据当前X轴位置获取当前列车速度letspeed=this.getSpeedByX(currentX);//根据当前X轴位置获取当前列车不透明度letopacity=this.getOpacityByX(currentX);//判断此时X轴位置是否超过某个值,即地铁在某个范围内移动if(Math.abs(currentX)<=5000){//设置当前透明度opacity!==1?currentMetro.s({'shape3d.transparent':true,'shape3d.opacity':opacity}):currentMetro.s({'shape3d.transparent':false});//设置当前X轴位置data.setX(currentX+direction*speed);//判断此时地铁的速度为0,所以此时应该执行开门动画if(speed===0)this.doorAnimation(currentMetro,direction);}//右方向的地铁开到尽头重置if(currentX>5000&&direction===1){currentMetro=leftMetro;当前地铁。设置X(5000);}//左边方向的地铁跑到终点,重置if(currentX<-5000&&direction===-1){currentMetro=rightMetro;currentMetro.setX(-5000);}}}};dm3d。添加计划任务(metroTask);通过上面的代码我们可以知道,地铁运行过程中,主要是通过修改地铁的x轴位置来生成移动动画,而地铁需要在一定的区间内移动,边界需要判断,为了模拟出真实的效果,需要根据地铁当前位置,不断获取当前列车速度和列车透明度。下面是流程图:上图是地铁进站时的流程。地铁停站关门时,需要出站。这个时候我们只需要重新设置地铁位置不为0即可。下面是部分代码实现:currentMetro.setX(direction*10);//设置出站列车的位置。执行完上面的代码后,上面的metroTask调度任务执行到getSpeedByX方法。方法后得到的速度不为0,所以此时地铁的动画会继续执行。速度由慢到快,透明度由深到浅。下面是开门动画的执行过程:自动巡检代码的实现。有rotate和walk两种方法来控制视角的旋转和视角的前进。rotate方法在非第一人称模式下,旋转以中心为中心,即围绕中心物体旋转。在第一人称时,旋转是以eyeRotate为中心,即旋转眼睛朝向方向。walk函数同时改变眼睛和中心的位置,即眼睛和中心在两点建立的向量方向上同时移动相同的偏移量。在这个系统中,我没有使用旋转功能,而是自己实现了视角的旋转。因为原来的rotate函数是旋转一定角度,没有旋转过程就马上旋转,所以我重新实现了旋转方法。系统中视角的旋转是通过不断修改center的值来实现的。具体实现过程原理如下图所示:部分实现代码如下:rotateStep(){//就是上图中的辅助点CletfromCenter=this.fromCenter;//就是上图B点lettoCenter=this.toCenter;//每帧旋转一度letrotateValue=this.rotateFrame||数学.PI/180;//在辅助点C和B之间创建一个方向向量letcenterVector=newht.Math.Vector2(toCenter.x-fromCenter.x,toCenter.y-fromCenter.y);让centerVectorLength=centerVector.length();//此时旋转百分比letrotatePercent=rotateValue*this.stepNum/this.curRotateVal;如果(rotatePercent>=1){rotatePercent=1;这个.stepNum=-2;}让newLength=rotatePercent*centerVectorLength;centerVector.setLength(newLength);让newCenterVector=centerVector.add(fromCenter);//获取旋转时的中心点信息letnewCenterPosition=[newCenterVector.x,this.personH??eight,newCenterVector.y];//设置当前中心的大小this.g3d.setCenter(newCenterPosition);}通过上面的代码,实现了场景中透视的旋转,可以通过修改rotateValue的值来控制旋转的速度。分析场景中电梯动画代码的实现电梯是一个obj模型,而3D模型是由最基本的三角形面拼接合成的,比如一个长方形可以由2个三角形组成,一个立方体可以由6个面或12个三角形组成,等等,更复杂的模型可以由很多小三角形组成。因此,3D模型的定义是对构成该模型的所有三角形的描述,每个三角形由三个顶点vertex组成,每个顶点vertex由x,y,z的三维空间坐标决定,而HT中用vs数组记录构成三角面的所有电梯的顶点坐标,所以如果想让电梯运行,只需要将所有的顶点坐标平移到电梯运行的方向即可。以下是一些关键的伪代码://vs指的是构成电梯模型的三角形的所有顶点坐标数组//由于场景中电梯的运行方向是向对角线的右上方向移动,所以只需要修改x轴和y轴坐标值即可//xStepyStep为电梯每次移动的距离setInterval(()=>{//i+3是因为vs数组的阶数为x,y,z轴,所以每次i偏移三个单位大小for(leti=0,l=vs.length;i
