最近学习threejs有些时间了,就想着着手做些东西。前端Vue里面引入thrssjs,先给大家看下效果。

在线地址

用到的知识点只要有三维几何体BoxGeometry、材质、纹理贴图、PlaneGeometry、CylinderGeometry,以及BSP的二元操作等。在使用Vue引入threejs中,我采用的方法是直接在index.html中引用。

初次尝试,代码写的臃肿杂乱,不过比较适合刚入门的小伙伴,后续我也会将样例发布到云服务器上,可供大家在线预览。有不足之处欢迎指出,下方评论留言。大神请略过。。。

    <!-- 引入THREEJS 相关-->
    <script src="lib/three/three.js"></script>
    <script src="lib/three/controls/TrackballControls.js"></script>
    <script src="lib/three/loaders/MTLLoader.js"></script>
    <script src="lib/three/loaders/OBJLoader.js"></script>
    <script src="lib/three/loaders/GLTFLoader.js"></script>
    <script src="lib/three/controls/OrbitControls.js"></script>
    <script src="lib/three/other/ThreeBSP.js"></script>

1 首先初始化场景

            init(){
                //创建三大件
                //1.1 场景
                this.scene = new THREE.Scene();
                //1.2 相机
                this.camera = new THREE.PerspectiveCamera(50, window.innerWidth/window.innerHeight, 0.1, 1000);
                //1.3 渲染器
                this.renderer = new THREE.WebGLRenderer({antialias: true});

                //创建纹理加载器
                this.textureLoader = new THREE.TextureLoader();

                //设置相机
                //设置摄像机位置,相机方向逆X轴方向,倾斜向下看
                this.camera.position.set(360, 360 ,0);
                //指向场景中心
                this.camera.lookAt(this.scene.position);
                //添加坐标轴,辅助判断位置
                let axes = new THREE.AxesHelper(1000);
                // this.scene.add(axes);

                //设置环境
                this.renderer.setClearColor(new THREE.Color(0xF7F2F1));
                //设置场景大小
                this.renderer.setSize(
                    document.getElementById("threeworld").clientWidth,
                    document.getElementById("threeworld").clientHeight);
                //渲染器开启阴影效果
                this.renderer.shadowMap.enabled = true;
                //渲染div到canvas
                document.getElementById("threeworld").appendChild(this.renderer.domElement);

                //鼠标键盘控制
                this.controls = new THREE.OrbitControls(this.camera, this.renderer.domElement);

                //点光源
                let point = new THREE.PointLight(0xffffff);
                point.position.set(500, 300, 400); //点光源位置
                this.scene.add(point); //点光源添加到场景中
                // 环境光
                let ambient = new THREE.AmbientLight(0x999999);
                this.scene.add(ambient);
            }

2 创建不同类型的几何体,组合成会议室

2.1 地板

地板基本单元是使用BoxBufferGeometry创建,使用纹理贴图。

            addGeoBox(){
              //创建材质并贴上纹理
              let floorTexture = this.textureLoader.load('/static/textures/floor/floor.jpg');
              let boxTextureMaterial = new THREE.MeshStandardMaterial({
                    map:floorTexture,
                    metalness: 0.2,
                    roughness: 0.07,
                    side: THREE.DoubleSide
                });
              //创建地板
              for(let i=0; i<12; i++){
                  for(let j=0; j<16; j++){
                      let boxGeo = new THREE.BoxBufferGeometry(29.9,2, 29.9);
                      let boxMesh = new THREE.Mesh(boxGeo, boxTextureMaterial);
                      boxMesh.position.set(-150 + i*30, 0, -240 + j*30);
                      this.scene.add(boxMesh);
                  }
              }
            },

2.2 墙体

