用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轴的正负轴上,整个组的位置应该是原来门应该在的位置。

(这也是我向一位大神请教的,真的很感谢他那么耐心的教我,O(∩_∩)O)

 

运行方式:

在支持webgl的浏览器上打开room.html,即可看到效果图。如果加载不出来,打开Chrome快捷方式的属性中设置:右击Chrome浏览器快捷方式, 选择“属性”,在“目标”中加上"--allow-file-access-from-files",注意前面有个空格。修改完成,点击应用,确定后,关闭所有chrome上的窗口,重启chrome。再找到该资源room.html文件,以Google Chrome浏览器方式打开即可。

 

错误解决。

如果出现地板和门的两张图片加载不出来时,提示已被跨源资源共享策略阻止加载。解决办法第一种是如上图所示在Chrome的属性加"--allow-file-access-from-files";第二种就是把图片位置的相对路径改成绝对路径。

原demo发邮件索取的太多了,都从网盘自取吧。就是个demo,不喜勿喷,渣渣博主很玻璃心。

链接:https://pan.baidu.com/s/1UmBcjFCQ5QzdLcfGp-gpug 密码:r5c8

©️2020 CSDN 皮肤主题: 技术工厂 设计师: CSDN官方博客 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值