重庆分公司,新征程启航
为企业提供网站建设、域名注册、服务器等服务
这篇文章给大家分享的是有关HTML5中WebGL怎么实现民航客机飞行监控系统的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。
创新互联建站是一家专业提供阿拉善盟企业网站建设,专注与成都网站制作、网站建设、H5场景定制、小程序制作等业务。10年已为阿拉善盟众多企业、政府机构等服务。创新互联专业网站制作公司优惠进行中。
前些日子出差,在飞机上看到头顶的监控面板,除了播放电视剧和广告之外,还会时不时的切换到一个飞机航行的监控系统,不过整个监控系统让人感到有一点点的简陋,所以我就突发奇想制作了一个采用 HT for Web 的升级版监控系统,demo 的效果还行,发出来大家相互学习下。
demo
实现过程
云中穿行效果
为了达到飞机云中穿行的效果,最开始我遇到的问题是飞机飞行的层次感,也就通常所说的透视效果,这里我采用的是云通道和云背景以不同的速度流动,制造一种飞行的透视效果。
云我采用的是贴图的方式呈现的,但是仅仅是贴图会遮挡天空和飞机,非常影响飞机飞行的观感,所以我开启了相应图元的 transparent 和 opacity ,云背景和云通道设置不同的透明度,不仅增加了层次感,还会让人产生云朵从眼前飘过的错觉。
云通道采用的是 ht.Polyline 类型,通道缩放拉大了 Y 轴的比例,使云通道有更大的纵向空间,设置 reverse.flip 背拷贝使云通道内部也显示出贴图,仿佛让飞机置身于云海中穿梭;云背景采用 ht.Node 类型,只设置一个面显示充当云背景。
整体的云流动效果采用 offset 偏移实现,改变相应图元或相应图元面的贴图偏移量来达到飞机云中穿行的效果, 代码如下:
var i = 1, p = 0; setInterval(() => { i -= 0.1; p += 0.005; clouds.s('shape3d.uv.offset', [i, 0]); cloudBackground.s('all.uv.offset', [p, 0]); }, 100);
升降颠簸效果
虽然达到了飞机云中穿行的效果,但是如果飞机只是直直的飞行,那也会降低飞行的实感,相信坐过飞机的朋友肯定都遇到过因气流产生的颠簸,也经常感受到飞机飞行途中的爬升和下降,这其实是因为飞机的航线并不是一直固定在一个高度上,有时会爬升有时会下降,所以我就用 ht-animation.js
HT 动画扩展插件去实现飞机颠簸效果,代码如下:
dm.enableAnimation(20); plane.setAnimation({ back1: { from: 0, to: 160, easing: 'Cubic.easeInOut', duration: 8000, next: "up1", onUpdate: function (value) { value = parseInt(value); var p3 = this.p3(); this.p3(value, p3[1], p3[2]); } }, //...省略相似 start: ["back1"] });
球扇形视角限制
飞行效果完善之后,这时我就遇到了一个比较棘手的问题,因为实际上虽然看着飞机是在云海中穿梭,但是仅仅是在通道中飞行,背景其实也只是平面贴图,所以当视角到达某种程度的时候就会有强烈的违和感和不真实感,就需要一个视角限制,使视角的调整刚刚好在一个范围内。
视角限制的话一般是限制 g3d 的 eye 和 center ,不太了解的朋友可以去看 hightopo 官网中的 3d 手册,里面有详细的说明,这里我就不再赘述了;因为视角范围的关系,所以我决定固定 center 的位置,代码如下:
g3d.addPropertyChangeListener(e => { // 固定中心点 if (e.property === 'center') { e.newValue[0] = center[0]; e.newValue[1] = center[1]; e.newValue[2] = center[2]; } }
然后再把 eye 限制在某一个范围内就大功告成了,然而这里却并不是那么简单,最开始我把 eye 限制在一个立方体的空间内,但交互效果很不理想,考虑到 g3d 默认交互中,鼠标拖拽平移视角变换时,实际上 eye 是在一个以 center 为球心的球面上运动的,所以我决定从这个球中挖出来一块作为 eye 的限制空间,也就是球扇形,不太理解的朋友可以参考这个图:
球扇形视角限制,一共需要三个参数,分别是中心参考轴、中心轴和外边所成角度、所在球限制半径,其中中心参考轴可根据初始 eye 和 center 的连接延长线确定,所在球限制半径又分最大限制和最小限制,代码如下:
function limitEye(g3d, eye, center, options) { var limitMaxL = options.limitMaxL, limitMinL = options.limitMinL, limitA = options.limitA; g3d.addPropertyChangeListener(e => { // 固定中心点 if (e.property === 'center') { e.newValue[0] = center[0]; e.newValue[1] = center[1]; e.newValue[2] = center[2]; } // 限制视角 if (e.property === 'eye') { var newEyeV = new ht.Math.Vector3(e.newValue), centerV = new ht.Math.Vector3(center), refEyeV = new ht.Math.Vector3(eye), refVector = refEyeV.clone().sub(centerV), newVector = newEyeV.clone().sub(centerV); if (centerV.distanceTo(newEyeV) > limitMaxL) { newVector.setLength(limitMaxL); e.newValue[0] = newVector.x; e.newValue[1] = newVector.y; e.newValue[2] = newVector.z; } if (centerV.distanceTo(newEyeV) < limitMinL) { newVector.setLength(limitMinL); e.newValue[0] = newVector.x; e.newValue[1] = newVector.y; e.newValue[2] = newVector.z; } if (newVector.angleTo(refVector) > limitA) { var oldLength = newVector.length(), oldAngle = newVector.angleTo(refVector), refLength = oldLength * Math.cos(oldAngle), vertVector, realVector, realEye; refVector.setLength(refLength); newEyeV = newVector.clone().add(centerV); refEyeV = refVector.clone().add(centerV); vertVector = newEyeV.clone().sub(refEyeV); vertLength = refLength * Math.tan(limitA); vertVector.setLength(vertLength); realVector = vertVector.clone().add(refEyeV).sub(centerV); realVector.setLength(oldLength); realEye = realVector.clone().add(centerV); // 防止移动角度大于 180 度,视角反转 if (oldAngle > Math.PI / 2) { realEye.negate(); } e.newValue[0] = realEye.x; e.newValue[1] = realEye.y; e.newValue[2] = realEye.z; } } }) }
飞机监控系统
当然作为监控系统,自然要有监控了,增加右下角的小地图,并提供三种模式,分别是聚焦飞机,聚焦飞行轨迹和聚焦地图,并根据飞机的飞行方向控制飞行轨迹的流动效果,其中聚焦飞机会跟随飞机移动进行 fitData ,使飞机一直处于小地图的中心,代码如下:
var fitFlowP = function (e) { if (e.property === 'position' && e.data === plane) { mapGV.fitData(plane, false); } }; buttonP.s({ 'interactive': true, 'onClick': function (event, data, view, point, width, height) { map.a('fitDataTag', 'plane2D'); mapGV.fitData(plane, false); mapDM.md(fitFlowP); } }); buttonL.s({ 'interactive': true, 'onClick': function (event, data, view, point, width, height) { mapDM.umd(fitFlowP); map.a('fitDataTag', 'flyLine'); mapGV.fitData(flyLine, false); } }); // ...省略
增加鼠标移到飞机相应位置进行名称的提示、双击后显示飞机相应位置的信息面板并将视角聚焦到面板上、点击飞机任意地方切换回飞机飞行模式等效果。
左侧增加监控面板替代上面提到的双击相应位置这步操作直接聚焦到相应位置的信息面板上,这里按钮开启了交互并添加了相应的交互逻辑,代码如下:
button_JC.s({ 'interactive': true, 'onClick': function (event, data, view, point, width, height) { event.preventDefault(); let g3d = G.g3d, g3dDM = G.g3d.dm(); g3d.fireInteractorEvent({ kind: 'doubleClickData', data: g3dDM.getDataByTag(data.getTag()) }) } }); //...省略
天空渲染效果
既然是监控系统肯定是 24 小时无差别的监控,这就涉及到一个问题,我总不可能半夜的时候飞机也从瓦蓝瓦蓝的天空上飞过,这就很欠缺真实性了,所以要有一个天空从亮到暗再从暗到亮的过程,这个过程我暂定到 06:00-06:30 和19:00-19:30 这两个时间段。
天空采用的是 shape3d : 'sphere' 球形,包裹整个场景,然后使用 reverse.flip 背拷贝 和 blend 染色,之后天空就可以渲染成我想要的颜色,如果按照时间改变天空明暗只要改变染色值就可以了。
但是由于白天和晚上光照情况的不同,云反射光的强度也不同,就导致了白天和晚上云的差异,所以也要调整云道和云背景的贴图的 opacity 透明度,晚间更为透明度,代码如下:
if ((hour > 6 && hour < 19) || (hour == 6 && minutes >= 30)) { timePane && timePane.a({ 'morning.visible': false, 'day.visible': true, 'dusk.visible': false, 'night.visible': false, 'day.opacity': 1 }) skyBox.s({ "shape3d.blend": 'rgb(127, 200, 240)', }) cloudBackground.s({ "back.opacity": 0.7, }) clouds.s({ "shape3d.opacity": 0.7, }) } else if ((hour < 6 || hour > 19) || (hour == 19 && minutes >= 30)) { //...省略 } else if (hour == 6 && minutes < 15 ) { //...省略 } else if (hour == 6 && minutes >= 15 && minutes < 30) { //...省略 } else if (hour == 19 && minutes < 15) { //...省略 } else if (hour == 19 && minutes >= 15 && minutes < 30) { //...省略 }
这里我还增加了对右上角时间面板时间状态图标的支持,并增加了图标切换时的渐隐渐显效果,同时给时间面板状态图标位置增加了点击切换到下一时间状态的功能。
为了演示效果我增加了时间倍速按钮,下图是 500 倍时间流速下的变化情况:
感谢各位的阅读!关于“HTML5中WebGL怎么实现民航客机飞行监控系统”这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,让大家可以学到更多知识,如果觉得文章不错,可以把它分享出去让更多的人看到吧!