Three.js 实现3D房间布局的简单实现

时间:2022-01-01 05:39:05

废话不说了,直接上成果图。

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)