HTML5+WebGL 打造精美的 Web 3D 机房监控系统

无插件纯Web 3D机房,HTML5+WebGL倾力打造

作者 twaver 2015-07-08

前言

最近项目开发任务告一段落,刚好有时间整理这大半年的一些成果。使用 html5 时间还不久,对 js 的认识还不够深入。没办法,以前一直搞 java,对js的一些语言特性和概念一时还转换不过来。

这一回我想介绍一下项目中的一个亮点技术:html5 的 3D,以及如何用它打造精美的 3D 机房监控系统。

目标效果图

下图是客户给找的一张的效果参考图,希望机房至少能达到下面的3D效果。

这里写图片描述

懂的人都知道,这可是一张设计公司出的装修效果图啊,就算是用max建模,也需要大量的工作,何况咱可是程序员在做数据中心的可视化项目啊。。。强忍心中奔腾的万千头**马,静下心来思考,那就先从搭建一个webGL的场景开始吧。

WebGL基本场景搭建

在html5里面使用3D已经不是什么高深技术,它的基础是WebGL,一个OpenGL的浏览器子集,支持大部分主要3D功能接口。目前最新的浏览器都有比较好的支持,IE需要到11(是的,你没有看错)。

要检测你的浏览器是否支持webGL,可直接访问网页http://get.webgl.org/ 看是否能看到一个旋转的立方体。如果能看到,说明你的浏览器支持webgGL,否则,可以下一个最新的chrome试试吧。相对来说chrome对webGL的支持最好,效率也很优秀。

要在浏览器里面使用webGL,就要研究webGL相关的技术和用法。做3D应用并不是一件轻松的事。就算最简单的搭建一下webGL场景,也需要下面这些代码:

var width = window.innerWidth;  
var height= window.innerHeight;  
var container = document.createElement( 'div' );  
document.body.appendChild( container );  
var webglcanvas = document.createElement('canvas');               
container.appendChild(webglcanvas);   
var gl = webglcanvas.getContext("experimental-webgl");                

function updateFrame () {             
  gl.viewport ( 0, 0, width, height );  
        gl.clearColor(0.4, 0.4, 0.7, 1);  
        gl.clear ( gl.COLOR_BUFFER_BIT );       
         setTimeout(   
    function(){updateFrame()},  
            20);  
     }  

setTimeout(   
  function(){
    updateFrame();
  },  
20);  

和html一样,需要先创建一个canvas元素,并获得其webgl上下文:

var gl = webglcanvas.getContext("experimental-webgl");

然后在一个updateFrame的函数中,像html5的2D context一样,去绘制3D的内容。

另外,要再起一个死循环,每隔**毫秒调用一次这个updateFrame函数来重绘场景。和2D不同,3D场景里面的变化是随时随地的,所以需要不停刷新,就像播放电影或视频,静止不动的画面基本没有,所以死循环刷新基本是必要的。不过实际项目使用中会有很多优化,尽量做到“按需刷新”,节省cpu和移动设备电量。有感兴趣的同学,哥可以单独写文章介绍。这段程序基本上什么也没做,就画了一个静止不动的区域,如下图:

这里写图片描述

虽然看不见任何3D的内容,不过它已经是一个最简单的webgl程序了。我们的3D机房,也就是在这上面不断丰富而已。

对象封装

要做项目,搭建下去工作量太大了,时间周期也不允许。使用第三方辅助工具是不可避免的,像Three.js, twaver.js都是选择。这些工具都可以提供3D的基本对象和各种特效,当然这都不是最主要的,主要是如何利用它做出我想要的效果:好看。为了避免大量修改代码,在项目里做了一些封装,即把原始3D的立方体等对象进行进一步封装,让一个json数据就可以提供这些对象的定义。这样使用起来就比较方便了。json大致结构如下:

var json={	
objects: [{
	name: '地板',},{}],
}

下面我们逐一来看这些3D对象是怎么进行美化的,过程可能稍显啰嗦,跬步千里,这次的基础打好了,以后的项目就手到擒来了。

地板和斜坡###

第一个要做的,也是应该比较简单的,就是地板对象。3D中,地板应该是一个有些厚度、带上格子贴图的薄薄立方体平面。因此我对经过封装的立方体对象,用一段json对象定义如下:

{
	name: '地板',
	type: 'cube',
	width: 1600,
	height: 10,
	depth: 1300,
		
	style: {
		'm.color': '#BEC9BE',
		'm.ambient': '#BEC9BE',
	}
}

通过定义,创建了一个13米*16米的地板块,这也是客户小型机房的实际尺寸:

这里写图片描述

看起来有那么点意思,就是颜色还不够,需要找一个地板砖纹理图。需要注意的是,纹理图的尺寸都需要是宽和高都是 2 的幂,例如 128x128、256*256 等,这样出来效果才会好。这也是 3D 软件一般所要求的。另外纹理要能连续拼接不露破绽,这样才好。例如下面我 google 出来的图:

这里写图片描述

在style里面添加:

   'top.m.texture.image': 'images/floor.png',
   'top.m.texture.repeat': new mono.Vec2(10,10),

效果如下:
这里写图片描述

有图片材质纹理,效果果然好多了。突然想到客户说,他们机房底面有一个方便运送设备的斜坡,必须要画出来。这……(╯-_-)╯

