前言
随着各行业对计算机依赖性的日益提高,计算机信息系统的发展使得作为其网络设备、主机服务器、数据存储设备、网络安全设备等核心设备存放地的计算机机房日益显现出它的重要地位,而机房的环境和动力设备如供配电、UPS、空调、消防、保安等必须时时刻刻为计算机信息系统提供正常的运行环境。现如今工业4.0的改革崛起,工业互联网和 5G 等新基建的发展下,工业管控在可视化系统的搭载上越来越广泛,比起传统的机房,智能机房在节省很多人力劳力的基础上,还带来许多新的保障环境。
HT for Web 不止自主研发了强大的基于 HTML5 的 2D、3D 渲染引擎,为可视化提供了丰富的展示效果。介于 2D 组态和 3D 组态上,Hightopo(以下简称 HT )的 HT for Web 产品上的有着丰富的组态化可供选择,本文将运用 HT 搭载出一个基于 HTML5 WebGL 和 VR 技术的 3D 机房数据中心可视化系统。
界面简介及效果预览
在场景的搭建上,采用以 HT 的 HT for Web 产品轻量化 HTML5/WebGL 建模的方案,实现快速建模、运行时轻量化到甚至手机终端浏览器即可 3D 可视化运维的良好效果;同时配备 HT 特有的矢量图标作为控制面板,能适应各种屏幕的不同分辨率的大小而不失真。3D 机房场景和 2D 控制面板的结合,打造出一套机房数据中心可视化系统的解决方案。
1、整体场景-摄像头效果图
2、局部场景-摄像头效果图
系统分析
在 3D 机房数据中心可视化应用中,随着视频监控联网系统的不断普及和发展, 网络摄像机更多的应用于监控系统中,尤其是高清时代的来临,更加快了网络摄像机的发展和应用。
在监控摄像机数量的不断庞大的同时,在监控系统中面临着严峻的现状问题:海量视频分散、孤立、视角不完整、位置不明确等问题,始终围绕着使用者。因此,如何更直观、更明确的管理摄像机和掌控视频动态,已成为提升视频应用价值的重要话题。所以当前项目正是从解决此现状问题的角度,应运而生。围绕如何提高、管理和有效利用前端设备采集的海量信息为公共安全服务,特别是在技术融合大趋势下,如何结合当前先进的视频融合,虚实融合、三维动态等技术,实现三维场景实时动态可视化监控,更有效的识别、分析、挖掘海量数据的有效信息服务公共应用,已成为视频监控平台可视化发展的趋势和方向。目前,在监控行业中,海康、大华等做监控行业领导者可基于这样的方式规划公共场所园区等的摄像头规划安放布局,可以通过海康、大华等摄像头品牌的摄像头参数,调整系统中摄像头模型的可视范围,监控方向等,更方便的让人们直观的了解摄像头的监控区域,监控角度等。
系统效果实现
一、摄像头模型及场景
项目中使用的摄像头模型是通过 3dMax 建模生成的,该建模工具可以导出 obj 与 mtl 文件,在 HT 中可以通过解析 obj 与 mtl 文件来生成 3d 场景中的摄像头模型。
项目中场景通过 HT 的 3d 编辑器进行搭建,场景中的模型有些是通过 HT 建模,有些通过 3dMax 建模,之后导入 HT 中,场景中的地面白色的灯光,是通过 HT 的 3d 编辑器进行地面贴图呈现出来的效果。
二、锥体建模
3D 模型是由最基础的三角形面拼接合成,例如 1 个矩形可以由 2 个三角形构成,1 个立方体由 6 个面即 12 个三角形构成, 以此类推更复杂的模型可以由许多的小三角形组合合成。因此 3D 模型定义即为对构造模型的所有三角形的描述, 而每个三角形由三个顶点 vertex 构成, 每个顶点 vertex 由 x, y, z 三维空间坐标决定,HT 采用右手螺旋定则来确定三个顶点构造三角形面的正面。
HT 中通过 ht.Default.setShape3dModel(name, model) 函数,可注册自定义 3D 模型,摄像头前方生成的锥体便是通过该方法生成。可以将该锥体看成由 5 个顶点,6 个三角形组成,具体图如下:
ht.Default.setShape3dModel(name, model)
1. name 为模型名称,如果名称与预定义的一样,则会替换预定义的模型;
2. model 为JSON类型对象,其中 vs 表示顶点坐标数组,is 表示索引数组,uv 表示贴图坐标数组,如果想要单独定义某个面,可以通过 bottom_vs,bottom_is,bottom_uv,top_vs,top_is, top_uv 等来定义,之后便可以通过 shape3d.top.*, shape3d.bottom.* 等单独控制某个面。
2. model 为JSON类型对象,其中 vs 表示顶点坐标数组,is 表示索引数组,uv 表示贴图坐标数组,如果想要单独定义某个面,可以通过 bottom_vs,bottom_is,bottom_uv,top_vs,top_is, top_uv 等来定义,之后便可以通过 shape3d.top.*, shape3d.bottom.* 等单独控制某个面。
锥形建模实现的效果如下:
我将当前摄像头的 tag 标签值作为模型的名称,tag 标签在 HT 中用于唯一标识一个图元,用户可以自定义 tag 的值。通过 pointArr 记录当前五面体的五个顶点坐标信息,代码中通过 from_vs, from_is, from_uv 单独构建五面体底面,底面用于显示当前摄像头呈现的图像。
实现中设置了锥体 style 对象的 wf.geometry 属性,通过该属性可以为锥体添加模型的线框,增强模型的立体效果,并且通过 wf.color,wf.width 等参数调节线框的颜色,粗细等。
三、摄像头图像生成原理
1、透视投影
透视投影是为了获得接近真实三维物体的视觉效果而在二维的纸或者画布平面上绘图或者渲染的一种方法,它也称为透视图。 透视使得远的对象变小,近的对象变大,平行线会出现先交等更更接近人眼观察的视觉效果。
如上图所示,透视投影最终显示到屏幕上的内容只有截头锥体( View Frustum )部分的内容, 因此 Graph3dView 提供了 eye, center, up, far,near,fovy 和 aspect 参数来控制截头锥体的具体范围。具体的透视投影可以参考 HT for Web 的 3D 手册。
根据上图的描述,在本项目中可以在摄像头初始化之后,缓存当前 3d 场景 eyes 眼睛的位置,以及 center 中心的位置,之后将 3d 场景 eyes 眼睛和 center 中心设置成摄像头中心点的位置,然后在这个时刻获取当前 3d 场景的截图,该截图即为当前摄像头的监控图像,之后再将 3d 场景的 center 与 eyes 设置成开始时缓存的 eyes 与 center 位置,通过该方法即可实现 3d 场景中任意位置的快照,从而实现摄像头监控图像实时生成。
摄像头成像实现的效果如下:
经过测试之后,通过该方法进行图像的获取会导致页面有所卡顿,因为是获取当前 3d 场景的整体截图,由于当前3d场景是比较大的,所以 toDataURL 获取图像信息是非常慢的,因此我采取了离屏的方式来获取图像,具体方式如下:
1. 创建一个新的 3d 场景,将当前场景的宽度与高度都设置为 200px 的大小,并且当前 3d 场景的内容与主屏的场景是一样的,HT中通过 new ht.graph3d.Graph3dView(dataModel) 来新建场景,其中的 dataModel 为当前场景的所有图元,所以主屏与离屏的 3d 场景都共用同一个 dataModel,保证了场景的一致。
2. 将新创建的场景位置设置成屏幕看不到的地方,并且添加进 dom 中。
3. 将之前对主屏获取图像的操作变成对离屏获取图像的操作,此时离屏图像的大小相对之前主屏获取图像的大小小很多,并且离屏获取不需要保存原来的眼睛 eyes 的位置以及 center 中心的位置,因为我们没有改变主屏的 eyes 与 center 的位置, 所以也减少的切换带来的开销,大大提高了摄像头获取图像的速度。
1. 创建一个新的 3d 场景,将当前场景的宽度与高度都设置为 200px 的大小,并且当前 3d 场景的内容与主屏的场景是一样的,HT中通过 new ht.graph3d.Graph3dView(dataModel) 来新建场景,其中的 dataModel 为当前场景的所有图元,所以主屏与离屏的 3d 场景都共用同一个 dataModel,保证了场景的一致。
2. 将新创建的场景位置设置成屏幕看不到的地方,并且添加进 dom 中。
3. 将之前对主屏获取图像的操作变成对离屏获取图像的操作,此时离屏图像的大小相对之前主屏获取图像的大小小很多,并且离屏获取不需要保存原来的眼睛 eyes 的位置以及 center 中心的位置,因为我们没有改变主屏的 eyes 与 center 的位置, 所以也减少的切换带来的开销,大大提高了摄像头获取图像的速度。
在视角的实现中有一个 getCenter 方法是用于获取 3d 场景中点 A 绕着点 B 旋转 angle 角度之后得到的点 A 在 3d 场景中的位置,方法中采用了 HT 封装的 ht.Math 下面的方法这里应用到向量的部分知识,具体如下:
OA + OB = OC
方法分为以下几个步骤求解:
1. var mtrx = new ht.Math.Matrix4() 创建一个转换矩阵,通过 mtrx.makeRotationFromEuler(euler.set(r3[0], r3[1], r3[2])) 获取绕着 r3[0],r3[1],r3[2] 即 x 轴,y 轴,z 轴旋转的旋转矩阵。
2. 通过 new ht.Math.Vector3() 创建 v1,v2 两个向量。
3. v1.fromArray(pointB) 为建立一个从原点到 pointB 的一个向量。
4. v2.fromArray(pointA) 为建立一个从原点到 pointA 的一个向量。
5. v1.fromArray(pointB).sub(v2.fromArray(pointA)) 即向量 OB - OA 此时得到的向量为 AB,此时 v1 变为向量 AB。
6. v2.copy(v1) v2 向量拷贝 v1 向量,之后通过 v2.copy(v1).applyMatrix4(mtrx) 对 v2 向量应用旋转矩阵,变换之后即为 v1向量绕着 pointA 旋转之后的的向量 v2。
7. 此时通过 v2.sub(v1) 就获取了起始点为 pointB,终点为 pointB 旋转之后点构成的向量,该向量此时即为 v2。
8. 通过向量公式得到旋转之后的点为 [pointB[0] + v2.x, pointB[1] + v2.y, pointB[2] + v2.z]。
2. 通过 new ht.Math.Vector3() 创建 v1,v2 两个向量。
3. v1.fromArray(pointB) 为建立一个从原点到 pointB 的一个向量。
4. v2.fromArray(pointA) 为建立一个从原点到 pointA 的一个向量。
5. v1.fromArray(pointB).sub(v2.fromArray(pointA)) 即向量 OB - OA 此时得到的向量为 AB,此时 v1 变为向量 AB。
6. v2.copy(v1) v2 向量拷贝 v1 向量,之后通过 v2.copy(v1).applyMatrix4(mtrx) 对 v2 向量应用旋转矩阵,变换之后即为 v1向量绕着 pointA 旋转之后的的向量 v2。
7. 此时通过 v2.sub(v1) 就获取了起始点为 pointB,终点为 pointB 旋转之后点构成的向量,该向量此时即为 v2。
8. 通过向量公式得到旋转之后的点为 [pointB[0] + v2.x, pointB[1] + v2.y, pointB[2] + v2.z]。
项目中的 3D 场景例子其实是 Hightopo 最近贵州数博会,HT 上工业互联网展台的 VR 示例,大众对 VR/AR 的期待很高,但路还是得一步步走,即使融资了 23 亿美金的 Magic Leap 的第一款产品也只能是 Full of Shit,这话题以后再展开,这里就上段当时现场的视频照片:
2、2D 图像贴到 3D 模型
通过上一步的介绍我们可以获取当前摄像机位置的截屏图像,那么如何将当前图像贴到前面所构建的五面体底部呢?前面通过 from_vs, from_is 来构建底部的长方形,所以在 HT 中可以通过将五面体的 style 中 shape3d.from.image 属性设置成当前图像,其中 from_uv 数组用来定义贴图的位置,具体如下图:
以下为定义贴图位置 from_uv 的代码:
from_uv: [0, 0, 1, 0, 1, 1, 0, 1]
from_uv 就是定义贴图的位置数组,根据上图的解释,可以将 2d 图像贴到 3d 模型的 from 面。
3、控制面板
HT 中通过 new ht.widget.Panel() 来生成如下图的面板:
面板中每个摄像头都有一个模块来呈现当前监控图像,其实这个地方也是一个 canvas,该 canvas 与场景中锥体前面的监控图像是同一个 canvas,每一个摄像头都有一个自己的 canvas 用来保存当前摄像头的实时监控画面,这样就可以将该 canvas 贴到任何地方,将该 canvas 添加进面板的代码如下:
formPane.addRow([{ 2 element: camera.a('canvas') 3 }], 240, 240);
代码中将 canvas 节点存储在摄像头图元的 attr 属性下面,之后便可以通过 camera.a('canvas') 来获取当前摄像头的画面。
在面板中的每一个控制节点都是通过 formPane.addRow 来进行添加,具体可参考 HT for Web 的表单手册。之后通过 ht.widget.Panel 将表单面板 formPane 添加进 panel 面板中,具体可参考 HT for Web 的面板手册。
控制面板通过 addRow 来添加控制元素,实现的方式为添加摄像头绕着 y 轴进行旋转的控制,onValueChanged 在 slider 的数值改变的时候调用,此时通过 camera.r3() 获取当前摄像头的旋转参数, 由于是绕着 y 轴旋转所以 x 轴与 z 轴的角度是不变的,变的是 y 轴的旋转角度,所以通过 camera.r3([cameraR3[0], this.getValue(), cameraR3[2]]) 来调整摄像头的旋转角度以及通过 rangeNode.r3([cameraR3[0], this.getValue(), cameraR3[2]]) 来设置摄像头前方锥体的旋转角度,然后调用之前封装好的 getFrontImg 函数来获取此时旋转角度下面的实时图像信息。
项目中通过 Panel 面板的配置参数 titleBackground: rgba(230, 230, 230, 0.4) 即可将标题背景设置为具有透明度的背景,其它类似的 titleColor, titleHeight 等标题参数都可以配置,通过 separatorColor,separatorWidth 等分割参数可以设置内部面板之间分割线的颜色,宽度等。最后面板通过 panel.setPositionRelativeTo('rightTop') 将面板的位置设置成右上角,并且通过 document.body.appendChild(panel.getView()) 将面板最外层的 div 添加进页面中, panel.getView() 用来获取面板的最外层 dom 节点。
在控制面板中可以调整摄像头的方向,摄像头监控的辐射范围,摄像头前方锥体的长度等等,并且摄像头的图像是实时生成,以下为运行截图:
以下是本项目采用的 3D 场景结合 HT for Web 的 VR 技术实现的操作:
总结
3D 机房数据中心可视化系统在很大程度上保障了机房日常运行的监控状态,及时反馈机房内部的实况信息,这是可视化系统的一大优势。对接数据进行可视化展示,集中监管数据的变动,对应急突发状态能够起到捕捉,及时做出应对方案,很大程度上发挥了智能机房的最大价值,而可视化系统的用处的地方,还很广阔,在各行各业的监管可视化展示上,以及对自身产品的研发改造下,HT 积累了许多的解决方案,有兴趣的小伙伴可以看看:线框模式下的 3D 数据中心机房
2019 我们也更新了数百个工业互联网 2D/3D 可视化案例集,在这里你能发现许多新奇的实例,也能发掘出不一样的工业互联网:https://mp.weixin.qq.com/s/ZbhB6LO2kBRPrRIfHlKGQA
同时,你也可以查看更多案例及效果:https://www.hightopo.com/demos/index.html