基于HTML5WebGL和VR技术的3D机房数据中心可视化高清时代的到来加速了网络摄像机的发展和应用。在监控摄像头数量不断增加的同时,监控系统面临着严峻的现实问题:海量视频分散、孤立、视角不完整、位置不清晰等问题始终围绕在用户身边。因此,如何更直观、更清晰地管理摄像机和控制视频动态成为提升视频应用价值的重要课题。因此,本项目就是从解决这一现状的角度出发的。着眼于如何改进、管理和有效利用前端设备采集的海量信息服务于公共安全,特别是在技术融合的大趋势下,如何结合当前先进的视频融合、虚拟现实融合、3D动态等实现3D场景实时动态可视化监控的技术,对海量数据进行更有效的识别、分析和挖掘,以提供有效的信息服务公共应用,已成为视频监控平台可视化发展的趋势和方向。目前在监控行业,海康威视、大华等监控行业的龙头企业可以基于此方法规划公共场所、公园的摄像机布局,并通过海康威视、大华等摄像机品牌的摄像机参数进行系统调整.中间是摄像机模型的可视范围和监控方向,更方便人们直观的了解摄像机的监控区域和监控角度。以下是项目地址:基于HTML5的WebGL自定义3D摄像头监控模型效果预览整体场景-摄像头渲染局部场景-摄像头渲染代码生成摄像头模型及场景项目中使用的摄像头模型是通过3dMax建模生成的。建模工具可以导出obj和mtl文件,通过在HT中解析obj和mtl文件生成3D场景中的相机模型。项目中的场景由HT的3d编辑器搭建而成。场景中有的模型是用HT建模的,有的是用3dMax建模的,然后导入到HT中。场景中地面上的白光是由HT的3d编辑器制作的。地面纹理呈现的效果。圆锥造型3D模型由最基本的三角形面组成。例如,一个长方形可以由2个三角形组成,一个立方体可以由6个面或12个三角形组成。以此类推,更复杂的模型可以由许多小三角形组合而成。因此,3D模型的定义就是对构成模型的所有三角形的描述,每个三角形由三个顶点vertex组成。顶点构造三角形面的前面。在HT中,ht.Default.setShape3dModel(name,model)函数可用于注册自定义3D模型。摄像机前面生成的锥体就是通过这种方法生成的。圆锥体可以看作由5个顶点和6个三角形组成。具体图如下:ht.Default.setShape3dModel(name,model)1.name为模型名称。如果名称与预定义的名称相同,它将替换预定义的名称。定义model2.model是一个JSON类型的对象,其中_vs_代表顶点坐标数组,_is_代表索引数组,_uv_代表纹理坐标数组。如果要单独定义一个面,可以通过_bottom_vs、bottom_is、bottom_uv、top_vs、top_is、top_uv_等,然后可以通过_shape3d.top.*、shape3d.bottom.*单独控制某个面_等下面是我定义模型的代码//camera是当前cameraprimitive//fovy是camera张角一半的tan值varsetRangeModel=function(camera,fovy){varfovyVal=0.5*fovy;varpointArr=[0,0,0,-fovyVal,fovyVal,0.5,fovyVal,fovyVal,0.5,fovyVal,-fovyVal,0.5,-fovyVal,-fovyVal,0.5];ht.Default.setShape3dModel(camera.getTag(),[{vs:pointArr,is:[2,1,0,4,1,0,4,3,0,3,2,0],from_vs:pointArr。slice(3,15),from_is:[3,1,0,3,2,1],from_uv:[0,0,1,0,1,1,0,1]}\]);}我用当前相机的标签值作为模型名称。tag标签在HT中用于唯一标识一个原语,用户可以自定义标签的值。使用pointArr记录当前五面体五个顶点的坐标信息。代码中使用from_vs、from_is、from_uv分别构造五面体的底面,用于显示当前相机呈现的图像。圆锥样式对象的wf.geometry属性在代码中设置。通过该属性可以将模型的线框添加到圆锥体中,增强模型的三维效果,线框的颜色可以通过wf.color、wf.width等参数调整粗细等相关模型样式属性的设置代码如下:rangeNode.s({'shape3d':cameraName,//相机模型名称'shape3d.color':'rgba(52,148,252,0.3)',//锥体模型颜色'shape3d.reverse.flip':true,//锥体模型的反面是否显示正面的内容'shape3d.light':false,//锥体模型是否受光影响'shape3d.transparent':true,//Cone体积模型是否透明'3d.movable':false,//cone模型是否可移动'wf.geometry':true//是否显示cone模型的线框});相机图像生成透视投影原理透视投影是在二维纸或画布平面上绘制或渲染接近真实三维物体视觉效果的一种方法,也称为透视绘制。透视使远的物体变小,近的物体变大,平行线会先相交,更接近人眼观察到的视觉效果。如上图所示,透视投影最终在屏幕上只显示截锥体(ViewFrustum)的内容,所以Graph3dView提供了eye、center、up、far、near、fovy和aspect参数来控制截锥体具体体的范围。具体透视投影请参考HTforWeb的3D手册。根据上图的描述,在本项目中,初始化相机后,可以缓存眼睛的位置和当前3d场景的中心,然后可以设置眼睛和3d场景的中心到摄像头中心点的位置,然后在获取当前3D场景此时的截图,也就是当前摄像头的监控画面,然后将3D场景的中心和眼睛设置到positions一开始缓存的眼睛和中心。该方法可以实现任意位置的3D场景抓拍,从而实现摄像机监控图像的实时生成。相关伪代码如下:functiongetFrontImg(camera,rangeNode){varoldEye=g3d.getEye();varoldCenter=g3d.getCenter();varoldFovy=g3d.getFovy();g3d.setEye(相机位置);g3d。setCenter(相机方向);g3d.setFovy(摄像机角度);g3d.setAspect(相机纵横比);g3d.validateImp();g3d.toDataURL();g3d.setEye(老眼);;g3d.setCenter(oldCenter);g3d.setFovy(oldFovy);g3d.setAspect(未定义);g3d.validateImp();}经过测试,通过该方法获取图片会导致页面卡顿,因为是获取当前3d场景的整体截图,因为当前3d场景比较大,所以toDataURL获取图片信息很慢,所以我采取了离屏的方式来获取图片,具体方法如下:1.新建一个3d场景,并将当前场景的宽高都设置为200px,并且当前场景的内容3d场景与主屏幕相同。HT中使用newht.graph3d.Graph3dView(dataModel)创建新场景,dataModel为当前场景的所有图元,所以主屏和离屏3D场景共享同一个dataModel,保证了场景的一致性。2.将新建的场景的位置设置到屏幕上看不到的地方,并添加到dom中。3、将之前在主屏上获取图像的操作改为在屏外获取图像的操作。此时离屏图像的尺寸比之前在主屏上获取的图像要小很多,离屏获取不需要保存原来的眼睛位置和中心位置,因为我们没有改变眼睛的位置和主屏幕的中心位置,因此切换带来的开销也减少了,相机获取图像的速度也大大提高了。下面是该方法实现的代码:functiongetFrontImg(camera,rangeNode){//捕捉当前图像时,隐藏相机所属的五面体rangeNode.s('shape3d.from.visible',false);rangeNode.s('shape3d.visible',false);rangeNode.s('wf.geometry',false);varcameraP3=camera.p3();varcameraR3=camera.r3();varcameraS3=camera.s3();varupdateScreen=function(){demoUtil.Canvas2dRender(相机,outScreenG3d.getCanvas());rangeNode.s({'shape3d.from.image':camera.a('canvas')});rangeNode.s('shape3d.from.visible',true);rangeNode.s('shape3d.visible',true);rangeNode.s('wf.geometry',true);};//当前圆锥起始位置varrealP3=\[cameraP3\[0\],cameraP3\[1\]+cameraS3\[1\]/2,cameraP3\[2\]+cameraS3\[2\]/2\];//开始围绕相机当前眼睛位置旋转位置以获得正确的眼睛位置varrealEye=demoUtil.getCenter(cameraP3,realP3,cameraR3);outScreenG3d.setEye(realEye);outScreenG3d.setCenter(demoUtil.getCenter(realEye,\[realEye\[0\],realEye\[1\],realEye\[2\]+5\],cameraR3));outScreenG3d.setFovy(camera.a('fovy'));outScreenG3d.validate();更新屏幕();}上述代码中有一个getCenter方法,用于获取A点在3d场景中绕B点旋转一个角度后在3d场景中的位置。该方法使用HT封装的ht.Math方法如下,代码如下://pointA为围绕pointB的旋转点//pointB为需要旋转的点//r3为旋转角度数组[xAngle,yAngle,zAngle]分别绕x,y,z轴旋转vargetCenter=function(pointA,pointB,r3){varmtrx=newht.Math.Matrix4();vareuler=newht.Math.Euler();varv1=newht.Math.Vector3();varv2=newht.Math.Vector3();mtrx.makeRotationFromEuler(euler.set(r3[0],r3[1],r3\[2]));v1.fromArray(pointB).sub(v2.fromArray(pointA));v2.copy(v1).applyMatrix4(mtrx);v2.sub(v1);返回[pointB[0]+v2.x,pointB[1]+v2.y,pointB[2]+v2.z];};这里应用到向量的部分知识如下:OA+OB=OC方法分为以下几步来解决:1.varmtrx=newht.Math.Matrix4()创建一个变换矩阵,通过mtrx.makeRotationFromEuler(euler.set(r3[0],r3[1],r3[2]))得到取围绕r3[0]、r3[1]、r3[2]旋转的旋转矩阵,即x轴、y轴、z轴。2.通过newht.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场景实例,其实就是海拓近期贵州数博会的VR实例,HT上的工业互联网展台。大众对VR/AR寄予厚望,但道路仍需一步步走。Leap的第一个产品只能是FullofShit。这个话题稍后会展开。下面是上一节场景的视频照片:2D图像贴在3D模型上。通过上一步的介绍,我们可以得到当前相机位置的截图。那么如何将当前图像粘贴到之前构建的五面体的底部呢?最下面的矩形是由from_vs、from_is构造的,所以在HT中可以将五面体样式中的shape3d.from.image属性设置为当前图像,其中from_uv数组用于定义贴图的位置,如下图所示:定义纹理位置的代码from_uv:1from_uv:[0,0,1,0,1,1,0,1]from_uv是定义纹理的位置数组。根据上面的解释,您可以将2d图像从face粘贴到3d模型。在控制面板HT中,使用newht.widget.Panel()生成如下图所示的面板:面板中的每个摄像头都有一个模块来呈现当前的监控画面。其实这个地方也是一个canvas,就是场景中锥体前面的监控图像是同一个canvas,每个摄像头都有自己的canvas来保存当前摄像头的实时监控图像,这样画布可以粘贴到任何地方,将画布添加到面板的代码如下:1formPane.addRow([{2element:camera.a('canvas')3}],240,240);代码中将canvas节点存放在camera图元的attr属性下,然后可以通过camera.a('canvas')获取当前的camera图像。面板中的各个控件节点都是通过formPane.addRow添加的,具体可以参考HTforWeb的表单手册。然后通过ht.widget.Panel将表单面板formPane添加到面板面板中。详见HTforWeb面板手册。部分控制代码如下:formPane.addRow(['rotateY',{slider:{min:-Math.PI,max:Math.PI,value:r3[1],onValueChanged:function(){varcameraR3=camera.r3();camera.r3([cameraR3[0],this.getValue(),cameraR3[2]]);rangeNode.r3([cameraR3[0],this.getValue(),cameraR3[2]]);getFrontImg(相机,rangeNode);}}}],[0.1,0.15]);控制面板使用addRow添加控件元素,上面的代码是添加相机绕y轴旋转的控制,当滑块的值发生变化时调用onValueChanged此时,当前相机的旋转参数为通过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等进行配置。split参数可以设置内面板之间的分割线的颜色、宽度等。最后panel通过panel.setPositionRelativeTo('rightTop')设置panel的位置为右上角,通过document.body.appendChild(panel.getView())将panel的最外层div添加到页面中,panel.getView()用于获取面板最外层的dom节点。具体初始化面板代码如下:functioninitPanel(){varpanel=newht.widget.Panel();varconfig={title:"相机控制面板",titleBackground:'rgba(230,230,230,0.4)',titleColor:'rgb(0,0,0)',titleHeight:30,separatorColor:'rgb(67,175,241)',separatorWidth:1,exclusive:true,items:[]};cameraArr.forEach(function(data,num){varcamera=data['camera'];varrangeNode=data['rangeNode'];varformPane=newht.widget.FormPane();initFormPane(formPane,camera,rangeNode);config.items.push({title:"Camera"+(num+1),titleBackground:'rgba(230,230,230,0.4)',titleColor:'rgb(0,0,0)',titleHeight:30,separatorColor:'rgb(67,175,241)',separatorWidth:1,content:formPane,flowLayout:true,contentHeight:400,width:250,扩展:num===0});});面板.setConfig(配置);panel.setPositionRelativeTo('rightTop');document.body.appendChild(panel.getView());window.addEventListener("resize",function(){panel.invalidate();});}在控制面板中,可以调整摄像头的方向、摄像头监控的辐射范围、摄像头前方锥体的长度等,实时生成摄像头的图像,下面是运行截图:下面是本项目使用的3D场景结合HTforWeb的VR技术的运行:
