Three.js實(shí)現(xiàn)簡單3D房間布局
本文實(shí)例為大家分享了Three.js實(shí)現(xiàn)簡單3D房間布局的具體代碼,供大家參考,具體內(nèi)容如下
廢話不說了,直接上成果圖。

代碼如下
<!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>
// 設(shè)置全局變量
var scene, camera, renderer, controls, tween, door;
var keyboard = new THREEx.KeyboardState();//保持鍵盤的當(dāng)前狀態(tài),可以隨時(shí)查詢
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 = [];//內(nèi)墻
var matArrayB = [];//外墻
var dummy = new THREE.Object3D();//仿制品
init();
animate();
//1.場景
function initScene() {
scene = new THREE.Scene();
}
//2.相機(jī)
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();
//設(shè)置渲染器的大小為窗口的內(nèi)寬度,也就是內(nèi)容區(qū)的寬度。
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() {
// 位置不同,方向光作用于物體的面也不同,看到的物體各個(gè)面的顏色也不同
// A start, 第二個(gè)參數(shù)是光源強(qiáng)度
var directionalLight = new THREE.DirectionalLight(0xffffff, 1);//模擬遠(yuǎn)處類似太陽的光源
directionalLight.position.set(0, 100, 0).normalize();
scene.add(directionalLight);
//A end
var ambient = new THREE.AmbientLight(0xffffff, 1); //AmbientLight,影響整個(gè)場景的光源
ambient.position.set(0, 0, 0);
scene.add(ambient);
//var pointlight = new THREE.PointLight(0x000000,1.5,2000);
//scene.add(pointlight);
}
//創(chuàng)建地板
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);
}
//墻上挖門,通過兩個(gè)幾何體生成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(); //重新計(jì)算幾何體側(cè)面法向量
result.geometry.computeVertexNormals();
result.material.needsUpdate = true; //更新紋理
result.geometry.buffersNeedUpdate = true;
result.geometry.uvsNeedUpdate = true;
scene.add(result);
}
//創(chuàng)建墻
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; //-逆時(shí)針旋轉(zhuǎn),+順時(shí)針
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;
}
//創(chuàng)建墻紋理
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
})); //右
}
//創(chuàng)建房間布局
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();
}
//初始化函數(shù)
function init() {
initScene();
initCamera();
initRender();
initEvent();
initControls();
initLight();
initObject();
//監(jiān)聽鍵盤按鍵
document.addEventListener("keydown", onkeyDown, false);
}
var door_state = true;//默認(rèn)是門是關(guān)閉的
//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鍵可控制開門和關(guān)門動作。門的旋轉(zhuǎn)是通過,把門克隆一份,把克隆的那個(gè)設(shè)置為不可見,然后把兩個(gè)門打個(gè)組 ,這個(gè)時(shí)候中旋轉(zhuǎn)組就可以了。
此時(shí)的旋轉(zhuǎn)中心實(shí)際是在組的中心,但設(shè)置一半不可見 ,看起來就像是門在旋轉(zhuǎn)了。注意的是,組內(nèi)的東西的坐標(biāo)是相對于組的組內(nèi),兩個(gè)門的坐標(biāo)應(yīng)該分別是x軸的正負(fù)軸上,整個(gè)組的位置應(yīng)該是原來門應(yīng)該在的位置。
這也是我向一位大神請教的,真的很感謝他那么耐心的教我。
運(yùn)行方式:
在支持webgl的瀏覽器上打開room.html,即可看到效果圖。如果加載不出來,打開Chrome快捷方式的屬性中設(shè)置:右擊Chrome瀏覽器快捷方式, 選擇“屬性”,在“目標(biāo)”中加上"--allow-file-access-from-files",注意前面有個(gè)空格。修改完成,點(diǎn)擊應(yīng)用,確定后,關(guān)閉所有chrome上的窗口,重啟chrome。再找到該資源room.html文件,以Google Chrome瀏覽器方式打開即可。

錯(cuò)誤解決。
如果出現(xiàn)地板和門的兩張圖片加載不出來時(shí),提示已被跨源資源共享策略阻止加載。解決辦法第一種是如上圖所示在Chrome的屬性加"--allow-file-access-from-files";第二種就是把圖片位置的相對路徑改成絕對路徑。
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
uniapp-路由uni-simple-router安裝配置教程
專為uniapp打造的路由器,和uniapp深度集成,uniapp用到了很多vue的api,但在路由管理的功能相對于vue-router還是比較欠缺的,比如全局導(dǎo)航守衛(wèi),本文給大家講解uniapp-路由uni-simple-router相關(guān)知識,感興趣的朋友跟隨小編一起看看吧2022-11-11
為非IE瀏覽器添加mouseenter,mouseleave事件的實(shí)現(xiàn)代碼
為非IE瀏覽器添加mouseenter,mouseleave事件的實(shí)現(xiàn)代碼,學(xué)習(xí)js的朋友可以參考下。2011-06-06
JS獲取當(dāng)前時(shí)間的年月日時(shí)分秒及時(shí)間的格式化的方法
這篇文章主要介紹了js獲取當(dāng)前時(shí)間的年月日時(shí)分秒及時(shí)間的格式化,本文通過實(shí)例代碼講解的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-12-12
微信公眾平臺 發(fā)送模板消息(Java接口開發(fā))
這篇文章主要介紹了微信公眾平臺 發(fā)送模板消息(Java接口開發(fā)),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-04-04