墙体使用BoxGeometry,使用两种不同图片的纹理贴图。墙上的门,窗户采用BSP的二元操作扣除。

            createWall(){
                //外墙
                let wallMaterial = new THREE.MeshLambertMaterial({color: 0x00FFFF});
                let wallGeo = new THREE.BoxGeometry(280, 120, 400);
                let wallMesh = new THREE.Mesh(wallGeo, wallMaterial);
                wallMesh.position.set(0, 60, -14.95);
                //内墙
                let wallInnerMaterial = new THREE.MeshLambertMaterial({color: 0x2D1BFF});
                let wallInnerGeo = new THREE.BoxGeometry(270, 120, 390);
                let wallInnerMesh = new THREE.Mesh(wallInnerGeo, wallInnerMaterial);
                wallInnerMesh.position.set(0, 60, -14.95);
                //门
                let doorInnerMaterial = new THREE.MeshLambertMaterial({color: 0x2D1BFF});
                let doorGeo = new THREE.BoxGeometry(18, 100, 70);
                let doorMesh = new THREE.Mesh(doorGeo, doorInnerMaterial);
                doorMesh.position.set(140.5, 50, 0);
                // this.scene.add(doorMesh);

                //转BSP
                let wallBSP = new ThreeBSP(wallMesh);
                let wallInnerBSP = new ThreeBSP(wallInnerMesh);
                let doorBSP = new ThreeBSP(doorMesh);
                let window1BSP = new ThreeBSP(this.createWindowRight());
                let window2BSP = new ThreeBSP(this.createWindowLeft());
                let wallResultBSP = wallBSP.subtract(wallInnerBSP);
                wallResultBSP = wallResultBSP.subtract(doorBSP);
                wallResultBSP = wallResultBSP.subtract(window1BSP);
                wallResultBSP = wallResultBSP.subtract(window2BSP);
                let wallResultMesh = wallResultBSP.toMesh();

                //转换后的Mesh配置属性
                let wallTexture = this.textureLoader.load('/static/textures/wall/wall.jpg');
                let wallTextureMaterial = new THREE.MeshStandardMaterial({
                    map:wallTexture,
                    metalness: 0.2,
                    roughness: 0.07,
                    side: THREE.DoubleSide
                });
                let wallInnerTexture = this.textureLoader.load('/static/textures/wall/wallinner.jpg');
                let wallInnerTextureMaterial = new THREE.MeshStandardMaterial({
                    map:wallInnerTexture,
                    metalness: 0.2,
                    roughness: 0.07,
                    side: THREE.DoubleSide
                });

                let wallResultMeshMaterial = [];
                wallResultMeshMaterial.push(wallTextureMaterial);
                wallResultMeshMaterial.push(wallInnerTextureMaterial);
                wallResultMesh.material = wallResultMeshMaterial;

                console.log(wallResultMesh.geometry.faces, 112233);
                wallResultMesh.geometry.faces.forEach((item, i) => {
                    if(i<160){
                        item.materialIndex = 0;
                    } else {
                        item.materialIndex = 1;
                    }
                })

                wallResultMesh.geometry.computeFaceNormals();
                wallResultMesh.geometry.computeVertexNormals();
                //添加结果到场景中
                this.scene.add(wallResultMesh);
            }

2.3 窗户

同样的道理,生成窗户

            createWindowRight(){
                let shpMaterial1 = new THREE.MeshBasicMaterial({color:'#F7C777'});
                let shpGeometry1 = new THREE.BoxGeometry(70, 90, 10);
                let shpMesh1 = new THREE.Mesh(shpGeometry1, shpMaterial1);
                shpMesh1.position.set(-40, 65, -213);
       
                let wMaterial1 = new THREE.MeshBasicMaterial({color:0x2D1BFF});
                let wGeometry1 = new THREE.BoxGeometry(70, 3, 10);
                let wMesh1 = new THREE.Mesh(wGeometry1, wMaterial1);
                wMesh1.position.set(-40, 85, -213);
   
                let wMaterial2 = new THREE.MeshBasicMaterial({color:0x2B2B2B});
                let wGeometry2 = new THREE.BoxGeometry(3, 90, 10);
                let wMesh2 = new THREE.Mesh(wGeometry2, wMaterial2);
                wMesh2.position.set(-40, 65, -213);
    
                let shpMesh1BSP = new ThreeBSP(shpMesh1);
                let wMesh1BSP = new ThreeBSP(wMesh1);
                let wMesh2BSP = new ThreeBSP(wMesh2);
                let shpMesh1BSPResult = shpMesh1BSP.subtract(wMesh1BSP);
                shpMesh1BSPResult = shpMesh1BSPResult.subtract(wMesh2BSP);
                let shpMesh1MeshResult = shpMesh1BSPResult.toMesh();
                return shpMesh1MeshResult;
            }

2.4 添加会议桌和桌上的花瓶

