时间:2020-12-10 09:22:52 | 栏目:JavaScript代码 | 点击:次
本文实例为大家分享了Three.js实现简单3D房间布局的具体代码,供大家参考,具体内容如下
废话不说了,直接上成果图。
代码如下
<!doctype html> <html lang="en"> <head> <title>房间布局</title> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0"> </head> <body> <script src="js/jquery-1.9.1.js"></script> <script src="js/Three.min.js"></script> <script src="js/OrbitControls.js"></script> <script src="js/ThreeBSP.js"></script> <script src="js/Detector.js"></script> <script src="js/Stats.js"></script> <script src="js/THREEx.KeyboardState.js"></script> <script src="js/THREEx.FullScreen.js"></script> <script src="js/THREEx.WindowResize.js"></script> <!-- people --> <script src="people/js/three.js"></script> <script src="people/js/DDSLoader.js"></script> <script src="people/js/MTLLoader.js"></script> <script src="people/js/OBJLoader.js"></script> <script src="people/js/Detector.js"></script> <script src="people/js/stats.min.js"></script> <script src="people/js/PathControls.js"></script> <script src="people/js/Tween.js"></script> <script src="people/js/RequestAnimationFrame.js"></script> <div id="ThreeJS" style="position: absolute; left: 0px; top: 0px"></div> <script> // 设置全局变量 var scene, camera, renderer, controls, tween, door; var keyboard = new THREEx.KeyboardState();//保持键盘的当前状态,可以随时查询 var clock = new THREE.Clock(); var SCREEN_WIDTH = window.innerWidth, SCREEN_HEIGHT = window.innerHeight; //var VIEW_ANGLE = 45, ASPECT = SCREEN_WIDTH / SCREEN_HEIGHT, NEAR = 0.1, FAR = 20000; var VIEW_ANGLE = 75, ASPECT = SCREEN_WIDTH / SCREEN_HEIGHT, NEAR = 0.1, FAR = 10000; var materialArrayA = []; var materialArrayB = []; var matArrayA = [];//内墙 var matArrayB = [];//外墙 var dummy = new THREE.Object3D();//仿制品 init(); animate(); //1.场景 function initScene() { scene = new THREE.Scene(); } //2.相机 function initCamera() { camera = new THREE.PerspectiveCamera(VIEW_ANGLE, ASPECT, NEAR, FAR); camera.position.set(0, 1000, 1800); camera.lookAt(scene.position); camera.lookAt(0, 0, 0); scene.add(camera); } //3.渲染器 function initRender() { if (Detector.webgl) renderer = new THREE.WebGLRenderer({ antialias : true }); else renderer = new THREE.CanvasRenderer(); //设置渲染器的大小为窗口的内宽度,也就是内容区的宽度。 renderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT); container = document.getElementById('ThreeJS'); container.appendChild(renderer.domElement); renderer.setClearColor(0x4682B4, 1.0); } //4.事件 function initEvent() { THREEx.WindowResize(renderer, camera); THREEx.FullScreen.bindKey({ charCode : 'm'.charCodeAt(0) }); } //5.控制 function initControls() { controls = new THREE.OrbitControls(camera, renderer.domElement); } //6.光源 function initLight() { // 位置不同,方向光作用于物体的面也不同,看到的物体各个面的颜色也不同 // A start, 第二个参数是光源强度 var directionalLight = new THREE.DirectionalLight(0xffffff, 1);//模拟远处类似太阳的光源 directionalLight.position.set(0, 100, 0).normalize(); scene.add(directionalLight); //A end var ambient = new THREE.AmbientLight(0xffffff, 1); //AmbientLight,影响整个场景的光源 ambient.position.set(0, 0, 0); scene.add(ambient); //var pointlight = new THREE.PointLight(0x000000,1.5,2000); //scene.add(pointlight); } //创建地板 function createFloor() { var loader = new THREE.TextureLoader(); loader.load("images/floor.jpg", function(texture) { texture.wrapS = texture.wrapT = THREE.RepeatWrapping; texture.repeat.set(10, 10); var floorGeometry = new THREE.BoxGeometry(1600, 1100, 1); var floorMaterial = new THREE.MeshBasicMaterial({ map : texture, side : THREE.DoubleSide }); var floor = new THREE.Mesh(floorGeometry, floorMaterial); floor.position.y = -0.5; floor.rotation.x = Math.PI / 2; scene.add(floor); }); //茶色:0x58ACFA 透明玻璃色:0XECF1F3 var glass_material = new THREE.MeshBasicMaterial({ color : 0XECF1F3 }); glass_material.opacity = 0.4; glass_material.transparent = true; var left_wall = returnWallObject(20, 200, 1100, 0, matArrayB, -801, 100, 0); var left_cube = returnWallObject(20, 110, 1100, 0, matArrayB, -801, 100, 0); createResultBsp(left_wall, left_cube, 1); createCubeWall(1, 110, 1100, 0, glass_material, -801, 100, 0); var right_wall = returnWallObject(20, 200, 1100, 1, matArrayB, 801, 100, 0); var right_cube = returnWallObject(20, 110, 1100, 0, matArrayB, 801, 100, 0); createResultBsp(right_wall, right_cube, 1); createCubeWall(1, 110, 1100, 0, glass_material, 801, 100, 0); } //墙上挖门,通过两个几何体生成BSP对象 function createResultBsp(bsp, less_bsp, mat) { switch (mat) { case 1: var material = new THREE.MeshPhongMaterial({ color : 0x9cb2d1, specular : 0x9cb2d1, shininess : 30, transparent : true, opacity : 1 }); break; case 2: var material = new THREE.MeshPhongMaterial({ color : 0xafc0ca, specular : 0xafc0ca, shininess : 30, transparent : true, opacity : 1 }); break; default: } var sphere1BSP = new ThreeBSP(bsp); var cube2BSP = new ThreeBSP(less_bsp);//0x9cb2d1 淡紫,0xC3C3C3 白灰 , 0xafc0ca灰 var resultBSP = sphere1BSP.subtract(cube2BSP); var result = resultBSP.toMesh(material); result.material.flatshading = THREE.FlatShading; result.geometry.computeFaceNormals(); //重新计算几何体侧面法向量 result.geometry.computeVertexNormals(); result.material.needsUpdate = true; //更新纹理 result.geometry.buffersNeedUpdate = true; result.geometry.uvsNeedUpdate = true; scene.add(result); } //创建墙 function createCubeWall(width, height, depth, angle, material, x, y, z) { var cubeGeometry = new THREE.BoxGeometry(width, height, depth); var cube = new THREE.Mesh(cubeGeometry, material); cube.position.x = x; cube.position.y = y; cube.position.z = z; cube.rotation.y += angle * Math.PI; //-逆时针旋转,+顺时针 scene.add(cube); } //返回墙对象 function returnWallObject(width, height, depth, angle, material, x, y, z) { var cubeGeometry = new THREE.BoxGeometry(width, height, depth); var cube = new THREE.Mesh(cubeGeometry, material); cube.position.x = x; cube.position.y = y; cube.position.z = z; cube.rotation.y += angle * Math.PI; return cube; } //创建墙纹理 function createWallMaterail() { matArrayA.push(new THREE.MeshPhongMaterial({ color : 0xafc0ca })); //前 0xafc0ca :灰色 matArrayA.push(new THREE.MeshPhongMaterial({ color : 0xafc0ca })); //后 matArrayA.push(new THREE.MeshPhongMaterial({ color : 0xd6e4ec })); //上 0xd6e4ec: 偏白色 matArrayA.push(new THREE.MeshPhongMaterial({ color : 0xd6e4ec })); //下 matArrayA.push(new THREE.MeshPhongMaterial({ color : 0xafc0ca })); //左 0xafc0ca :灰色 matArrayA.push(new THREE.MeshPhongMaterial({ color : 0xafc0ca })); //右 matArrayB.push(new THREE.MeshPhongMaterial({ color : 0xafc0ca })); //前 0xafc0ca :灰色 matArrayB.push(new THREE.MeshPhongMaterial({ color : 0x9cb2d1 })); //后 0x9cb2d1:淡紫 matArrayB.push(new THREE.MeshPhongMaterial({ color : 0xd6e4ec })); //上 0xd6e4ec: 偏白色 matArrayB.push(new THREE.MeshPhongMaterial({ color : 0xd6e4ec })); //下 matArrayB.push(new THREE.MeshPhongMaterial({ color : 0xafc0ca })); //左 0xafc0ca :灰色 matArrayB.push(new THREE.MeshPhongMaterial({ color : 0xafc0ca })); //右 } //创建房间布局 function createLayout() { // 墙面1 立方体比较长的面 左一 createCubeWall(10, 200, 900, 0, matArrayB, -651, 100, 0); // 墙面2 立方体比较长的面 右一 createCubeWall(10, 200, 900, 1, matArrayB, 651, 100, 0); // 墙面3 门对面的墙 立方体比较短的面 createCubeWall(10, 200, 1310, 1.5, matArrayB, 0, 100, -451); // 墙面4 带门的面 var wall = returnWallObject(1310, 200, 10, 0, matArrayB, 0, 100, 455); // 门框 var door_cube = returnWallObject(100, 180, 10, 0, matArrayB, 0, 90, 455); createResultBsp(wall, door_cube, 1); //为墙面安装门,右门 var loader = new THREE.TextureLoader(); loader.load("images/door_right.png", function(texture) { var doorgeometry = new THREE.BoxGeometry(100, 180, 2); var doormaterial = new THREE.MeshBasicMaterial({ map : texture, color : 0xffffff }); doormaterial.opacity = 1.0; doormaterial.transparent = true; door = new THREE.Mesh(doorgeometry, doormaterial); door.position.set(-50, 0, 0); var door1 = door.clone(); door1.position.set(50, 0, 0); door1.visible = false; dummy.add(door); dummy.add(door1); dummy.position.set(50, 90, 451) scene.add(dummy); }); // 房间A:隔墙1 createCubeWall(10, 200, 250, 0, matArrayA, -151, 100, 325); //房间A:隔墙2 无门 createCubeWall(10, 200, 220, 0.5, matArrayA, -256, 100, 201); // 厨房:隔墙3 createCubeWall(350, 200, 10, 0, matArrayA, 481, 100, 131); // 厨房:隔墙4 无门 createCubeWall(10, 200, 200, 0, matArrayA, 301, 100, 225); // 房间B createCubeWall(350, 200, 10, 0, matArrayA, -471, 100, -50); //房间B 无门 createCubeWall(200, 200, 10, 0.5, matArrayA, 0, 100, -350); // 房间C createCubeWall(220, 200, 10, 0, matArrayA, 540, 100, -50); //房间C 无门 createCubeWall(200, 200, 10, 0.5, matArrayA, 250, 100, -350); //厕所 var cube = returnWallObject(10, 200, 260, 0.5, matArrayA, 125, 100, -250); //厕所门框 var door_cube1 = returnWallObject(10, 160, 80, 0.5, matArrayA, 155, 90, -250); createResultBsp(cube, door_cube1, 2); //茶色:0x58ACFA 透明玻璃色:0XECF1F3 var glass_material = new THREE.MeshBasicMaterial({ color : 0x58ACFA }); glass_material.opacity = 0.6; glass_material.transparent = true; createCubeWall(1, 180, 80, 0.5, glass_material, 155, 90, -250); } //7.初始化OBJ对象 function initObject() { //墙纹理 createWallMaterail(); createFloor(); createLayout(); } //初始化函数 function init() { initScene(); initCamera(); initRender(); initEvent(); initControls(); initLight(); initObject(); //监听键盘按键 document.addEventListener("keydown", onkeyDown, false); } var door_state = true;//默认是门是关闭的 //Enter=13,Space=32; function onkeyDown(event) { switch (event.keyCode) { case 13: console.log(event.keyCode); if (door_state) { dummy.rotation.y += 0.5 * Math.PI; door_state = false; } else { dummy.rotation.y -= 0.5 * Math.PI; door_state = true; } break; default: console.log(event.keyCode); break; } } function animate() { requestAnimationFrame(animate); renderer.render(scene, camera); TWEEN.update(); update(); } function update() { var delta = clock.getDelta(); var moveDistance = 200 * delta; var rotateAngle = Math.PI / 2 * delta; controls.update(); } </script> </body> </html>
通过Enter键可控制开门和关门动作。门的旋转是通过,把门克隆一份,把克隆的那个设置为不可见,然后把两个门打个组 ,这个时候中旋转组就可以了。
此时的旋转中心实际是在组的中心,但设置一半不可见 ,看起来就像是门在旋转了。注意的是,组内的东西的坐标是相对于组的组内,两个门的坐标应该分别是x轴的正负轴上,整个组的位置应该是原来门应该在的位置。
这也是我向一位大神请教的,真的很感谢他那么耐心的教我。
运行方式:
在支持webgl的浏览器上打开room.html,即可看到效果图。如果加载不出来,打开Chrome快捷方式的属性中设置:右击Chrome浏览器快捷方式, 选择“属性”,在“目标”中加上"--allow-file-access-from-files",注意前面有个空格。修改完成,点击应用,确定后,关闭所有chrome上的窗口,重启chrome。再找到该资源room.html文件,以Google Chrome浏览器方式打开即可。
错误解决。
如果出现地板和门的两张图片加载不出来时,提示已被跨源资源共享策略阻止加载。解决办法第一种是如上图所示在Chrome的属性加"--allow-file-access-from-files";第二种就是把图片位置的相对路径改成绝对路径。