序:前段時間公司一次研討會上,一市場部同事展現了同行業其他公司的3D機房,我司領導覺得這個可以研究研究,為了節約成本,我們在網上大量檢索,最後找到一位前輩的博文【TWaver的技術博客】,在那篇博文的評論區終於找到了那位前輩的源碼,可惜下載後發現是壓縮過的.min.js文件。經過各種研究發現,那是人 ...
序:前段時間公司一次研討會上,一市場部同事展現了同行業其他公司的3D機房,我司領導覺得這個可以研究研究,為了節約成本,我們在網上大量檢索,最後找到一位前輩的博文【TWaver的技術博客】,在那篇博文的評論區終於找到了那位前輩的源碼,可惜下載後發現是壓縮過的.min.js文件。經過各種研究發現,那是人家公司自己賣錢的庫,不能完全共用,所以我司決定,派本人研究一下web3D的技術,於是乎便有了下麵的技術分享。
一、本著開源的思想,使用three.js框架,封裝常用的模型庫。先學著那位前輩的樣子,使用ThreeJS畫出類似的效果圖,只是稍微有點醜,看官莫怪。
特此申明:裡面的大部分模型貼圖都是使用的上述前輩的圖片
二、源碼
首先是各種庫的引入
<script src="../webglJS/jquery-2.2.2.js"></script>
<script src="../webglJS/three.min.js"></script>
<script src="../webglJS/stats.min.js"></script>
<script src="../webglJS/OrbitControls.js"></script>
<script src="../webglJS/ThreeBSP.js"></script>
<script src="../webglJS/Tween.js"></script>
上述幾個庫都是網上可以下載到的。
然後是封裝自己的模型庫,這是初始的模型庫,較為簡陋了 ,牛逼的庫都是不斷的迭代個幾百次的,不到的地方還望指正,互相學習共同進步
--這裡封裝一下 就叫做msj3D.js--
1 /* 2 作者:yeyunfei 3 創建時間:2016-11-28 4 版本:V16.11.01.bug 5 功能描述:用戶創建web版組態 6 7 使用方式 8 var msjstation=new msj3D(); 9 msjstation.initmsj3D('divid',{...},[datajson]); 10 msjstation.start(); 11 */ 12 function msj3D() { } 13 var msj3DObj = null; 14 msj3D.prototype.start = function () { 15 //此處用於判斷瀏覽器 16 17 //開始 18 var _this = this; 19 msj3DObj = _this; 20 _this.initThree(_this.fId); 21 _this.initCamera(); 22 _this.initScene(); 23 _this.initHelpGrid(); 24 _this.initLight(); 25 _this.addTestObj(); 26 //添加3D對象 27 $.each(_this.objList, function (index,_obj) { 28 _this.InitAddObject(_obj); 29 }); 30 _this.initMouseCtrl(); 31 //創建按鈕 32 _this.addBtns(_this.btns); 33 34 _this.animation(); 35 36 } 37 /* 38 方法:初始化 39 fid 畫布所屬div的Id 40 option:參數 { 41 antialias:true,//抗鋸齒效果為設置有效 42 clearCoolr:0xFFFFFF, 43 showHelpGrid:false,//是否顯示網格線 44 } 45 */ 46 msj3D.prototype.initmsj3D = function (_fId, _option,_datajson) { 47 //參數處理 48 this.option = new Object(); 49 this.option.antialias = _option.antialias || true;//刷新色 50 this.option.clearCoolr = _option.clearCoolr || 0x1b7ace;//刷新色 51 this.option.showHelpGrid = _option.showHelpGrid || false;//刷新色 52 //對象 53 this.fId = _fId; 54 this.width = $("#" + _fId).width(); 55 this.height = $("#" + _fId).height(); 56 this.renderer = null;//渲染器 57 this.camera = null;//攝像機 58 this.scene = null;//場景 59 this.SELECTED=null; 60 this.objects = []; 61 this.mouseClick = new THREE.Vector2(); 62 this.raycaster = new THREE.Raycaster(); 63 this.controls = null;//滑鼠控制器 64 this.objList = _datajson.objects;//對象列表 65 this.eventList = _datajson.events;//事件對象列表 66 this.btns = _datajson.btns;//按鈕列表 67 var _this = this; 68 } 69 //初始化渲染器 70 msj3D.prototype.initThree = function () { 71 var _this = this; 72 _this.renderer = new THREE.WebGLRenderer({ alpha: true, antialias: _this.option.antialias }); 73 _this.renderer.setSize(_this.width, _this.height); 74 $("#" + _this.fId).append(_this.renderer.domElement); 75 _this.renderer.setClearColor(_this.option.clearCoolr, 1.0); 76 _this.renderer.shadowMap.enabled = true;// 77 _this.renderer.shadowMapSoft = true; 78 //事件 79 _this.renderer.domElement.addEventListener('mousedown', _this.onDocumentMouseDown, false); 80 _this.renderer.domElement.addEventListener('mousemove', _this.onDocumentMouseMove, false); 81 } 82 //初始化攝像機 83 msj3D.prototype.initCamera = function () { 84 var _this = this; 85 _this.camera = new THREE.PerspectiveCamera(45, _this.width / _this.height, 1, 100000); 86 _this.camera.name = 'mainCamera'; 87 _this.camera.position.x =0; 88 _this.camera.position.y =1000; 89 _this.camera.position.z =-1800; 90 _this.camera.up.x = 0; 91 _this.camera.up.y =1; 92 _this.camera.up.z =0; 93 _this.camera.lookAt({ x: 0, y: 0, z: 0 }); 94 _this.objects.push(_this.camera); 95 } 96 //創建場景 97 msj3D.prototype.initScene=function() { 98 var _this = this; 99 _this.scene = new THREE.Scene(); 100 } 101 //添加對象 102 msj3D.prototype.addObject = function (_obj) { 103 var _this = msj3DObj; 104 _this.objects.push(_obj); 105 _this.scene.add(_obj); 106 } 107 //創建網格線 108 msj3D.prototype.initHelpGrid = function () { 109 var _this = this; 110 if (_this.option.showHelpGrid) { 111 var helpGrid = new THREE.GridHelper(1000, 50); 112 _this.scene.add(helpGrid); 113 } 114 } 115 //燈光佈置 116 msj3D.prototype.initLight = function () { 117 /* 118 AmbientLight: 環境光,基礎光源,它的顏色會被載入到整個場景和所有對象的當前顏色上。 119 PointLight:點光源,朝著所有方向都發射光線 120 SpotLight :聚光燈光源:類型臺燈,天花板上的吊燈,手電筒筒等 121 DirectionalLight:方向光,又稱無限光,從這個發出的光源可以看做是平行光. 122 */ 123 var _this = this; 124 var light = new THREE.AmbientLight(0xcccccc); 125 light.position.set(0, 0,0); 126 _this.scene.add(light); 127 var light2 = new THREE.PointLight(0x555555); 128 light2.shadow.camera.near =1; 129 light2.shadow.camera.far = 5000; 130 light2.position.set(0, 350, 0); 131 light2.castShadow = true;//表示這個光是可以產生陰影的 132 _this.scene.add(light2); 133 134 } 135 //創建滑鼠控制器 136 msj3D.prototype.initMouseCtrl=function() { 137 var _this = this; 138 _this.controls = new THREE.OrbitControls(_this.camera); 139 _this.controls.addEventListener('change', _this.updateControls); 140 } 141 //控制器回調 142 msj3D.prototype.updateControls = function () { 143 144 //controls.update(); 145 } 146 //迴圈渲染界面 147 msj3D.prototype.animation = function () { 148 var _this = msj3DObj; 149 if (TWEEN != null && typeof (TWEEN) != 'undefined') { 150 TWEEN.update(); 151 } 152 requestAnimationFrame(_this.animation); 153 _this.renderer.render(_this.scene, _this.camera); 154 } 155 156 /* 157 添加對象 158 */ 159 msj3D.prototype.InitAddObject = function (_obj) { 160 var _this = this; 161 if (_obj.show == null || typeof (_obj.show) == 'undefined' || _obj.show) { 162 var _tempObj = null; 163 switch (_obj.objType) { 164 case 'cube': 165 _tempObj = _this.createCube(_this, _obj); 166 _this.addObject(_tempObj); 167 break; 168 case 'floor': 169 _tempObj = _this.CreateFloor(_obj) 170 _this.addObject(_tempObj); 171 break; 172 case 'wall': 173 _this.CreateWall(_this,_obj); 174 break; 175 case 'plane': 176 _tempObj = _this.createPlaneGeometry(_this, _obj); 177 _this.addObject(_tempObj); 178 break; 179 case 'glasses': 180 _this.createGlasses(_this, _obj); 181 break; 182 case 'emptyCabinet': 183 _tempObj = _this.createEmptyCabinet(_this, _obj); 184 _this.addObject(_tempObj); 185 break; 186 case 'cloneObj': 187 _tempObj = _this.commonFunc.cloneObj(_obj.copyfrom, _obj); 188 _this.addObject(_tempObj); 189 break; 190 } 191 } 192 } 193 194 //創建地板 195 msj3D.prototype.CreateFloor = function (_obj) { 196 var _this = this; 197 var _cube = _this.createCube(_this, _obj); 198 return _cube; 199 } 200 //創建牆體 201 msj3D.prototype.CreateWall = function (_this, _obj) { 202 if (_this == null) { 203 _this = this; 204 } 205 var _commonThick = _obj.thick || 40;//牆體厚度 206 var _commonLength = _obj.length || 100;//牆體厚度 207 var _commonHeight = _obj.height || 300;//強體高度 208 var _commonSkin = _obj.style.skinColor || 0x98750f; 209 //建立牆面 210 $.each(_obj.wallData, function (index, wallobj) { 211 var wallLength = _commonLength; 212 var wallWidth = wallobj.thick||_commonThick; 213 var positionX = ((wallobj.startDot.x||0) + (wallobj.endDot.x||0)) / 2; 214 var positionY = ((wallobj.startDot.y || 0) + (wallobj.endDot.y || 0)) / 2; 215 var positionZ = ((wallobj.startDot.z || 0) + (wallobj.endDot.z || 0)) / 2; 216 //z相同 表示x方向為長度 217 if (wallobj.startDot.z == wallobj.endDot.z) { 218 wallLength = Math.abs(wallobj.startDot.x - wallobj.endDot.x); 219 wallWidth = wallobj.thick || _commonThick; 220 } else if (wallobj.startDot.x == wallobj.endDot.x) { 221 wallLength = wallobj.thick || _commonThick; 222 wallWidth = Math.abs(wallobj.startDot.z - wallobj.endDot.z); 223 } 224 var cubeobj = { 225 length: wallLength, 226 width: wallWidth, 227 height: wallobj.height || _commonHeight, 228 rotation: wallobj.rotation, 229 x: positionX, 230 y: positionY, 231 z: positionZ, 232 uuid: wallobj.uuid, 233 name:wallobj.name, 234 style: { 235 skinColor: _commonSkin, 236 skin:wallobj.skin 237 } 238 } 239 var _cube = _this.createCube(_this, cubeobj); 240 if (_this.commonFunc.hasObj(wallobj.childrens) && wallobj.childrens.length > 0) { 241 $.each(wallobj.childrens, function (index, walchildobj) { 242 var _newobj = null; 243 _newobj = _this.CreateHole(_this, walchildobj); 244 _cube = _this.mergeModel(_this, walchildobj.op, _cube, _newobj); 245 }); 246 } 247 _this.addObject(_cube); 248 }); 249 } 250 //挖洞 251 msj3D.prototype.CreateHole = function (_this, _obj) { 252 if (_this == null) { 253 _this = this; 254 } 255 var _commonThick = 40;//牆體厚度 256 var _commonLength = 100;//牆體厚度 257 var _commonHeight = 300;//強體高度 258 var _commonSkin = 0x98750f; 259 //建立牆面 260 var wallLength = _commonLength; 261 var wallWidth = _obj.thick || _commonThick; 262 var positionX = ((_obj.startDot.x || 0) + (_obj.endDot.x || 0)) / 2; 263 var positionY = ((_obj.startDot.y || 0) + (_obj.endDot.y || 0)) / 2; 264 var positionZ = ((_obj.startDot.z || 0) + (_obj.endDot.z || 0)) / 2; 265 //z相同 表示x方向為長度 266 if (_obj.startDot.z == _obj.endDot.z) { 267 wallLength = Math.abs(_obj.startDot.x - _obj.endDot.x); 268 wallWidth = _obj.thick || _commonThick; 269 } else if (_obj.startDot.x == _obj.endDot.x) { 270 wallLength = _obj.thick || _commonThick; 271 wallWidth = Math.abs(_obj.startDot.z - _obj.endDot.z); 272 } 273 var cubeobj = { 274 length: wallLength, 275 width: wallWidth, 276 height: _obj.height || _commonHeight, 277 rotation: _obj.rotation, 278 x: positionX, 279 uuid: _obj.uuid, 280 name: _obj.name, 281 y: positionY, 282 z: positionZ, 283 style: { 284 skinColor: _commonSkin, 285 skin: _obj.skin 286 } 287 } 288 var _cube = _this.createCube(_this, cubeobj); 289 return _cube; 290 } 291 //模型合併 使用ThreeBSP插件mergeOP計算方式 -表示減去 +表示加上 292 msj3D.prototype.mergeModel = function (_this, mergeOP, _fobj, _sobj) { 293 if (_this == null) { 294 _this = this; 295 } 296 var fobjBSP = new ThreeBSP(_fobj); 297 var sobjBSP = new ThreeBSP(_sobj); 298 // var sobjBSP = new ThreeBSP(_sobj); 299 var resultBSP = null; 300 if (mergeOP == '-') { 301 resultBSP = fobjBSP.subtract(sobjBSP); 302 } else if (mergeOP == '+') { 303 var subMesh = new THREE.Mesh(_sobj); 304 _sobj.updateMatrix(); 305 _fobj.geometry.merge(_sobj.geometry, _sobj.matrix); 306 return _fobj; 307 // resultBSP = fobjBSP.union(sobjBSP); 308 } else if (mergeOP == '&') {//交集 309 resultBSP = fobjBSP.intersect(sobjBSP); 310 } else { 311 _this.addObject(_sobj); 312 return _fobj; 313 } 314 var cubeMaterialArray = []; 315 for (var i = 0; i < 1; i++) { 316 cubeMaterialArray.push(new THREE.MeshLambertMaterial({ 317 //map: _this.createSkin(128, 128, { imgurl: '../datacenterdemo/res2/'+(i%11)+'.jpg' }), 318 vertexColors: THREE.FaceColors 319 })); 320 } 321 var cubeMaterials = new THREE.MeshFaceMaterial(cubeMaterialArray); 322 var result = resultBSP.toMesh(cubeMaterials); 323 result.material.shading = THREE.FlatShading; 324 result.geometry.computeFaceNormals(); 325 result.geometry.computeVertexNormals(); 326 result.uuid= _fobj.uuid+mergeOP+_sobj.uuid; 327 result.name=_fobj.name+mergeOP+_sobj.name; 328 result.material.needsUpdate = true; 329 result.geometry.buffersNeedUpdate = true; 330 result.geometry.uvsNeedUpdate = true; 331 var _foreFaceSkin = null; 332 for (var i = 0; i < result.geometry.faces.length; i++) { 333 var _faceset = false; 334 for (var j = 0; j < _fobj.geometry.faces.length; j++) { 335 if (result.geometry.faces[i].vertexNormals[0].x === _fobj.geometry.faces[j].vertexNormals[0].x 336 && result.geometry.faces[i].vertexNormals[0].y === _fobj.geometry.faces[j].vertexNormals[0].y 337 && result.geometry.faces[i].vertexNormals[0].z === _fobj.geometry.faces[j].vertexNormals[0].z 338 && result.geometry.faces[i].vertexNormals[1].x === _fobj.geometry.faces[j].vertexNormals[1].x 339 && result.geometry.faces[i].vertexNormals[1].y === _fobj.geometry.faces[j].vertexNormals[1].y 340 && result.geometry.faces[i].vertexNormals[1].z === _fobj.geometry.faces[j].vertexNormals[1].z 341 && result.geometry.faces[i].vertexNormals[2].x === _fobj.geometry.faces[j].vertexNormals[2].x 342 && result.geometry.faces[i].vertexNormals[2].y === _fobj.geometry.faces[j].vertexNormals[2].y 343 && result.geometry.faces[i].vertexNormals[2].z === _fobj.geometry.faces[j].vertexNormals[2].z) { 344 result.geometry.faces[i].color.setHex(_fobj.geometry.faces[j].color.r * 0xff0000 + _fobj.geometry.faces[j].color.g * 0x00ff00 + _fobj.geometry.faces[j].color.b * 0x0000ff); 345 _foreFaceSkin = _fobj.geometry.faces[j].color.r * 0xff0000 + _fobj.geometry.faces[j].color.g * 0x00ff00 + _fobj.geometry.faces[j].color.b * 0x0000ff; 346 _faceset =true; 347 } 348 } 349 if (_faceset == false){ 350 for(var j = 0; j < _sobj.geometry.faces.length; j++) { 351 if (result.geometry.faces[i].vertexNormals[0].x === _sobj.geometry.faces[j].vertexNormals[0].x 352 && result.geometry.faces[i].vertexNormals[0].y === _sobj.geometry.faces[j].vertexNormals[0].y 353 && result.geometry.faces[i].vertexNormals[0].z === _sobj.geometry.faces[j].vertexNormals[0].z 354 && result.geometry.faces[i].vertexNormals[1].x === _sobj.geometry.faces[j].vertexNormals[1].x 355 && result.geometry.faces[i].vertexNormals[1].y === _sobj.geometry.faces[j].vertexNormals[1].y 356 && result.geometry.faces[i].vertexNormals[1].z === _sobj.geometry.faces[j].vertexNormals[1].z 357 && result.geometry.faces[i].vertexNormals[2].x === _sobj.geometry.faces[j].vertexNormals[2].x 358 && result.geometry.faces[i].vertexNormals[2].y === _sobj.geometry.faces[j].vertexNormals[2].y 359 && result.geometry.faces[i].vertexNormals[2].z === _sobj.geometry.faces[j].vertexNormals[2].z 360 && result.geometry.faces[i].vertexNormals[2].z === _sobj.geometry.faces[j].vertexNormals[2].z) { 361 result.geometry.faces[i].color.setHex(_sobj.geometry.faces[j].color.r * 0xff0000 + _sobj.geometry.faces[j].color.g * 0x00ff00 + _sobj.geometry.faces[j].color.b * 0x0000ff); 362 _foreFaceSkin = _sobj.geometry.faces[j].color.r * 0xff0000 + _sobj.geometry.faces[j].color.g * 0x00ff00 + _sobj.geometry.faces[j].color.b * 0x0000ff; 363 _faceset = true; 364 } 365 } 366 } 367 if (_faceset == false) { 368 result.geometry.faces[i].color.setHex(_foreFaceSkin); 369 } 370 // result.geometry.faces[i].materialIndex = i 371 } 372 result.castShadow = true; 373 result.receiveShadow = true; 374 return result; 375 } 376 //創建盒子體 377 msj3D.prototype.createCube = function (_this, _obj) { 378 if (_this == null) { 379 _this = this; 380 } 381 var _length = _obj.length || 1000;//預設1000 382 var _width = _obj.width || _length; 383 var _height = _obj.height || 10; 384 var _x = _obj.x || 0, _y = _obj.y || 0, _z = _obj.z || 0; 385 var skinColor = _obj.style.skinColor || 0x98750f; 386 var cubeGeometry = new THREE.CubeGeometry(_length, _height, _width, 0, 0, 1); 387 388 //六面顏色 389 for (var i = 0; i < cubeGeometry.faces.length; i += 2) { 390 var hex = skinColor || Math.random() * 0x531844; 391 cubeGeometry.faces[i].color.setHex(hex); 392 cubeGeometry.faces[i + 1].color.setHex(hex); 393 } 394 //六面紋理 395 var skin_up_obj = { 396 vertexColors: THREE.FaceColors 397 } 398 var skin_down_obj = skin_up_obj, 399 skin_fore_obj = skin_up_obj, 400 skin_behind_obj = skin_up_obj, 401 skin_left_obj = skin_up_obj, 402 skin_right_obj = skin_up_obj; 403 var skin_opacity = 1; 404 if (_obj.style != null && typeof (_obj.style) != 'undefined' 405 && _obj.style.skin != null && typeof (_obj.style.skin) != 'undefined') { 406 //透明度 407 if (_obj.style.skin.opacity != null && typeof (_obj.style.skin.opacity) != 'undefined') { 408 skin_opacity = _obj.style.skin.opacity; 409 console.log(skin_opacity) 410 } 411 //上 412 skin_up_obj = _this.createSkinOptionOnj(_this, _length, _width, _obj.style.skin.skin_up, cubeGeometry, 4); 413 //下 414 skin_down_obj = _this.createSkinOptionOnj(_this, _length, _width, _obj.style.skin.skin_down, cubeGeometry, 6); 415 //前 416 skin_fore_obj = _this.createSkinOptionOnj(_this, _length, _width, _obj.style.skin.skin_fore, cubeGeometry, 0); 417 //後 418 skin_behind_obj = _this.createSkinOptionOnj(_this, _length, _width, _obj.style.skin.skin_behind, cubeGeometry, 2); 419 //左 420 skin_left_obj = _this.createSkinOptionOnj(_this, _length, _width, _obj.style.skin.skin_left, cubeGeometry, 8); 421 //右 422 skin_right_obj = _this.createSkinOptionOnj(_this, _length, _width, _obj.style.skin.skin_right, cubeGeometry, 10); 423 } 424 var cubeMaterialArray = []; 425 cubeMaterialArray.push(new THREE.MeshLambertMaterial(skin_fore_obj)); 426 cubeMaterialArray.push(new THREE.MeshLambertMaterial(skin_behind_obj)); 427 cubeMaterialArray.push(new THREE.MeshLambertMaterial(skin_up_obj)); 428 cubeMaterialArray.push(new THREE.MeshLambertMaterial(skin_down_obj)); 429 cubeMaterialArray.push(new THREE.MeshLambertMaterial(skin_right_obj)); 430 cubeMaterialArray.push(new THREE.MeshLambertMaterial(skin_left_obj)); 431 var cubeMaterials = new THREE.MeshFaceMaterial(cubeMaterialArray); 432 cube = new THREE.Mesh(cubeGeometry, cubeMaterials); 433 cube.castShadow = true; 434 cube.receiveShadow = true; 435 cube.uuid = _obj.uuid; 436 cube.name = _obj.name; 437 cube.position.set(_x, _y, _z); 438 if (_obj.rotation != null && typeof (_obj.rotation) != 'undefined' && _obj.rotation.length > 0) { 439 $.each(_obj.rotation, function (index, rotation_obj) { 440 switch (rotation_obj.direction) { 441 case 'x': 442 cube.rotateX(rotation_obj.degree); 443 break; 444 case 'y': 445 cube.rotateY(rotation_obj.degree);