会议中使用BoxGeometry,BSP做二元操作,花瓶使用LatheGeometry生成。

            createConferenceTable(){
                let desktopTexture = this.textureLoader.load('/static/textures/desktop/desktop.jpg');
                let desktopTextureMaterial = new THREE.MeshStandardMaterial({
                    map:desktopTexture,
                    metalness: 0.2,
                    roughness: 0.07,
                    side: THREE.DoubleSide
                });
                let desktopGeo = new THREE.BoxGeometry(100, 50, 280);
                let desktopMesh = new THREE.Mesh(desktopGeo, desktopTextureMaterial);
                desktopMesh.position.set(0, 25, -14.95);

                let d1Material = new THREE.MeshBasicMaterial({color:0xC49235});
                let d1Geo = new THREE.BoxGeometry(100, 44, 274);
                let d1Mesh = new THREE.Mesh(d1Geo, d1Material);
                d1Mesh.position.set(0, 22, -14.95);

                let d2Material = new THREE.MeshBasicMaterial({color:0x0000FF});
                let d2Geo = new THREE.BoxGeometry(94, 44, 320);
                let d2Mesh = new THREE.Mesh(d2Geo, d2Material);
                d2Mesh.position.set(0, 22, -14.95);

                let desktopMeshBSP = new ThreeBSP(desktopMesh);
                let d1BSP = new ThreeBSP(d1Mesh);
                let d2BSP = new ThreeBSP(d2Mesh);
                let desktopBSPResult = desktopMeshBSP.subtract(d1BSP);
                desktopBSPResult = desktopBSPResult.subtract(d2BSP);
                let desktopMeshResult = desktopBSPResult.toMesh();
                desktopMeshResult.material = desktopTextureMaterial;
                this.scene.add(desktopMeshResult);

                // //会议桌上的花瓶
                let points = [];
                let height = 3;
                let count = 30;
                for(let i=0; i<count; i++){
                    let pointV3 = new THREE.Vector3(
                        (Math.sin(i*0.2) + Math.cos(i*0.3)) * height + 6,
                        (i-count) + count/2,
                        0
                    );
                    points.push(pointV3);
                }
                let latheGeometry = new THREE.LatheGeometry(
                    points,
                    20,
                    0,
                    2*Math.PI
                );

                let latheTexture = this.textureLoader.load('/static/textures/flowervase/flowervase.jpg');
                let latheMaterial = new THREE.MeshStandardMaterial({
                    map:latheTexture,
                    metalness: 0.2,
                    roughness: 0.07,
                    side: THREE.DoubleSide
                });

                //花
                var leafTexture = this.textureLoader.load('/static/textures/flowervase/flower.jpg');
                var leafMaterial = new THREE.MeshBasicMaterial(
                    {
                                    map:leafTexture,
                                    side:THREE.DoubleSide,
                                    transparent:true
                                });
                let geom = new THREE.PlaneGeometry(4, 8);

                //第一个花瓶
                for(let i=0;i<40;i++){
                    let leaf = new THREE.Mesh(geom, leafMaterial );
                    leaf.position.x = Math.random()*2;
                    leaf.position.y = 70 + Math.random() * i/2;
                    leaf.position.z = Math.random() * 3 - 2;
                    leaf.rotation.x = -Math.PI/(i+1) + Math.random();
                    leaf.rotation.y = -Math.PI/(i+1) + Math.random();
                    leaf.rotation.z = -Math.PI/(i+1) + Math.random();
                    this.scene.add(leaf);
                }
                let latheMesh1 = new THREE.Mesh(latheGeometry, latheMaterial);
                latheMesh1.position.set(0, 60, 0);
                this.scene.add(latheMesh1);

                //第二个花瓶
                for(let i=0;i<40;i++){
                    let leaf = new THREE.Mesh(geom, leafMaterial );
                    leaf.position.x = Math.random()*2;
                    leaf.position.y = 70 + Math.random() * i/2;
                    leaf.position.z = Math.random() * 3 - 2 -100;
                    leaf.rotation.x = -Math.PI/(i+1) + Math.random();
                    leaf.rotation.y = -Math.PI/(i+1) + Math.random();
                    leaf.rotation.z = -Math.PI/(i+1) + Math.random();
                    this.scene.add(leaf);
                }
                let latheMesh = new THREE.Mesh(latheGeometry, latheMaterial);
                latheMesh.position.set(0, 60, -100);
                this.scene.add(latheMesh);

                //第三个花瓶
                for(let i=0;i<40;i++){
                    let leaf = new THREE.Mesh(geom, leafMaterial );
                    leaf.position.x = Math.random()*2;
                    leaf.position.y = 70 + Math.random() * i/2;
                    leaf.position.z = Math.random() * 3 - 2 +85.05;
                    leaf.rotation.x = -Math.PI/(i+1) + Math.random();
                    leaf.rotation.y = -Math.PI/(i+1) + Math.random();
                    leaf.rotation.z = -Math.PI/(i+1) + Math.random();
                    this.scene.add(leaf);
                }
                let latheMesh3 = new THREE.Mesh(latheGeometry, latheMaterial);
                latheMesh3.position.set(0, 60, 85.05);
                this.scene.add(latheMesh3);
            }

