接到了一个需求,一个物联网的电力保障系统实时监控。实施用到了Web 3D技术,以前用过一段时间的Three.js,因为太耗资源,没怎么铺开,现在硬件条件好了,就拿出来试试。
环境:blender-2.93.6-windows-x64+Three.js?0.91.0+Echart2.0
我习惯不用最新版本的软件做开发,一方面新版的稳定性不如旧版,主要是基于js的前端开发,新版大部分对IE都不友好,国内的需求现状就是,问就是随便,验收就拿IE说事,所以多一事不如少一事,就用旧的成熟版本对付了。
首先是3D建模,打开blender,一通XJB操作,做出模型长这样:
然后把工业电区和农用电区分开,按照硬件的分布编好号(部分检测设备在一层并且比较近,就编在一起,报警时候单独处置)。
然后建一个html,按照监控屏的分辨率1920*1080设置一个画布、背景?什么的,建一个对象叫做three,作为我们绘制模型的容器。
#three{width:1920px;height:1080px;position:absolute;z-index:1}
<div id="three"></div>
建一个js文件,加载模型,按照不同的供电方式设置材质:
function init() {
?? ?var container = document.getElementById("three");
?? ?camera = new THREE.PerspectiveCamera( 45, sceneWidth / sceneHeight, 1, 4000 ); ?? ?//camera.position.set(900, 400, 900); ?? ?camera.position.set(300, 600, -1400); ?? ?scene = new THREE.Scene(); ?? ?//scene.rotation.set(0,Math.PI,0); ?? ?scene.rotation.y = 0.5; ?? ? ?? ?scene.background = new THREE.Color('#394855'); ?? ?scene.fog = new THREE.Fog('#394855', 2000, 2500 ); ?? ? ?? ?var hemiLight = new THREE.HemisphereLight( 0xFFFFFF, 0x444444); ?? ?hemiLight.position.set( 0, 500, -300 ); ?? ?scene.add( hemiLight );
?? ?var dirLight = new THREE.DirectionalLight('#FFFFFF'); ?? ?dirLight.position.set( 100, 300, 200 ); ?? ?dirLight.castShadow = true; ?? ?dirLight.shadow.camera.top = 1000; ?? ?dirLight.shadow.camera.bottom = - 1000; ?? ?dirLight.shadow.camera.left = - 1000; ?? ?dirLight.shadow.camera.right = 1000;?? ? ?? ?dirLight.shadow.camera.far=1000; ?? ?dirLight.shadow.mapSize.width = 1024; ?? ?dirLight.shadow.mapSize.height = 1024; ?? ?scene.add( dirLight );
?? ?//scene.add(new THREE.CameraHelper( dirLight.shadow.camera ) );
?? ?//地板 ?? ?//var textureLoader=new THREE.TextureLoader(); ?? ?//var texture = textureLoader.load("../images/ground.png"); ?? ?//var mesh = new THREE.Mesh( new THREE.PlaneGeometry( 2000, 2000 ), new THREE.MeshPhongMaterial( {map:texture,color: '#2B3745', depthWrite: false } ) ); ?? ?var mesh = new THREE.Mesh( new THREE.PlaneGeometry( 8000, 8000 ), new THREE.MeshBasicMaterial( {color: '#394855', depthWrite:false } ) ); ?? ?mesh.rotation.set(- Math.PI / 2,0,0); ?? ?mesh.receiveShadow = true; ?? ?scene.add( mesh );
?? ?var grid = new THREE.GridHelper(8000, 200, '#556B80','#556B80'); ?? ?grid.material.opacity = 0.2; ?? ?grid.material.transparent = true; ?? ?//grid.rotation.set(0,100,0); ?? ?scene.add(grid); ?? ? ?? ?var loader = new THREE.FBXLoader();?? ??? ??? ??? ? ?? ?loader.load('/object/factory.fbx', function ( object ) {?? ? ?? ??? ?object.traverse( function ( child ) { ?? ??? ??? ?if ( child.isMesh ) {?? ??? ??? ??? ? ?? ??? ??? ??? ?var material =new THREE.MeshPhongMaterial( {color: '#2F3C46' ,opacity:1,transparent:true}) ?? ??? ??? ??? ?var material2 =new THREE.MeshStandardMaterial({color: '#394855' ,opacity:0.8,transparent:true})?? ??? ??? ??? ? ?? ??? ??? ??? ?var material1=new THREE.LineBasicMaterial( { ?? ??? ??? ??? ??? ?color: 0xffffff, ?? ??? ??? ??? ??? ?linewidth: 1, ?? ??? ??? ??? ??? ?linecap: 'round', ?? ??? ??? ??? ??? ?linejoin: ?'round' ?? ??? ??? ??? ?} );?? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ? ?? ??? ??? ??? ? ?? ??? ??? ??? ?child.material =material;?? ??? ??? ??? ? ?? ??? ??? ??? ?child.castShadow = true; ?? ??? ??? ??? ?child.receiveShadow = true; ?? ??? ??? ??? ? ?? ??? ??? ??? ?if(child.name=="ground") //地板 ?? ??? ??? ??? ?{ ?? ??? ??? ??? ??? ?child.material=material2;?? ??? ??? ??? ??? ??? ??? ??? ??? ??? ? ?? ??? ??? ??? ?}?? ??? ??? ??? ??? ??? ??? ??? ? ?? ??? ??? ??? ?else if(child.name=="wall") //外墙 ?? ??? ??? ??? ?{ ?? ??? ??? ??? ??? ?var textureLoader = new THREE.TextureLoader();?? ? ?? ??? ??? ??? ??? ?var alphaMap = textureLoader.load( 'images/alphaMap.png' ); ?? ??? ??? ??? ??? ?var wallMaterial=new THREE.MeshBasicMaterial({ ?? ??? ??? ??? ??? ??? ?color: 0x005E9B, ?? ??? ??? ??? ??? ??? ?side:THREE.DoubleSide, ?? ??? ??? ??? ??? ??? ?transparent: true, ?? ??? ??? ??? ??? ??? ?depthWrite: false, ?? ??? ??? ??? ??? ??? ?alphaMap: alphaMap, ?? ??? ??? ??? ??? ?}); ?? ??? ??? ??? ??? ?child.material=wallMaterial; ?? ??? ??? ??? ??? ?child.receiveShadow = false; ?? ??? ??? ??? ??? ?child.castShadow = false;?? ??? ??? ??? ??? ??? ??? ??? ??? ? ?? ??? ??? ??? ?}?? ??? ??? ??? ??? ??? ? ?? ??? ??? ??? ?else if(child.name.indexOf("HF")==0) //民用电?? ? ?? ??? ??? ??? ?{?? ??? ??? ??? ??? ? ?? ??? ??? ??? ??? ?child.material.color.set('#00538A'); ?? ??? ??? ??? ??? ?if(child.name.indexOf("HF18")==0 )? ?? ??? ??? ??? ??? ?{?? ??? ??? ??? ??? ??? ? ?? ??? ??? ??? ??? ??? ?var fined=false; ?? ??? ??? ??? ??? ??? ?for(var i=0;i<selectNodes.length;i++) ?? ??? ??? ??? ??? ??? ?{ ?? ??? ??? ??? ??? ??? ??? ?if(selectNodes[i].name==child.name) ?? ??? ??? ??? ??? ??? ??? ?{ ?? ??? ??? ??? ??? ??? ??? ??? ?fined=true; ?? ??? ??? ??? ??? ??? ??? ??? ?break;?? ? ?? ??? ??? ??? ??? ??? ??? ?} ?? ??? ??? ??? ??? ??? ?} ?? ??? ??? ??? ??? ??? ?//if(!fined) selectNodes.push(child); ?? ??? ??? ??? ??? ?} ?? ??? ??? ??? ?}?? ??? ??? ??? ??? ??? ??? ? ?? ??? ??? ??? ?else if(child.name.indexOf("TF")==0) //工业电 ?? ??? ??? ??? ?{?? ??? ??? ??? ??? ? ?? ??? ??? ??? ??? ?child.material.color.set('#917000'); ?? ??? ??? ??? ??? ?//var text=createTitle(child.name); ?? ??? ??? ??? ??? ?//text.position.set(child.position.x,child.position.y+35,child.position.z); ?? ??? ??? ??? ??? ?//scene.add(text); ?? ??? ??? ??? ?}?? ??? ??? ??? ??? ??? ??? ??? ? ?? ??? ??? ??? ?else if(child.name.indexOf("F")==0) //冷凝罐?? ??? ??? ??? ? ?? ??? ??? ??? ?{?? ??? ??? ??? ??? ? ?? ??? ??? ??? ??? ?child.material.color.set('#0099FF'); ?? ??? ??? ??? ??? ?//var text=createTitle(child.name); ?? ??? ??? ??? ??? ?//text.position.set(child.position.x,child.position.y+25,child.position.z); ?? ??? ??? ??? ??? ?//scene.add(text); ?? ??? ??? ??? ?}?? ??? ??? ??? ??? ??? ??? ??? ? ?? ??? ??? ??? ?else if(child.name.indexOf("NO")==0) //氮气 ?? ??? ??? ??? ?{ ?? ??? ??? ??? ??? ?child.material.color.set('#FF0000'); ?? ??? ??? ??? ??? ?//var text=createTitle(child.name); ?? ??? ??? ??? ??? ?//text.position.set(child.position.x,child.position.y+25,child.position.z); ?? ??? ??? ??? ??? ?//scene.add(text); ?? ??? ??? ??? ?}?? ??? ??? ??? ??? ??? ??? ??? ? ?? ??? ??? ??? ?else if(child.name.indexOf("NH")==0) //氨气 ?? ??? ??? ??? ?{?? ??? ??? ??? ??? ? ?? ??? ??? ??? ??? ?child.material.color.set('#0000FF'); ?? ??? ??? ??? ??? ?//var text=createTitle(child.name); ?? ??? ??? ??? ??? ?//text.position.set(child.position.x,child.position.y+25,child.position.z); ?? ??? ??? ??? ??? ?//scene.add(text); ?? ??? ??? ??? ?}?? ??? ??? ??? ??? ??? ??? ??? ? ?? ??? ??? ??? ?else if(child.name.indexOf("YH")==0) //液化气 ?? ??? ??? ??? ?{?? ??? ??? ??? ??? ? ?? ??? ??? ??? ??? ?child.material.color.set('#00FF00'); ?? ??? ??? ??? ??? ?//var text=createTitle(child.name); ?? ??? ??? ??? ??? ?//text.position.set(child.position.x,child.position.y+25,child.position.z);?? ??? ??? ??? ??? ? ?? ??? ??? ??? ??? ?//scene.add(text); ?? ??? ??? ??? ?} ?? ??? ??? ??? ? ?? ??? ??? ??? ?//console.log(child.name); ?? ??? ??? ??? ? ?? ??? ??? ?} ?? ??? ?} );?? ??? ? ?? ??? ?scene.add( object ); ?? ?} );?? ??? ??? ??? ??? ?
?? ?renderer = new THREE.WebGLRenderer( { antialias: true,alpha:true } ); ?? ?//renderer.setPixelRatio( window.devicePixelRatio ); ?? ?renderer.setSize( sceneWidth, sceneHeight ); ?? ?renderer.shadowMap.enabled = true; ?? ?renderer.shadowMap.type = THREE.PCFSoftShadowMap; ?? ?container.appendChild( renderer.domElement );
?? ?var controls = new THREE.OrbitControls(camera, renderer.domElement); ?? ?controls.target.set( 0, 0, 0 ); ?? ?controls.update();
?? ?window.addEventListener( 'resize', onWindowResize ); }
一通操作以后,模型加载进来,页面长这样:
然后上面再加一个效果层,就是一个摄像机一样的东东,禁止它的鼠标响应。把要显示的东西都加上,页面长这样:
数据的刷新分两部分,常规的数据库信息(人员,统计指标什么的)就JQuery加载进来。监控的实时信息,引入MQTT.js和代理服务做个长连接,实时读取显示就行了,数据异常,就弹出个报警框然后发声音什么的。
|