这里写图片描述

后来想到twaver里面的对象可以支持运算,比如可以定义一个斜的立方体,让地板剪掉立方体,就可以做到。于是继续定义json:

{
	name: '地板切坡',
	type: 'cube',
	width: 200,
	height: 20,
	depth: 260,
	translate: [-348,0,530],
	rotate: [Math.PI/180*3, 0, 0],
	op: '-',
	style: {,
	}
}

这里定义的一个倾斜的立方体,通过translate定义位置,rotate定义旋转角度,然后再通过op定义运算符,这里是“减去”,就用“-”表示。被剪掉的立方体也可以设置材质、纹理、贴图、颜色…等等,和地板一样。看看效果:

这里写图片描述

第一步总算是有惊无险地搞定了。

走廊桌

下一步找了个简单的对象,按要求走廊要放一个接待桌。为了简单,我决定就偷懒做一个立方体表示。

{
	name: '走廊板凳',
	type: 'cube',
	width: 300,
	height: 50,
	depth: 100,
	translate: [350, 0, -500],
}

效果如下:

这里写图片描述

这里偷懒其实是有原因的。在3D里,最重视的就是效率,千万不要放一些很复杂的模型,尤其是这类非业务对象。就像这个桌子,尽管只是个简单的立方体,但只要和整体风格协调一致,再增加一点配色并启动阴影效果后,看着就好多了:

这里写图片描述

墙体

墙体是机房里很重要的一个部分,有好的光照、阴影的效果才能看起来更加逼真。由于墙体是不规则的路径,一段一段去生成还真挺麻烦的,还好引擎支持这种物体,甚至曲线路径都可以。这里只要在json里面定义一组数字的坐标,让这些数字依次连接,组成一个墙体,最后生成3D对象放入场景中就行啦。

json定义如下:

{
	name: '主墙体',
	type: 'path',
	width: 20,
	height: 200,
	translate: [-500, 0, -500],
	data:[
		[0, 0],
		[1000, 0],
		[1000, 500],
		[500, 500],
		[500, 1000],
		[0, 1000],
		[0,0],
	],
}

注意这里的类型变成了pathdata中定义了一个二维坐标数组来描述墙体。由于墙都是从底面开始的,所以只定义它的平面的x、y坐标就行了。看看效果:

这里写图片描述

不过如前文所说,还是需要上色、上阴影,才能有更好的效果。这里我们启用阴影并咨询设计师美眉几个颜色值,加上去后再看下效果:

这里写图片描述

以及一些细节:

这里写图片描述

看着雪白的墙,是不是觉得少了点什么?对,就是门。在3D机房的监控系统里,门禁是很重要的一块,客户要求门应该与实际位置相对应,并且要有开门关门的动画效果。这样,实际的门禁信息采集上来后,就能在界面实时看到门的状态了。

这里,考虑到门如果直接放上去,会被墙盖住;如果比墙厚,又难看不符合实际。还是应该先定义一个门洞立方体,把门所在的位置挖掉:

{
	name: '门洞',
	type: 'cube',
	width: 195,
	height: 170,
	depth: 30,
	op: '-',
	translate:[-350,2,500],
}

刚好挖在斜坡的位置,这样设备进屋就方便了:

这里写图片描述

不过这门没有一个门框,感觉不太生动。多一个门框会感觉立体感强一些。门框可以是一个比门洞略大的立方体,在挖门洞之前添加:

{
	name: '门框',
	type: 'cube',
	width: 205,
	height: 180,
	depth: 26,
	translate: [-350, 0, 500],
	op: '+',
}

加上阴影和光线等综合效果后,还不错,挺有档次的。

这里写图片描述

来张全景图看看:

这里写图片描述

接着,只要把门安上去就行了。门的定义比较简单,就是一个薄的立方体。不过为了做到玻璃效果,需要设置透明度,让它看上去更像一个玻璃,再让设计师美眉弄一张好看一点的门的图,贴上去。尽管有了webGL,复杂的建模工作可以省略了,不过设计师美眉的配合仍然很重要。
先做左边的门:

{
	name: '左门',
	type: 'cube',
	width: 93,
	height: 165,
	depth: 2,
	translate:[-397,4,500],
	style:{
		'm.transparent': true,
		'm.texture.image': 'images/door_left.png',					
	}

上面增加的style主要透明和贴图两项。看看效果:

这里写图片描述

同样的方法,再把右侧门贴上就搞定了。为了增加体验,也是用户的要求,门上面设置了动画:双击可以自动打开,再双击可以直接关闭。动画功能引擎做好了封装,在json中直接指定动画类型就行了。不过要注意左右门的动画旋转方向要相反,要不然一个向里开一个向外开感觉比较怪异。

项目中,窗本身不需要有任何业务属性,但是美观度的要求可一点都不能少。方法和门类似,先放窗框后挖窗体。不过为了有点变化,这里不做窗框了,做一个窗台,方法和道理与门相同。

{
	name: '主窗户洞',
	type: 'cube',
	width: 420,
	height: 150,
	depth: 50, 
	translate: [200, 30, 500],
	op: '-',
},{
	name: '主窗户台',
	type: 'cube',
	width: 420,
	height: 10,
	depth: 40, 
	translate: [200, 30, 510],
	op: '+',
}

定义了一个窗洞(挖掉)、一个窗台(添加)。一个大窗户就做好了:

这里写图片描述

再添加一个略带颜色的透明玻璃。玻璃设置点高光和反射,增加“玻璃”感觉:

{
	name: '主窗户玻璃',
	type: 'cube',
	width: 420,
	height: 150,
	depth: 2,
	translate: [200, 30, 500],
	op: '+',
	style: {
		'm.transparent': true,
		'm.opacity':0.4,
		'm.color':'#58ACFA',
	},			
}

json中玻璃设置了透明度和颜色。这样一个半透明的茶色玻璃就好了:

这里写图片描述

到这里突然在想:盖房子如果像写程序一样简单就好了,所有的程序猿就不会是无房一族单身狗了。当然写程序和盖房子一样:该封装好的要封装好,最后就是搭积木组装就行了。如果盖房子都是从挖土活泥巴开始,那就杯具了。写程序也是一样,如果从webGL的main开始写……这3D机房的系统要几个月甚至几年才能做出来呢?

外侧墙

按照项目实际要求,继续增加更多建筑物墙体。主要是房间外侧有两道走廊隔墙。这是一个中间有大片透明玻璃的走廊隔墙,需要做的好看一点。由于是直线墙,没有复杂走向,直接用立方体定义:

{
	name: '左外墙',
	type: 'cube',
	width: 20,
	height: 200,
	depth: 1300,
	translate: [-790, 0, 0],
	op: '+',
}

效果如下:

这里写图片描述

再继续挖掉中间的窗户部分:

{
	name: '左外墙洞',
	type: 'cube',
	width: 30,
	height: 110,
	depth: 1300,
	translate: [-790, 60, 0],
	op: '-',
}

这里写图片描述

空白显得很奇怪,加上玻璃试试:

{
	name: '左外墙玻璃',
	type: 'cube',
	width: 4,
	height: 110,
	depth: 1300,
	translate: [-790, 60, 0],
	op: '+',
	style: {
		'm.transparent': true,
		'm.opacity':0.6,
	},
}

这里写图片描述

有了半透明和高光的效果,看起来就有质感了,右边也如法炮制:

这里写图片描述

这样,整个建筑的外观就基本完成了。最后,放一些绿植,增加些生气吧。

植物

做一盆植物,需要有一个空的花盆,花盆里面有泥土,上面有一株植物。这些东西用3D做起来都有点啰嗦。不过也不难。花盆用一个大圆柱剪掉中间的小圆柱,做成空心花盆;植物用贴图+透明模拟一下就行,不用真的去做植物的3D模型,否则要累死了。

根据上面的思路,在项目中通过仔细调整,把创建花盆的代码封装好,然后在json中定义花盆位置就行了。下面定义一个:

{
	name: '花1',
	type: 'plant',
	translate: [560, 0, 400],
}

程序中解析如果type是plant则创建植物对象并添加场景。

这里写图片描述

在房间、走廊、甚至窗台上都可以放几盆,窗台上的可以通过设置scale缩小一些,并提升其高度到窗台位置即可。

这里写图片描述

看看下整体效果,还不赖吧。

这里写图片描述

机柜和设备

写了那么一大篇,才终于把3D机房的外观装修完成,咱也算是个设计师程序员的混合型人才了呢。其实机房最核心的资源——机柜,还没找落呢,没办法,形象工程也是项目建设的一大亮点。

机柜

机柜,以及其中的服务器设备。这才是3D机房里面最终要管理的内容。在我们的实际项目中,这些资产都是在数据库中存储,并通过json接口加载到浏览器中显示。这里为了演示方便,直接写几个机柜的片段,看一下显示效果。

这里写图片描述

机柜对象在项目中是这样封装的:用一个立方体来表示机柜,并加上贴图。项目中,为了提高显示速度,机柜一开始并不加载内部服务器内容,而是只显示自身一个立方体。当用户双击后,会触发一个延迟加载器,从服务器端加载机柜内部服务器,并加载到对应的位置上。此时,机柜会被挖空成一个空心的立方体,以便视觉上更像一个机柜。

定义机柜的json如下:

{
	name: '机柜',
	type: 'rack',
	lazy: true,
	width: 70,
	depth: 100,
	height: 220,
	translate: [-370, 0, -250],
	severity: CRITICAL,
}

上面的机柜定义中,有一个lazy标记,标记它是否延迟加载其内容。如果延迟加载,则双击触发,否则程序显示时直接加载其内容。Severity是定义了机柜的告警信息,它是否有业务告警。如果有告警,会用一个气泡显示在机柜的上方,同时机柜也会被染色成告警对应的颜色。

加入更多的机柜看看效果:

这里写图片描述

设备

简单起见,这里管理的设备假设都是机架设备,尺寸规格比较规整,因此比较容易在机柜中组织。一个设备的外观确定后,在数据库中定义好模板,加载时根据其所在机柜的位置放置即可。

这里写图片描述

这里只是随机生成了几个服务器设备,并按位置摆放。在实际应用中,可以通过手工录入或者智能机架报送的信息来确定服务器的类型和位置。

这里写图片描述

如果需要监控到端口级别,还可以在服务器弹出后,再进一步延迟加载设备商的板卡、端口对象,并点击后进一步进行配置、监控等操作。当然加载的数据越细,对3D引擎和浏览器的压力会越大。可以通过动态延迟加载/卸载策略,获取一些平衡折中。

电视机

纯属无聊,再做一个电视机挂在墙上。依旧,定义一个立方体、挖空屏幕,放上透明玻璃,再贴上我们喜欢的电视节目画面,就ok了。

{
	name: '电视机体',
	type: 'cube',
	width: 150,
	height: 80,
	depth: 5,
	translate: [80, 100, 13],
	op: '+',		
},{
	name: '电视机挖空',
	type: 'cube',
	width: 130,
	height: 75,
	depth: 5,
	translate: [80, 102.5, 17],
	op: '-',
},{
	name: '电视机屏幕',
	type: 'cube',
	width: 130,
	height: 75,
	depth: 1,
	translate: [80, 102.5, 14.6],
	op: '+',
	style: {
		'front.m.texture.image': 'images/screen.jpg',
	},
}

当然,实际项目中,可以换上监控大屏幕的效果:

这里写图片描述

总结

整个场景写到最后,我也已经脑洞大开游刃有余了。3D场景,尤其是这类业务系统,并不一定要死抠模型的仿真度,才能做到“好看”的效果。先来一张全景看一下:

这里写图片描述

怎么样,还算精美吧?基本不输前面看到的广告公司的效果图。但和效果图一张死图片不一样,我们这是一个能操作、能漫游、能缩放、有动画、显示流畅、浏览器无需插件就能直接打开的3D机房小程序,就一个json文件和一百多行代码和一天的时间就搞定了,还是让人有点惊讶的。

这里写图片描述

不用插件、不用3Dmax,不用模型库,干干净净纯粹的小程序,手机和平板也能用哦,而且还很流畅!上一张我的Nexus5截图瞅瞅:

这里写图片描述

经过优化,场景加载已经控制在600毫秒以内,缩放漫游也很流畅。当然,技术和美化永无止境,用户的需求也千变万化精益求精。但只要我们选择好了技术和工具,就能事半功倍。Html5就是极佳的一个选择。


无插件纯Web HTML5 3D机房 进阶篇(新增设备、线缆、巡查等功能)

前情提要

上一篇中主要介绍的是如何从最基础的webgl封装到创建3d物体对象,再通过3d物体对象“搭积木”式的组建整个3d机房场景。这一篇主要介绍一些如何在这个场景上进一步丰富更多的功能和呈现效果,以及实现这些功能在技术上的思路。

首先我们来看看第一季已经实现的纯天然无添加无PS的HTML5 3D机房效果:

这里写图片描述

已经拿到过代码的朋友应该知道,这一场景是通过一个json文件进行组装和加载,可以很方便地进行修改和维护。在此基础上,这一次我又增加了机柜标签、机柜门、复杂设备、机房走线、人员轨迹等效果,下面我就把第二季的干货一一为大家奉上。

机柜标签

机房中最重要的物理资源——机柜——是机房管理、规划、监控人员最关注的对象之一。对于规模在几十个、上百个甚至上千个机柜的机房,每个机柜必然会进行资产编号,方便检索和管理。这个在多数资产管理系统中,都是最基本的。但是在3d场景中,如何显示这些机柜编号,才能让用户更直观的看到每个机柜的位置呢?

传统的方式是用标签显示资产编号,例如可以用“告警冒泡”那样的方式显示一个文字气泡。不过当机柜产生告警时,两个气泡会有所冲突。而且过多的气泡会产生相互遮挡覆盖,有点混乱,比如像这样:

这里写图片描述

因此我尝试了一种不同的思路:把文字渲染到一个内存图片,**“溶解”**到机柜的上方贴图中。

想要生成一个内存的图片文字,可以通过下面的函数实现:

	generateAssetImage: function(text){         
		var width=512, height=256;

		var canvas = document.createElement('canvas');
		canvas.width  = width;
		canvas.height = height;

		var ctx = canvas.getContext('2d');		
		ctx.fillStyle='white';
		ctx.fillRect(0,0,width,height);
		
		ctx.font = 150+'px "Microsoft Yahei" bold';
		ctx.fillStyle = 'black';
		ctx.textAlign = 'center';
		ctx.textBaseline = 'middle';
		ctx.fillText(text, width/2,height/2);
		ctx.strokeStyle='black';
		ctx.lineWidth=15;
		ctx.strokeText(text, width/2,height/2);

		return canvas;   
	}

需要留意的是:

  1. 生成的图片宽高数值最好是2的幂,例如128、256、512等,这样在3d中渲染不容易出现闪烁和锯齿。相关原理请查阅google。
  2. 文字绘制尽量居中、充满整个图,不要太小,否则看上去比较奇怪。
  3. 空白处保持透明,不必填充色,方便和机柜本身贴图的“溶解”。
  4. 直接返回canvas对象即可,不必生成图片点阵数组。

生成canvas后,可以这样直接贴图使用:

	var labelCanvas=demo.Default.generateAssetImage(label);
	rack.setStyle('top.m.texture.image', labelCanvas);
	rack.setStyle('top.m.specularmap.image', labelCanvas);

通过上面代码,把贴图作为机柜立方体top面的贴图和反射映射图。这样出来的效果如下:

这里写图片描述

这样,既不用增加3d对象,也不影响机柜的美观度,关键是看得非常清晰,在大场景中也不干扰用户的视线:

这里写图片描述

机柜门

上一篇里,由于时间紧迫,也考虑到呈现效率,机柜采用了“双击机柜出现设备”的方案。一个立方体的机柜虽然简单直接,但是没有机柜门,总觉得假了一点,客户也提到了这一点,因此按照机房门的思路,增加一个机柜门,增加双击开门的效果,这个比较简单:

	var rackDoor = new mono.Cube(width, height, 2);
	rackDoor.s({
		'm.type':'phong',
		'm.color': '#A5F1B5',
		'm.ambient': '#A4F4EC',
		'front.m.texture.image': 'images/rack_front_door.png',
		'back.m.texture.image': 'images/rack_door_back.png',
		'm.envmap.image': demo.Default.getEnvmap('envmap1'),
	});
	rackDoor.setParent(rack);
	rackDoor.setPosition(0, 0, depth/2+1); 
	rackDoor.setClient('animation','rotate.right.120');	

上面代码创建了一个薄薄的立方体作为机柜门。设置贴图、颜色等,再设置其parent是机柜。这样,如果拖拽机柜位置,机柜也会跟着移动,简单方便。最后,在设置一下机柜门的动画。通过一个字符串进行定义:rotate.right.120表示动画是“向右侧旋转120度”,在双击的时候触发。

这里写图片描述

复杂电信设备

第一季里,机柜内的设备,主要用乐服务器来表现,加入了设备弹出、告警等效果。后期根据现场和用户的交流,用户进一步提出要显示机架上需要安装更加复杂的电信设备,包括板卡、板卡的插拔动作、呈现方法等,也就是在机柜上显示一个有多个板卡的设备,双击板卡可以弹出。

要做这个,需要把原来的一个立方体的服务器设备做一个“挖空”处理,变成一个空的设备框的样子。然后再生成一系列的板卡对象,插入这个空框。每个板卡应该由面板+电路板组成,可以用两个立方体进行拼接,添加适当的贴图完成。代码如下:

	var parts=[{
		//card panel
		type: 'cube',
		width: width,
		height: height,
		depth: 1,
		translate: [x, y, z+1],
		rotate: rotate,
		op: '+',
		style:{
			'm.color': color,
			'm.ambient': color,
			'm.texture.image': 'images/gray.png',
			'front.m.texture.image': pic,
			'back.m.texture.image': pic,
		}
	},{
		//card body
		type: 'cube',
		width: 1,
		height: height*0.95,
		depth: depth,
		translate: [x, y, z-depth/2+1],
		rotate: rotate,
		op: '+',
		style:{
			'm.color': color,
			'm.ambient': color,
			'm.texture.image': 'images/gray.png',
			'left.m.texture.image': 'images/card_body.png',
			'right.m.texture.image': 'images/card_body.png',
			'left.m.texture.flipX': true,
			'm.transparent': true,	
		}
	}];
	
	var card=demo.Default.createCombo(parts);
	card.setClient('animation', 'pullOut.z');
	box.add(card);

上面代码可以生成一个板卡对象。循环重复,设置位置,即可生成整个设备。通过设置animation属性,定义板卡动画为**“双击拉出”**,再次双击推回。效果如下图:

这里写图片描述

这里写图片描述

当然,实际项目中,各种结构的电信设备千奇百怪,要通过代码定义是不现实的。所以我们还开发了一个设备编辑器,可以通过拖拽方式快速生成设备结构图。

这里写图片描述

机房线缆和走线架

除了机柜之外,线缆的连接走向和连接关系是管理员关注的另外一个焦点。机架中的电信设备或服务器设备会通过端口和线缆进行连接,组成一定结构的网络。而线缆的走向在物理上通过肉眼是很难看清晰的。更多线缆会从机柜连出,延伸到屋顶上方或地板下方的隐蔽工程中(例如走线架)固定和布线,用肉眼更无法观察。此时,需要3d机房界面能清晰的显示电缆从端口到走线架再到端口的**“端到端”**的物理走线,方便管理员了解网络情况和管理。

线缆

线缆,可以用一个空间的path来定义,并设置其贴图:

	var path = demo.Default.create3DPath(json.data);
	var cable=new mono.PathNode(path, 100, 1);
	cable.s({
		'm.type': 'phong',
		'm.specularStrength': 30,
		'm.color': json.color,
		'm.ambient': json.color, 		
		'm.texture.image': 'images/flow.jpg',
		'm.texture.repeat': new mono.Vec2(200, 1),
	});
	box.add(cable);

通过json定义的[x, y, z]数组来生成一个path对象,然后用它来生成一个空间的“管子”对象。流动效果可以通过一个动画来修改贴图纹理的offset,让眼睛产生贴图不断**“移动”**的效果。

这里写图片描述

走线架

走线架可以通过简单的镂空贴图来模拟,不需要建一个复杂的框架模型,减少对GPU的压力。实际的走线架有很多种,例如下图是一种典型的走线架:

这里写图片描述

我们通过程序模拟一下:

	var rail=demo.Default.createPathObject(railInfo);
	rail.s({
		'm.texture.image': 'images/rail.png',
		'm.type': 'phong',
		'm.transparent': true,
		'm.color': '#CEECF5',
		'm.ambient': '#CEECF5',
		'aside.m.visible': false,
		'zside.m.visible': false,
		'm.specularStrength': 50,
	});
	rail.setPositionY(263);
	box.add(rail);

最终走线架+线缆的效果如下:

这里写图片描述

路径规划

在3d场景中,经常需要计算规划并显示一个最优的空间或平面路径,用来指示如何最快、最合理的到达目标,或通过路径显示一个移动物体的轨迹,方便进行监控和分析。例如,客户提出一个实际需求:现场检修人员手持平板进入机房,输入设备id后,直接给出前方走动路径,并进行实时导航引导

我们可以把这个需求分解为导航路径显示和人员模拟两步。 导航路径

导航线可以通过一个浮在地板上方的path路径来定义。做一个简单的颜色渲染和弯角处理,就可以比较清晰的展示底面的走动路径。

			var path=new mono.Path();
			path.moveTo(object.getPositionX(), object.getPositionZ());
			for(var i=0;i<points.length; i++){
				path.lineTo(points[i][0], points[i][13]);
			}
			path = mono.PathNode.prototype.adjustPath(path, 20);

			var trail=new mono.PathCube(path, 10, 3);
			trail.s({
				'm.type': 'phong',
				'm.specularStrength': 30,
				'm.color': '#298A08',
				'm.ambient': '#298A08', 		
				'm.texture.image': 'images/flow.jpg',
				'm.texture.repeat': new mono.Vec2(150, 1),
			});
			trail.setRotationX(Math.PI);
			//trail.setStartCap('plain');
			//trail.setEndCap('plain');
			trail.setPositionY(5);
			trail.setClient('type', 'trail');
			box.add(trail);

这里写图片描述

拉近后看下细节:

这里写图片描述

移动轨迹的显示也有很多变化,形状、颜色的变化,空间坐标的变化,都可以产生一些不同的效果。例如下图是另外一个3d车库导航系统的场景:

这里写图片描述

加载人物模型

接下来,要加载一个人的模型进来,放在路径上。可以在网上找一些3d max做的模型,并转成obj格式的文件,这样就可以方便的导入场景中。Obj通过对应的mtl文件进行定义材质,需要的贴图也需要一并涵盖进来。通过下面几行代码即可完成obj模型的导入:

		var obj='images/worker.obj';
		var mtl='images/worker.mtl';               
			
		var loader = new mono.OBJMTLLoader();
		loader.load(obj, mtl, {'worker': 'images/worker.png',}, function (object) {                    			
			box.addByDescendant(object);
		});

效果如下图:

这里写图片描述

需要留意的是,人的模型不要太大,能把人物轮廓大概渲染清晰即可。例如上面的模型也就几百k,加载和显示几乎瞬间完成,不会产生卡顿。如果加载几十兆上百兆的高清模型,则可能出现卡顿,也会拖慢场景的渲染速度。毕竟我们的主要场景对象是3d机房,对模型选择要有所取舍,千万不要喧宾夺主。

让任务沿着路径移动,可以通过动画进行控制,对path中每一段路线进行平移,连续、反复的播放,即可实现人物的移动效果。

这里写图片描述

当然这里有一个缺点,人物的移动是僵硬不动的,不能像真实人物一样迈腿一步一步的走路进行移动。如果要做,需要用到骨骼动画等技术,相对复杂一些,目前项目紧急,就留给以后找时间研究了。当然对于3d机房这样的企业应用来说,必要性不一定很大,毕竟不是游戏。

通过这些技术,大家可以轻松构建一个比较完整的3d机房系统,就跟我们这次的项目一样:

这里写图片描述

这里写图片描述

这里写图片描述

后记

篇幅有限,这一篇就介绍这么多,希望对大家有所启发和帮助,后续找时间会继续给大家介绍如何拖拽移动机柜、显示机房中的温度云图、风向监控、摄像头及视频监控,如何创建一个大的园区和楼宇等内容。


无插件纯Web HTML5 3D机房 终结篇(新增资产管理、动环监控等内容)

原本以为这次的机房资产管理项目告一段落,可以歇一歇,哥还是太天真了。我们伟大的甲方又拿下了第二期的项目,誓把哥的才华发挥到极致啊。国庆长假也没正经休息几天,硬是给人折腾出了个demo,加上了容量管理、电源走线、告警巡航这些实用功能,以及温湿度、风向、门禁、视频监控效果

甲方虐归虐,思路还是比较清晰的,第一期重点放在三维呈现和静态的资产管理上,第二期着重动环监控,这样基本上一个比较完整的数据中心监控系统就出来了。废话不多说了,这就开始给大家介(嘚)绍(瑟)。

界面美化

这次先是做了一些界面的美化工作,最近跟设计师mm配合得不错,果然界面档次也有所提高。

右键菜单调整

随着demo上堆砌的功能越来越多,右键的按钮也不够使了,加之有朋友反映右键菜单有点隐蔽不容易找到,我把所有的功能效果都改为从工具栏按钮进入,直接纵向显示放在了左侧,效果还不错:

这里写图片描述

动态客户信息

上次在机柜顶部显示资产编码的方式得到了客户的肯定,这次又尝试在机柜组地板上动态生成客户的信息,这就是3D的好处啊,哪儿有空我贴哪里。

这里写图片描述

其它装饰性细节

上次随手找的CCTV主播电视画面果断被吐槽了,看来大家还是很严肃地在探讨问题,所以这次我也把电视画面改成了统计图表,另外新增了特别合时宜的海报,给哥点个赞?

这里写图片描述

这里写图片描述

资产管理功能

以上都是一些界面上的小改动,下面给大家上几个硬菜。

机架可用空间

当服务器陆续上架后,会对机柜的空间产生占用和分隔。及时了解整个机房中每个机架的占用情况和空闲空间的大小情况,是非常重要的日常工作。通过3d来呈现就再适合不过了:我们把有服务器占用的空间用白色块填充,有空闲的空间根据大小不同用不同的色块填充,就有了下方的效果:

这里写图片描述

图上的颜色不光是为了好看,上面1-2U的空间用红色、5U以下的用紫色,通过不同颜色来表示连续剩余空间的数量,白色表示已经占用的空间,这样对于机房管理人员来说,可以迅速掌握整个机房的占用情况。当机房要上架一个5U的设备,我们可以一眼看到哪里有紫色的色块,并快速定出在哪里上架。

实现这一效果很简单,简单说一下思路:切换空间视图后,首先把原来的机柜隐藏,然后在同样的地方根据空间和设备情况创建多个不同的立方体进行罗列。立方体的颜色根据立方体的高度进行调整。为了增加视觉效果,可以留意立方块不要完全紧挨一起,可以留一点空隙增加立体感;空闲的方块设置一定的透明度,以和乳白色的占用空间形成一定的差异;设置一定的反光度,增加色块的质感。如下图:

这里写图片描述

机柜利用率

项目还有个需求是显示机柜的整个空间使用率情况。使用率不用显示具体哪里占用哪里空闲,只要显示一个整体使用比例即可,相对简单一点。先用一个线框把机柜位置显示出来,再把一个高度符合使用比例的立方块显示出来就行了,类似一个柱状图。设置一下颜色、光照等属性,让它看上去更真实:

这里写图片描述

纯3d的“动画”冒出来的柱状图,还能绑定真实数据,也算是个很棒的应用了。GIF录得不咋滴,想看高清大动画的朋友,还是直接看源程序吧。

这里写图片描述

告警巡航

上一篇里,已经介绍了设备告警的用法,这里进一步增加了告警巡航的功能,即当一个告警发生后,自动将镜头推向故障点,并显示故障信息,这是告警巡航的作用。下面的图显示了当一个告警发生后,镜头会在场景中自动进行漫游,寻找机房门,进入,推向故障机柜,打开机柜门,拉出故障设备、板卡,并显示故障详细信息。

这里写图片描述

动力环境监控

动力环境监控的对象主要是机房动力和环境设备等设备,像配电、UPS、空调、温湿度、漏水、门禁、安防、消防、防雷等等,这些通常会有独立的采集和监控系统,我需要做的是把动环的效果用3D呈现,并且和系统的真实数据进行对接。

温度监控

温度监控是机房中动力环境监控的重要内容。机房里面大量的设备运行产生大量热量,需要及时监控温度的热点,避免温度过高导致设备故障。在我们的项目中,每个机柜都有温度采集器,每个机柜的纵向也有多个温度传感器,可以通过后台接口采集到。有了这些数据,就可以做温度的呈现了。一般传统的方法是显示温度数字,这样并不太直观。新潮一点的玩法是显示温度云图。也就是根据温度采样点动态生成一个渐变的温度场云图,再将图贴在地板上,这样能更直观的观察到温度在空间的变化。

这里写图片描述

温度云图模拟了热衰减的过程,并在地板上留下痕迹。同样,纵向的设备上也通过温度数值来生成色差不同的温度图贴在机架上,这样一眼就能看到整个机柜的热点在哪里,以便及时查看故障或调整空调气流。

对于没有机柜的位置,可以通过增加一个纵向空间切片来显示温度云图:

这里写图片描述

上图在机柜旁边纵向虚拟切片,并对这一切面上进行温度采集、渲染。可以观察出温度随着气流、空间的变化而产生的变化,从而知道整个空间的热点分布。可以根据需要增加更多切片,不过过多切片也会相互遮盖,影响视觉效果。

生成温度场的代码如下。需要传入机架和机架上的温度采集点信息:

createSideTemperatureImage: function(rack, count){
		var width=2;
		var height=rack.getHeight();
		var step=height/count;
		var board = new TemperatureBoard(width,height,'v', height/count);		

		for(var i=0;i<count;i++){		
			var value=0.3+Math.random()*0.2;
			if(value<4){
				value=Math.random()*0.9;
			}
			board.addPoint(width/2,step*i,value);
		};

		return board.getImage();
	}

以上温度值是随机产生,项目中可接入实际数值。

空调风向

机房中的空调冷风流向也是一个需要监控的业务。一般冷风从地板下方吹出、穿过机柜从上方流走。如果有通道的情况,冷风的冷却效效率会更好。下图展示了冷风是如何流动的。其中箭头会用流动的动画效果展示。

这里写图片描述

通过颜色的变化可以看到空气随着流动的变化而产生的温度变化。(下面是一张动图,好像要戳开才会动)

这里写图片描述

要做这一效果,先创建一个曲面的对象,然后在上面添加箭头的贴图。然后启用一个动画,来控制贴图的位移,就可以实现动画流动的效果了:

	toggleAirView: function(network){
		network.airView=!network.airView;

		if(!network.getDataBox().airPlanes){
			network.getDataBox().airPlanes=demo.createAirPlanes();
		}

		for(var i=0;i<network.getDataBox().airPlanes.length;i++){
			var plane=network.getDataBox().airPlanes[i];
			if(network.airView){
				network.getDataBox().add(plane);
				plane.airAnimation.play();
			}else{
				network.getDataBox().remove(plane);
				plane.airAnimation.stop();
			}
		}		
	}

烟雾监控

现在的建筑中一般都有烟雾监控传感器,当有烟雾发生时会发生报警。下图模拟了当发生烟雾的情况,我们在着火点用一团烟雾来渲染,增加场景的逼真度。同时通过动画,来模拟烟雾的冒出情况。

这里写图片描述

同时把场景中的灭火器的位置高亮并用箭头动画示意,提示灭火器材位置,及时施救。

这里写图片描述

然而,这个功能可能并没什么卵用。一旦机房失火,产生的后果将是灾难性的,不是几个灭火器能够解决的。最好还是做好防灾预案和日常维护。

漏水监控

机房除了怕火,再就是怕水。机房漏水产生的后果恐怕不必火灾更小。水会直接导致设备和损坏,应该大力防范。水的产生一般来自管道或设备的漏水,可以通过地板铺设漏水绳进行监控。下图展示了当机房中铺设多条漏水绳并发生漏水的情况:

这里写图片描述

漏水点可以通过漏水绳精确检测,我们在3d场景中进行精确定位和显示,并通过一个标签指示其位置。为了观察的更清晰,这里动态虚化了机柜和房间等物体,能一目了然看清漏水绳的走向和分布。

这里写图片描述

防盗监测

机房会严格限制人员的进入。对于敏感区域,可以放置防盗红外或激光探头,当有人员通过,会立刻发起警报,起到防止进入和防盗的效果。这种激光防盗在各种电影大片中已经屡见不鲜了:

这里写图片描述

似乎密室逃脱、拓展训练等游戏里面都有了,急速前进里筷子兄弟过激光阵可把我看得愁死了,下次公司组织活动的时候可以搞一把:

这里写图片描述

回到机房,不是拍电影的话一般不用布成天罗地网,比较规整的线就可以了:

这里写图片描述

里面的射线可以通过line对象创建,设置颜色、透明度、反光度等即可。貌似这个机房要想进去偷点东西还挺有难度的吧。

这里写图片描述

电源走线

沿着这个思路,利用line对象,可以创建更多连线管理。例如电源的布线走线显示。每一个机柜的供电线布局走线,可以通过不同的颜色和走向进行显示。这样会比传统的表格或2d图形显示更加直观。

这里写图片描述

安防监控

安防监控,最传统的做法是视频监控,在机房的各个位置放上摄像头,然后集中对画面进行监控。

这里写图片描述

视频监控

在我们的3D机房中,则可以通过json进行配置显示摄像头的位置,摄像头可以根据不同类型进行提前建模,根据json配置进行动态加载。:

这里写图片描述

双击摄像头,可以连接摄像头调出实时监控视频画面。这里放了一个假视频代替了。

这里写图片描述

门禁监控

安防的另一个重要部分就是门禁系统,通常门禁设备的厂家会自带管理系统,我们只需要把系统数据接进平台即可。在3D呈现上,我们可以把门禁的位置展示出来:

这里写图片描述

双击门禁控制器后,显示人员的进出信息:

这里写图片描述

三维拖拽功能

很多拿到程序的朋友留言问如何能够直接在界面上拖拽移动机柜何设备。通过 api 移动物体并不复杂,object.setPosition(x,y,z)就行了。不过在界面上如果在 3d 中直接通过鼠标拖拽就要考虑如何操作方便。点击这个按钮,可以切换到编辑模式,此时可以直接在界面上移动机柜位置。
这里写图片描述

编辑模式下,鼠标点击可以选中一个机柜。同时机柜会出现3个扇形和3个箭头。点中任意扇形拖拽,可以在所在的平面上平移;点击任意箭头拖拽,可以在这个轴向上平移。同时为了能看清坐标数值,在鼠标的tip中显示了当前x、y、z的数值,以及旋转、缩放比例的数值。如下图:

这里写图片描述

当然在这里,机柜只能允许在水平面平移,纵向平移没有什么实际意义,这都可以通过代码进行控制。下面的代码可以控制是否允许拖拽、是否允许缩放、旋转等操作:

var editInteraction = new mono.EditInteraction(network);
		editInteraction.setShowHelpers(true);
        editInteraction.setScaleable(false);
        editInteraction.setRotateable(false);
        editInteraction.setTranslateable(true);

这样,我们就可以很任性的对机房进行自定义布局了,很多家装设计的工具也是这样的原理。

其他

以上是这次大更新的主要内容,根据项目的需求,程序还做了其他很多小改进,具体大家可以自己看看。包括连线显示实时流量信息(如下图:)

这里写图片描述

动画显示机柜设备的加载过程:

这里写图片描述

更多细节改进就不再赘述,感兴趣的同学可以留言或发邮件到 tw-service@servasoft.com,欢迎一起探讨!如果大家想看到什么更多的效果也可以给我留言。时间有限,很多细节没办法写的很详细,请大家包涵。


via:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值