2.5 椅子

createChair(){
    let groupBox = new THREE.Group();
    let boxMaterial = new THREE.MeshBasicMaterial({color:0x559762});
    let boxTextures1 = this.textureLoader.load('/static/textures/chair/chair.jpg');
    let boxMaterial1 = new THREE.MeshStandardMaterial({
        map: boxTextures1,
        metalness: 0.2,
        roughness: 0.07,
        side: THREE.DoubleSide
    })
    for(let i=0; i<6; i++){
        //前面椅子
        let boxGeo1 = new THREE.BoxGeometry(30, 40, 30);
        let boxGeo2 = new THREE.BoxGeometry(24, 28, 32);
        let boxGeo3 = new THREE.BoxGeometry(32, 28, 20);
        let boxMesh1 = new THREE.Mesh(boxGeo1, boxMaterial1);
        let boxMesh2 = new THREE.Mesh(boxGeo2, boxMaterial);
        let boxMesh3 = new THREE.Mesh(boxGeo3, boxMaterial);
        boxMesh1.position.set(0, 20, -i*45 + 10);
        boxMesh2.position.set(0, 14, -i*45 + 10);
        boxMesh3.position.set(0, 14, -i*45 + 10);
        let boxMeshBSP1 = new ThreeBSP(boxMesh1);
        let boxMeshBSP2 = new ThreeBSP(boxMesh2);
        let boxMeshBSP3 = new ThreeBSP(boxMesh3);
        let boxMeshBSPResult = boxMeshBSP1.subtract(boxMeshBSP2);
        boxMeshBSPResult = boxMeshBSPResult.subtract(boxMeshBSP3);
        let boxMeshResult = boxMeshBSPResult.toMesh();
        boxMeshResult.material = boxMaterial1;
        groupBox.add(boxMeshResult);
        //靠背
        let boxBackRestGeo = new THREE.BoxGeometry(3, 30, 30);
        let boxBackRestMesh = new THREE.Mesh(boxBackRestGeo, boxMaterial1);
        boxBackRestMesh.position.set(13, 55, -i*45+10);
        groupBox.add(boxBackRestMesh);

        //后面椅子
        let boxGeob1 = new THREE.BoxGeometry(30, 40, 30);
        let boxGeob2 = new THREE.BoxGeometry(24, 28, 32);
        let boxGeob3 = new THREE.BoxGeometry(32, 28, 20);
        let boxMeshb1 = new THREE.Mesh(boxGeob1, boxMaterial1);
        let boxMeshb2 = new THREE.Mesh(boxGeob2, boxMaterial);
        let boxMeshb3 = new THREE.Mesh(boxGeob3, boxMaterial);
        boxMeshb1.position.set(-160, 20, -i*45 + 10);
        boxMeshb2.position.set(-160, 14, -i*45 + 10);
        boxMeshb3.position.set(-160, 14, -i*45 + 10);
        let boxMeshBSPb1 = new ThreeBSP(boxMeshb1);
        let boxMeshBSPb2 = new ThreeBSP(boxMeshb2);
        let boxMeshBSPb3 = new ThreeBSP(boxMeshb3);
        let boxMeshBSPResultb = boxMeshBSPb1.subtract(boxMeshBSPb2);
        boxMeshBSPResultb = boxMeshBSPResultb.subtract(boxMeshBSPb3);
        let boxMeshResultb = boxMeshBSPResultb.toMesh();
        boxMeshResultb.material = boxMaterial1;
        groupBox.add(boxMeshResultb);
        //靠背
        let boxBackRestGeob = new THREE.BoxGeometry(3, 30, 30);
        let boxBackRestMeshb = new THREE.Mesh(boxBackRestGeob, boxMaterial1);
        boxBackRestMeshb.position.set(-173.3, 55, -i*45+10);
        groupBox.add(boxBackRestMeshb);
    }
    groupBox.position.set(80, 5, 100);
    this.scene.add(groupBox);
}

