• threejs(2)-Geometry进阶详解


    一、全面讲解UV与应用

    在本节中,我们将讨论Three.js中的UV映射,包括UV映射的概念、与顶点位置的关系和区别以及如何在Geometry中设置UV坐标。我们将使用BufferGeometry进行示例说明。

    颜色对应
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    1. 什么是UV映射?

    UV映射是一种将二维纹理映射到三维模型表面的技术。在这个过程中,3D模型上的每个顶点都会被赋予一个二维坐标(U, V)。U和V分别表示纹理坐标的水平和垂直方向。这些坐标用于将纹理图像上的像素与模型表面上的点进行对应。通过UV映射,我们可以在模型上精确地控制纹理的位置和方向。

    1. UV坐标与顶点位置的关系和区别

    顶点位置(Position)表示3D模型中每个顶点的空间坐标(x, y, z)。UV坐标则表示该顶点在纹理上的二维坐标(U, V)。顶点位置用于确定模型在场景中的形状,而UV坐标用于确定纹理在模型上的分布。
    两者之间的主要区别在于:
    ● 顶点位置是三维坐标,描述了一个顶点在三维空间中的位置。
    ● UV坐标是二维坐标,描述了一个顶点在纹理图像上的位置。

    1. 设置UV坐标

    下面我们将通过一个示例来展示如何在BufferGeometry中设置UV坐标。
    假设我们要创建一个带纹理的平面。首先,我们需要一张纹理图片。我们将使用THREE.TextureLoader加载纹理:

    const loader = new THREE.TextureLoader();
    const texture = loader.load('path/to/your/texture.jpg');
    
    • 1
    • 2

    接着,我们创建一个BufferGeometry并设置顶点位置:

    const geometry = new THREE.BufferGeometry();
    
    const vertices = new Float32Array([
      -1.0, -1.0,  0.0,
       1.0, -1.0,  0.0,
       1.0,  1.0,  0.0,
      -1.0,  1.0,  0.0
    ]);
    
    geometry.setAttribute('position', new THREE.BufferAttribute(vertices, 3));
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    然后,我们为每个顶点设置UV坐标。在这个例子中,我们将纹理均匀地映射到四个顶点上:

    const uvs = new Float32Array([
      0.0, 0.0,
      1.0, 0.0,
      1.0, 1.0,
      0.0, 1.0
    ]);
    
    geometry.setAttribute('uv',new THREE.BufferAttribute(uvs, 2));
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    接下来,我们需要定义组成平面的两个三角形。为此,我们设置索引属性:

    const indices = new Uint16Array([
      0, 1, 2,
      2, 3, 0
    ]);
    
    geometry.setIndex(new THREE.BufferAttribute(indices, 1));
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    现在我们创建一个材质,并将之前加载的纹理传递给材质:

    const material = new THREE.MeshBasicMaterial({ map: texture });
    
    • 1

    最后,我们创建一个网格,并将BufferGeometry和材质传递给Mesh对象:

    const plane = new THREE.Mesh(geometry, material);
    
    // 将网格添加到场景中
    scene.add(plane);
    
    • 1
    • 2
    • 3
    • 4

    这个示例展示了如何在BufferGeometry中设置UV坐标。我们首先创建了一个包含四个顶点的平面。接着,我们为每个顶点分配了一个二维UV坐标,使纹理能够均匀地映射到平面上。最后,我们使用纹理创建了一个材质,并将其应用到了平面上。

    二、法向量属性应用与法向量辅助器

    在这里插入图片描述
    垂直面的线就是法向量

    // 导入threejs
    import * as THREE from "three";
    // 导入轨道控制器
    import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
    // 导入lil.gui
    import { GUI } from "three/examples/jsm/libs/lil-gui.module.min.js";
    // 导入hdr加载器
    import { RGBELoader } from "three/examples/jsm/loaders/RGBELoader.js";
    // 导入顶点法向量辅助器
    import { VertexNormalsHelper } from "three/examples/jsm/helpers/VertexNormalsHelper.js";
    // 创建场景
    const scene = new THREE.Scene();
    
    // 创建相机
    const camera = new THREE.PerspectiveCamera(
      45, // 视角
      window.innerWidth / window.innerHeight, // 宽高比
      0.1, // 近平面
      1000 // 远平面
    );
    
    // 创建渲染器
    const renderer = new THREE.WebGLRenderer();
    renderer.setSize(window.innerWidth, window.innerHeight);
    document.body.appendChild(renderer.domElement);
    
    let uvTexture = new THREE.TextureLoader().load("./texture/uv_grid_opengl.jpg");
    
    // // 创建平面几何体
    const planeGeometry = new THREE.PlaneGeometry(2, 2);
    console.log(planeGeometry);
    // // 创建材质
    const planeMaterial = new THREE.MeshBasicMaterial({
      map: uvTexture,
    });
    // // 创建平面
    const planeMesh = new THREE.Mesh(planeGeometry, planeMaterial);
    // // 添加到场景
    scene.add(planeMesh);
    planeMesh.position.x = -3;
    
    // 创建几何体
    const geometry = new THREE.BufferGeometry();
    // 创建顶点数据,顶点是有序的,每三个为一个顶点,逆时针为正面
    // const vertices = new Float32Array([
    //   -1.0, -1.0, 0.0, 1.0, -1.0, 0.0, 1.0, 1.0, 0.0,
    
    //   1.0, 1.0, 0, -1.0, 1.0, 0, -1.0, -1.0, 0,
    // ]);
    // // 创建顶点属性
    // geometry.setAttribute("position", new THREE.BufferAttribute(vertices, 3));
    
    // 使用索引绘制
    const vertices = new Float32Array([
      -1.0, -1.0, 0.0, 1.0, -1.0, 0.0, 1.0, 1.0, 0.0, -1.0, 1.0, 0,
    ]);
    // 创建顶点属性
    geometry.setAttribute("position", new THREE.BufferAttribute(vertices, 3));
    // 创建索引
    const indices = new Uint16Array([0, 1, 2, 2, 3, 0]);
    // 创建索引属性
    geometry.setIndex(new THREE.BufferAttribute(indices, 1));
    
    // 设置uv坐标
    const uv = new Float32Array([
      0,
      0,
      1,
      0,
      1,
      1,
      0,
      1, // 正面
    ]);
    // 创建uv属性
    geometry.setAttribute("uv", new THREE.BufferAttribute(uv, 2));
    
    // 设置法向量
    const normals = new Float32Array([
      0,
      0,
      1,
      0,
      0,
      1,
      0,
      0,
      1,
      0,
      0,
      1, // 正面
    ]);
    // 创建法向量属性
    geometry.setAttribute("normal", new THREE.BufferAttribute(normals, 3));
    
    // 计算出法向量
    // geometry.computeVertexNormals();
    
    console.log(geometry);
    // 创建材质
    const material = new THREE.MeshBasicMaterial({
      map: uvTexture,
    });
    const plane = new THREE.Mesh(geometry, material);
    scene.add(plane);
    plane.position.x = 3;
    
    // 创建法向量辅助器
    const helper = new VertexNormalsHelper(plane, 0.2, 0xff0000);
    scene.add(helper);
    
    // 设置相机位置
    camera.position.z = 5;
    camera.position.y = 2;
    camera.position.x = 2;
    camera.lookAt(0, 0, 0);
    
    // 添加世界坐标辅助器
    const axesHelper = new THREE.AxesHelper(5);
    scene.add(axesHelper);
    
    // 添加轨道控制器
    const controls = new OrbitControls(camera, renderer.domElement);
    // 设置带阻尼的惯性
    controls.enableDamping = true;
    // 设置阻尼系数
    controls.dampingFactor = 0.05;
    // 设置旋转速度
    // controls.autoRotate = true;
    
    // 渲染函数
    function animate() {
      controls.update();
      requestAnimationFrame(animate);
      // 渲染
      renderer.render(scene, camera);
    }
    animate();
    
    // 监听窗口变化
    window.addEventListener("resize", () => {
      // 重置渲染器宽高比
      renderer.setSize(window.innerWidth, window.innerHeight);
      // 重置相机宽高比
      camera.aspect = window.innerWidth / window.innerHeight;
      // 更新相机投影矩阵
      camera.updateProjectionMatrix();
    });
    
    let eventObj = {
      Fullscreen: function () {
        // 全屏
        document.body.requestFullscreen();
        console.log("全屏");
      },
      ExitFullscreen: function () {
        document.exitFullscreen();
        console.log("退出全屏");
      },
    };
    
    // 创建GUI
    const gui = new GUI();
    // 添加按钮
    gui.add(eventObj, "Fullscreen").name("全屏");
    gui.add(eventObj, "ExitFullscreen").name("退出全屏");
    // 控制立方体的位置
    // gui.add(cube.position, "x", -5, 5).name("立方体x轴位置");
    
    // rgbeLoader 加载hdr贴图
    let rgbeLoader = new RGBELoader();
    rgbeLoader.load("./texture/Alex_Hart-Nature_Lab_Bones_2k.hdr", (envMap) => {
      // 设置球形贴图
      envMap.mapping = THREE.EquirectangularReflectionMapping;
      // 设置环境贴图
      scene.background = envMap;
      // 设置环境贴图
      scene.environment = envMap;
      // 设置plane的环境贴图
      planeMaterial.envMap = envMap;
      // 设置plane的环境贴图
      material.envMap = envMap;
    });
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184

    三、几何定点转化-定点位移、旋转、缩放

    1. 使用Geometry方法的示例

    首先,我们创建一个简单的立方体Geometry,并应用一些变换:

    const geometry = new THREE.BoxGeometry(1, 1, 1);
    
    // 使用Geometry的方法进行变换
    geometry.rotateX(Math.PI / 4); // 沿X轴旋转45度
    geometry.rotateY(Math.PI / 6); // 沿Y轴旋转30度
    geometry.scale(2, 2, 2);       // 将立方体沿X、Y、Z轴缩放2倍
    geometry.translate(1, 1, 1);   // 将立方体沿X、Y、Z轴平移1个单位
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    现在,我们为立方体创建一个基本材质,并将Geometry和材质传递给Mesh对象:

    const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
    const cube = new THREE.Mesh(geometry, material);
    
    // 将网格添加到场景中
    scene.add(cube);
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    ● rotateX、rotateY、rotateZ:这些方法用于分别沿X、Y、Z轴旋转几何体。它们接受一个弧度值作为参数,表示旋转的角度。通过调用这些方法,我们可以更改几何体的方向。
    ● scale:此方法用于缩放几何体。它接受三个参数:x、y和z,分别表示沿X、Y、Z轴的缩放系数。通过调用此方法,我们可以更改几何体的大小。
    ● translate:此方法用于平移几何体。它接受三个参数:x、y和z,分别表示沿X、Y、Z轴的平移距离。通过调用此方法,我们可以更改几何体的位置。
    需要注意的是,这些方法直接修改几何体的顶点数据,因此会影响所有使用该几何体的网格。

    1. 使用Object3D属性的示例

    我们将创建另一个立方体,并使用Object3D的属性应用类似的变换:

    const geometry2 = new THREE.BoxGeometry(1, 1, 1);
    const material2 = new THREE.MeshBasicMaterial({ color: 0xff0000 });
    const cube2 = new THREE.Mesh(geometry2, material2);
    
    // 使用Object3D属性进行变换
    cube2.rotation.x = Math.PI / 4; // 沿X轴旋转45度
    cube2.rotation.y = Math.PI / 6; // 沿Y轴旋转30度
    cube2.scale.set(2, 2, 2);       // 将立方体沿X、Y、Z轴缩放2倍
    cube2.position.set(3, 3, 3);    // 将立方体沿X、Y、Z轴平移1个单位
    
    // 将网格添加到场景中
    scene.add(cube2);
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    1. Geometry与Object3D方法的区别

    以下是Geometry中的方法与Object3D属性之间的主要区别:

    • Geometry中的方法直接修改几何体的顶点数据,而Object3D的属性仅影响对象在场景中的变换。因此,更改Geometry中的方法会影响所有使用该几何体的网格,而更改Object3D属性不会影响其他使用相同几何体的对象。

    • Geometry中的方法更适合在创建时对几何体进行一次性的修改,而Object3D的属性更适合在实时渲染过程中对场景中的对象进行动态变换。

    • 在性能方面,更改Object3D属性通常比修改Geometry中的顶点数据更高效,因为后者涉及到重新计算顶点数据。因此,在实际应用中,优先考虑使用Object3D的属性进行变换。

    四、包围盒使用与世界矩阵转换

    在这里插入图片描述
    用途:检测碰撞。

    // 导入threejs
    import * as THREE from "three";
    // 导入轨道控制器
    import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
    // 导入lil.gui
    import { GUI } from "three/examples/jsm/libs/lil-gui.module.min.js";
    // 导入hdr加载器
    import { RGBELoader } from "three/examples/jsm/loaders/RGBELoader.js";
    // 导入顶点法向量辅助器
    import { VertexNormalsHelper } from "three/examples/jsm/helpers/VertexNormalsHelper.js";
    // 导入gltf加载器
    import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
    // 导入draco解码器
    import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader.js";
    // 创建场景
    const scene = new THREE.Scene();
    
    // 创建相机
    const camera = new THREE.PerspectiveCamera(
      45, // 视角
      window.innerWidth / window.innerHeight, // 宽高比
      0.1, // 近平面
      1000 // 远平面
    );
    
    // 创建渲染器
    const renderer = new THREE.WebGLRenderer();
    renderer.setSize(window.innerWidth, window.innerHeight);
    document.body.appendChild(renderer.domElement);
    
    // 设置相机位置
    camera.position.z = 5;
    camera.position.y = 2;
    camera.position.x = 2;
    camera.lookAt(0, 0, 0);
    
    // 添加世界坐标辅助器
    const axesHelper = new THREE.AxesHelper(5);
    scene.add(axesHelper);
    
    // 添加轨道控制器
    const controls = new OrbitControls(camera, renderer.domElement);
    // 设置带阻尼的惯性
    controls.enableDamping = true;
    // 设置阻尼系数
    controls.dampingFactor = 0.05;
    // 设置旋转速度
    // controls.autoRotate = true;
    
    // 渲染函数
    function animate() {
      controls.update();
      requestAnimationFrame(animate);
      // 渲染
      renderer.render(scene, camera);
    }
    animate();
    
    // 监听窗口变化
    window.addEventListener("resize", () => {
      // 重置渲染器宽高比
      renderer.setSize(window.innerWidth, window.innerHeight);
      // 重置相机宽高比
      camera.aspect = window.innerWidth / window.innerHeight;
      // 更新相机投影矩阵
      camera.updateProjectionMatrix();
    });
    
    let eventObj = {
      Fullscreen: function () {
        // 全屏
        document.body.requestFullscreen();
        console.log("全屏");
      },
      ExitFullscreen: function () {
        document.exitFullscreen();
        console.log("退出全屏");
      },
    };
    
    // 创建GUI
    const gui = new GUI();
    // 添加按钮
    gui.add(eventObj, "Fullscreen").name("全屏");
    gui.add(eventObj, "ExitFullscreen").name("退出全屏");
    // 控制立方体的位置
    // gui.add(cube.position, "x", -5, 5).name("立方体x轴位置");
    
    // rgbeLoader 加载hdr贴图
    let rgbeLoader = new RGBELoader();
    rgbeLoader.load("./texture/Alex_Hart-Nature_Lab_Bones_2k.hdr", (envMap) => {
      // 设置球形贴图
      envMap.mapping = THREE.EquirectangularReflectionMapping;
      // 设置环境贴图
      scene.background = envMap;
      // 设置环境贴图
      scene.environment = envMap;
    });
    
    // 实例化加载器gltf
    const gltfLoader = new GLTFLoader();
    // 加载模型
    gltfLoader.load(
      // 模型路径
      "./model/Duck.glb",
      // 加载完成回调
      (gltf) => {
        console.log(gltf);
        scene.add(gltf.scene);
    
        let duckMesh = gltf.scene.getObjectByName("LOD3spShape");
        let duckGeometry = duckMesh.geometry;
    
        // 计算包围盒
        duckGeometry.computeBoundingBox();
        // 设置几何体居中
        // duckGeometry.center();
        // 获取duck包围盒
        let duckBox = duckGeometry.boundingBox;
    
        // 更新世界矩阵
        duckMesh.updateWorldMatrix(true, true);
        // 更新包围盒
        duckBox.applyMatrix4(duckMesh.matrixWorld);
        // 获取包围盒中心点
        let center = duckBox.getCenter(new THREE.Vector3());
        console.log(center);
        // 创建包围盒辅助器
        let boxHelper = new THREE.Box3Helper(duckBox, 0xffff00);
        // 添加包围盒辅助器
        scene.add(boxHelper);
        console.log(duckBox);
        console.log(duckMesh);
    
        // 获取包围球
        let duckSphere = duckGeometry.boundingSphere;
        duckSphere.applyMatrix4(duckMesh.matrixWorld);
    
        console.log(duckSphere);
        // 创建包围球辅助器
        let sphereGeometry = new THREE.SphereGeometry(duckSphere.radius, 16, 16);
        let sphereMaterial = new THREE.MeshBasicMaterial({
          color: 0xff0000,
          wireframe: true,
        });
        let sphereMesh = new THREE.Mesh(sphereGeometry, sphereMaterial);
        sphereMesh.position.copy(duckSphere.center);
        scene.add(sphereMesh);
      }
    );
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151

    五、几何体居中与获取几何体中心

    // 导入threejs
    import * as THREE from "three";
    // 导入轨道控制器
    import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
    // 导入lil.gui
    import { GUI } from "three/examples/jsm/libs/lil-gui.module.min.js";
    // 导入hdr加载器
    import { RGBELoader } from "three/examples/jsm/loaders/RGBELoader.js";
    // 导入顶点法向量辅助器
    import { VertexNormalsHelper } from "three/examples/jsm/helpers/VertexNormalsHelper.js";
    // 导入gltf加载器
    import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
    // 导入draco解码器
    import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader.js";
    // 创建场景
    const scene = new THREE.Scene();
    
    // 创建相机
    const camera = new THREE.PerspectiveCamera(
      45, // 视角
      window.innerWidth / window.innerHeight, // 宽高比
      0.1, // 近平面
      1000 // 远平面
    );
    
    // 创建渲染器
    const renderer = new THREE.WebGLRenderer();
    renderer.setSize(window.innerWidth, window.innerHeight);
    document.body.appendChild(renderer.domElement);
    
    // 设置相机位置
    camera.position.z = 5;
    camera.position.y = 2;
    camera.position.x = 2;
    camera.lookAt(0, 0, 0);
    
    // 添加世界坐标辅助器
    const axesHelper = new THREE.AxesHelper(5);
    scene.add(axesHelper);
    
    // 添加轨道控制器
    const controls = new OrbitControls(camera, renderer.domElement);
    // 设置带阻尼的惯性
    controls.enableDamping = true;
    // 设置阻尼系数
    controls.dampingFactor = 0.05;
    // 设置旋转速度
    // controls.autoRotate = true;
    
    // 渲染函数
    function animate() {
      controls.update();
      requestAnimationFrame(animate);
      // 渲染
      renderer.render(scene, camera);
    }
    animate();
    
    // 监听窗口变化
    window.addEventListener("resize", () => {
      // 重置渲染器宽高比
      renderer.setSize(window.innerWidth, window.innerHeight);
      // 重置相机宽高比
      camera.aspect = window.innerWidth / window.innerHeight;
      // 更新相机投影矩阵
      camera.updateProjectionMatrix();
    });
    
    let eventObj = {
      Fullscreen: function () {
        // 全屏
        document.body.requestFullscreen();
        console.log("全屏");
      },
      ExitFullscreen: function () {
        document.exitFullscreen();
        console.log("退出全屏");
      },
    };
    
    // 创建GUI
    const gui = new GUI();
    // 添加按钮
    gui.add(eventObj, "Fullscreen").name("全屏");
    gui.add(eventObj, "ExitFullscreen").name("退出全屏");
    // 控制立方体的位置
    // gui.add(cube.position, "x", -5, 5).name("立方体x轴位置");
    
    // rgbeLoader 加载hdr贴图
    let rgbeLoader = new RGBELoader();
    rgbeLoader.load("./texture/Alex_Hart-Nature_Lab_Bones_2k.hdr", (envMap) => {
      // 设置球形贴图
      envMap.mapping = THREE.EquirectangularReflectionMapping;
      // 设置环境贴图
      scene.background = envMap;
      // 设置环境贴图
      scene.environment = envMap;
    });
    
    // 实例化加载器gltf
    const gltfLoader = new GLTFLoader();
    // 加载模型
    gltfLoader.load(
      // 模型路径
      "./model/Duck.glb",
      // 加载完成回调
      (gltf) => {
        console.log(gltf);
        scene.add(gltf.scene);
    
        let duckMesh = gltf.scene.getObjectByName("LOD3spShape");
        let duckGeometry = duckMesh.geometry;
    
        // 计算包围盒
        duckGeometry.computeBoundingBox();
        // 设置几何体居中
        // duckGeometry.center();
        // 获取duck包围盒
        let duckBox = duckGeometry.boundingBox;
    
        // 更新世界矩阵
        duckMesh.updateWorldMatrix(true, true);
        // 更新包围盒
        duckBox.applyMatrix4(duckMesh.matrixWorld);
        // 获取包围盒中心点
        let center = duckBox.getCenter(new THREE.Vector3());
        console.log(center);
        // 创建包围盒辅助器
        let boxHelper = new THREE.Box3Helper(duckBox, 0xffff00);
        // 添加包围盒辅助器
        scene.add(boxHelper);
        console.log(duckBox);
        console.log(duckMesh);
    
        // 获取包围球
        let duckSphere = duckGeometry.boundingSphere;
        duckSphere.applyMatrix4(duckMesh.matrixWorld);
    
        console.log(duckSphere);
        // 创建包围球辅助器
        let sphereGeometry = new THREE.SphereGeometry(duckSphere.radius, 16, 16);
        let sphereMaterial = new THREE.MeshBasicMaterial({
          color: 0xff0000,
          wireframe: true,
        });
        let sphereMesh = new THREE.Mesh(sphereGeometry, sphereMaterial);
        sphereMesh.position.copy(duckSphere.center);
        scene.add(sphereMesh);
      }
    );
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151

    六、获取多个物体的包围盒

    // 导入threejs
    import * as THREE from "three";
    // 导入轨道控制器
    import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
    // 导入lil.gui
    import { GUI } from "three/examples/jsm/libs/lil-gui.module.min.js";
    // 导入hdr加载器
    import { RGBELoader } from "three/examples/jsm/loaders/RGBELoader.js";
    // 导入顶点法向量辅助器
    import { VertexNormalsHelper } from "three/examples/jsm/helpers/VertexNormalsHelper.js";
    // 导入gltf加载器
    import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
    // 导入draco解码器
    import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader.js";
    // 创建场景
    const scene = new THREE.Scene();
    
    // 创建相机
    const camera = new THREE.PerspectiveCamera(
      45, // 视角
      window.innerWidth / window.innerHeight, // 宽高比
      0.1, // 近平面
      1000 // 远平面
    );
    
    // 创建渲染器
    const renderer = new THREE.WebGLRenderer();
    renderer.setSize(window.innerWidth, window.innerHeight);
    document.body.appendChild(renderer.domElement);
    
    // 设置相机位置
    camera.position.z = 5;
    camera.position.y = 2;
    camera.position.x = 2;
    camera.lookAt(0, 0, 0);
    
    // 添加世界坐标辅助器
    const axesHelper = new THREE.AxesHelper(5);
    scene.add(axesHelper);
    
    // 添加轨道控制器
    const controls = new OrbitControls(camera, renderer.domElement);
    // 设置带阻尼的惯性
    controls.enableDamping = true;
    // 设置阻尼系数
    controls.dampingFactor = 0.05;
    // 设置旋转速度
    // controls.autoRotate = true;
    
    // 渲染函数
    function animate() {
      controls.update();
      requestAnimationFrame(animate);
      // 渲染
      renderer.render(scene, camera);
    }
    animate();
    
    // 监听窗口变化
    window.addEventListener("resize", () => {
      // 重置渲染器宽高比
      renderer.setSize(window.innerWidth, window.innerHeight);
      // 重置相机宽高比
      camera.aspect = window.innerWidth / window.innerHeight;
      // 更新相机投影矩阵
      camera.updateProjectionMatrix();
    });
    
    let eventObj = {
      Fullscreen: function () {
        // 全屏
        document.body.requestFullscreen();
        console.log("全屏");
      },
      ExitFullscreen: function () {
        document.exitFullscreen();
        console.log("退出全屏");
      },
    };
    
    // 创建GUI
    const gui = new GUI();
    // 添加按钮
    gui.add(eventObj, "Fullscreen").name("全屏");
    gui.add(eventObj, "ExitFullscreen").name("退出全屏");
    // 控制立方体的位置
    // gui.add(cube.position, "x", -5, 5).name("立方体x轴位置");
    
    // rgbeLoader 加载hdr贴图
    let rgbeLoader = new RGBELoader();
    rgbeLoader.load("./texture/Alex_Hart-Nature_Lab_Bones_2k.hdr", (envMap) => {
      // 设置球形贴图
      envMap.mapping = THREE.EquirectangularReflectionMapping;
      // 设置环境贴图
      scene.background = envMap;
      // 设置环境贴图
      scene.environment = envMap;
    });
    
    // 实例化加载器gltf
    const gltfLoader = new GLTFLoader();
    // 加载模型
    gltfLoader.load(
      // 模型路径
      "./model/Duck.glb",
      // 加载完成回调
      (gltf) => {
        console.log(gltf);
        scene.add(gltf.scene);
    
        let duckMesh = gltf.scene.getObjectByName("LOD3spShape");
        let duckGeometry = duckMesh.geometry;
    
        // 计算包围盒
        duckGeometry.computeBoundingBox();
        // 设置几何体居中
        // duckGeometry.center();
        // 获取duck包围盒
        let duckBox = duckGeometry.boundingBox;
    
        // 更新世界矩阵
        duckMesh.updateWorldMatrix(true, true);
        // 更新包围盒
        duckBox.applyMatrix4(duckMesh.matrixWorld);
        // 获取包围盒中心点
        let center = duckBox.getCenter(new THREE.Vector3());
        console.log(center);
        // 创建包围盒辅助器
        let boxHelper = new THREE.Box3Helper(duckBox, 0xffff00);
        // 添加包围盒辅助器
        scene.add(boxHelper);
        console.log(duckBox);
        console.log(duckMesh);
    
        // 获取包围球
        let duckSphere = duckGeometry.boundingSphere;
        duckSphere.applyMatrix4(duckMesh.matrixWorld);
    
        console.log(duckSphere);
        // 创建包围球辅助器
        let sphereGeometry = new THREE.SphereGeometry(duckSphere.radius, 16, 16);
        let sphereMaterial = new THREE.MeshBasicMaterial({
          color: 0xff0000,
          wireframe: true,
        });
        let sphereMesh = new THREE.Mesh(sphereGeometry, sphereMaterial);
        sphereMesh.position.copy(duckSphere.center);
        scene.add(sphereMesh);
      }
    );
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151

    七、边缘几何体和线框几何体

    在这里插入图片描述
    在这里插入图片描述

    // 导入threejs
    import * as THREE from "three";
    // 导入轨道控制器
    import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
    // 导入lil.gui
    import { GUI } from "three/examples/jsm/libs/lil-gui.module.min.js";
    // 导入hdr加载器
    import { RGBELoader } from "three/examples/jsm/loaders/RGBELoader.js";
    // 导入顶点法向量辅助器
    import { VertexNormalsHelper } from "three/examples/jsm/helpers/VertexNormalsHelper.js";
    // 导入gltf加载器
    import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
    // 导入draco解码器
    import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader.js";
    // 创建场景
    const scene = new THREE.Scene();
    
    // 创建相机
    const camera = new THREE.PerspectiveCamera(
      45, // 视角
      window.innerWidth / window.innerHeight, // 宽高比
      0.1, // 近平面
      1000 // 远平面
    );
    
    // 创建渲染器
    const renderer = new THREE.WebGLRenderer();
    renderer.setSize(window.innerWidth, window.innerHeight);
    document.body.appendChild(renderer.domElement);
    
    // 设置相机位置
    camera.position.z = 5;
    camera.position.y = 2;
    camera.position.x = 2;
    camera.lookAt(0, 0, 0);
    
    // 添加世界坐标辅助器
    const axesHelper = new THREE.AxesHelper(5);
    scene.add(axesHelper);
    
    // 添加轨道控制器
    const controls = new OrbitControls(camera, renderer.domElement);
    // 设置带阻尼的惯性
    controls.enableDamping = true;
    // 设置阻尼系数
    controls.dampingFactor = 0.05;
    // 设置旋转速度
    // controls.autoRotate = true;
    
    // 渲染函数
    function animate() {
      controls.update();
      requestAnimationFrame(animate);
      // 渲染
      renderer.render(scene, camera);
    }
    animate();
    
    // 监听窗口变化
    window.addEventListener("resize", () => {
      // 重置渲染器宽高比
      renderer.setSize(window.innerWidth, window.innerHeight);
      // 重置相机宽高比
      camera.aspect = window.innerWidth / window.innerHeight;
      // 更新相机投影矩阵
      camera.updateProjectionMatrix();
    });
    
    let eventObj = {
      Fullscreen: function () {
        // 全屏
        document.body.requestFullscreen();
        console.log("全屏");
      },
      ExitFullscreen: function () {
        document.exitFullscreen();
        console.log("退出全屏");
      },
    };
    
    // 创建GUI
    const gui = new GUI();
    // 添加按钮
    gui.add(eventObj, "Fullscreen").name("全屏");
    gui.add(eventObj, "ExitFullscreen").name("退出全屏");
    // 控制立方体的位置
    // gui.add(cube.position, "x", -5, 5).name("立方体x轴位置");
    
    // rgbeLoader 加载hdr贴图
    let rgbeLoader = new RGBELoader();
    rgbeLoader.load("./texture/Alex_Hart-Nature_Lab_Bones_2k.hdr", (envMap) => {
      // 设置球形贴图
      envMap.mapping = THREE.EquirectangularReflectionMapping;
      // 设置环境贴图
      // scene.background = envMap;
      // 设置环境贴图
      scene.environment = envMap;
    });
    // 实例化加载器gltf
    const gltfLoader = new GLTFLoader();
    // 实例化加载器draco
    const dracoLoader = new DRACOLoader();
    // 设置draco路径
    dracoLoader.setDecoderPath("./draco/");
    // 设置gltf加载器draco解码器
    gltfLoader.setDRACOLoader(dracoLoader);
    
    // 加载模型
    // gltfLoader.load(
    //   // 模型路径
    //   "./model/building.glb",
    //   // 加载完成回调
    //   (gltf) => {
    //     // console.log(gltf);
    //     // scene.add(gltf.scene);
    //     let building = gltf.scene.children[0];
    //     let geometry = building.geometry;
    
    //     // 获取边缘geometry
    //     // let edgesGeometry = new THREE.EdgesGeometry(geometry);
    //     // // 创建线段材质
    //     // let edgesMaterial = new THREE.LineBasicMaterial({
    //     //   color: 0xffffff,
    //     // });
    
    //     // 线框geometry
    //     let edgesGeometry = new THREE.WireframeGeometry(geometry);
    //     // 创建线段
    //     let edges = new THREE.LineSegments(edgesGeometry, edgesMaterial);
    
    //     // 更新建筑物世界转换矩阵
    //     building.updateWorldMatrix(true, true);
    //     edges.matrix.copy(building.matrixWorld);
    //     edges.matrix.decompose(edges.position, edges.quaternion, edges.scale);
    
    //     // 添加到场景
    //     scene.add(edges);
    //   }
    // );
    
    gltfLoader.load(
      // 模型路径
      "./model/city.glb",
      // 加载完成回调
      (gltf) => {
        // console.log(gltf);
        // scene.add(gltf.scene);
       // 遍历所有元素
        gltf.scene.traverse((child) => {
          if (child.isMesh) {
            let building = child;
            let geometry = building.geometry;
    
            // 获取边缘geometry
            let edgesGeometry = new THREE.EdgesGeometry(geometry);
            // // 创建线段材质
            let edgesMaterial = new THREE.LineBasicMaterial({
              color: 0xffffff,
            });
    
            // 线框geometry
            // let edgesGeometry = new THREE.WireframeGeometry(geometry);
            // 创建线段
            let edges = new THREE.LineSegments(edgesGeometry, edgesMaterial);
    
            // 更新建筑物世界转换矩阵
            building.updateWorldMatrix(true, true);
            edges.matrix.copy(building.matrixWorld);
            // 解构
            edges.matrix.decompose(edges.position, edges.quaternion, edges.scale);
    
            // 添加到场景
            scene.add(edges);
          }
        });
      }
    );
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
  • 相关阅读:
    视频讲解vue2基础之style样式class类名绑定
    如何使用.NET在2.2秒内处理10亿行数据(1brc挑战)
    DigestUtils实现md5加密算法
    02_SpringMVC从0开始框架搭建
    原型和原型链
    如何使用gpt提高效率
    【机器学习】使用scikitLearn进行SVM支持向量机线性分类
    Google-Spyder:Google搜索引擎关键词检索结果抓取
    重温mybatis之一篇带你入门Mybatis
    创建域名邮箱邮件地址的方法与步骤
  • 原文地址:https://blog.csdn.net/woyebuzhidao321/article/details/133798111