2.6 盆栽

createPottedPlant(){
    let group = new THREE.Group();
    let cylinderMaterial = new THREE.MeshBasicMaterial({color:0x559762});
    let cylinderTextures1 = this.textureLoader.load('/static/textures/cylinder/cylinder.jpg');
    let cylinderMaterial1 = new THREE.MeshStandardMaterial({
        map: cylinderTextures1,
        metalness: 0.2,
        roughness: 0.07,
        side: THREE.DoubleSide
    });

    var leafTexture = this.textureLoader.load('/static/textures/leaf/leaf.jpg');
    var leafMaterial = new THREE.MeshBasicMaterial(
        {
            map:leafTexture,
            side:THREE.DoubleSide,
            transparent:true
        });
    let geom = new THREE.PlaneGeometry(4, 8);

    let cylinderGeo = new THREE.CylinderGeometry(12, 15, 40, 20, 10, false);
    let cylinderMesh = new THREE.Mesh(cylinderGeo, cylinderMaterial1);
    cylinderMesh.position.set(100, 20, -180);
    group.add(cylinderMesh);
    for(let i=0;i<40;i++){
        let leaf = new THREE.Mesh(geom, leafMaterial);
        leaf.position.x = 95 + Math.random() * 15;
        leaf.position.y = 40 + Math.random() * i/1.2;
        leaf.position.z = -185 + Math.random() * 10;
        leaf.rotation.x = -Math.PI/(i+1) + Math.random();
        leaf.rotation.y = -Math.PI/(i+1) + Math.random();
        leaf.rotation.z = -Math.PI/(i+1) + Math.random();
        group.add(leaf);
    }

    let cylinderGeo1 = new THREE.CylinderGeometry(12, 15, 40, 20, 10, false);
    let cylinderMesh1 = new THREE.Mesh(cylinderGeo1, cylinderMaterial1);
    cylinderMesh1.position.set(-100, 20, -180);
    group.add(cylinderMesh1);
    for(let i=0;i<60;i++){
        let leaf = new THREE.Mesh(geom, leafMaterial);
        leaf.position.x = -105 + Math.random() * 15;
        leaf.position.y = 40 + Math.random() * i/1.2;
        leaf.position.z = -185 + Math.random() * 10;
        leaf.rotation.x = -Math.PI/(i+1) + Math.random();
        leaf.rotation.y = -Math.PI/(i+1) + Math.random();
        leaf.rotation.z = -Math.PI/(i+1) + Math.random();
        group.add(leaf);
    }
    group.position.set(0, 0, 0);
    this.scene.add(group);
    console.log(Math.random(), 999999);
}

2.7 墙上大屏

createLargeScreen(){
                let createLargeTextures1 = this.textureLoader.load('/static/textures/largeScreen/largeScreen.jpg');
                let createLargeMaterial1 = new THREE.MeshStandardMaterial({
                    map: createLargeTextures1,
                    metalness: 0.2,
                    roughness: 0.07,
                    side: THREE.DoubleSide
                });
                let boxGeo1 = new THREE.BoxGeometry(140, 80, 0.1);
                let boxGeo2 = new THREE.PlaneGeometry(140, 80);
                let boxMesh1 = new THREE.Mesh(boxGeo1, createLargeMaterial1);
                let boxMesh2 = new THREE.Mesh(boxGeo2, createLargeMaterial1);
                boxMesh2.position.set(0, 70,  180);
                this.scene.add(boxMesh2);
            }

2.8 墙上文字

createText(){
                this.fontload = new THREE.FontLoader();
                this.fontload.load(
                    '../static/fonts/MI LANTING_Regular.json',
                    (response) => {
                        let options = {
                            size: 18,
                            height: 10,
                            font:response,
                        };
                        let textGeom = new THREE.TextGeometry("保密就是保安全,保发展", options);
                        let textMaterial = new THREE.MeshBasicMaterial({color:"red"});
                        let textMesh = new THREE.Mesh(textGeom, textMaterial);
                        textMesh.position.set(-140, 80, 100);
                        textMesh.rotation.y = 0.5 * Math.PI;
                        this.scene.add(textMesh);
                    }
                );
            }

至此,简单会议室创建完成

Logo

前往低代码交流专区

更多推荐