threejs(2)-Geometry advanced detailed explanation

1. Comprehensive explanation of UV and applications

In this section, we will discuss UV mapping in Three.js, including the concept of UV mapping, its relationship and difference with vertex positions, and how to set UV coordinates in Geometry. We will use BufferGeometry for example.

Color correspondence
Insert image description here
Insert image description here
Insert image description here

  1. What is UV mapping?

UV mapping is a technique for mapping two-dimensional textures onto the surface of a three-dimensional model. In this process, each vertex on the 3D model will be assigned a two-dimensional coordinate (U, V). U and V represent the horizontal and vertical directions of texture coordinates respectively. These coordinates are used to correspond pixels on the texture image to points on the model surface. With UV mapping, we can precisely control the position and orientation of textures on the model.

  1. The relationship and difference between UV coordinates and vertex positions

Vertex position (Position) represents the spatial coordinates (x, y, z) of each vertex in the 3D model. UV coordinates represent the two-dimensional coordinates (U, V) of the vertex on the texture. Vertex positions are used to determine the shape of the model in the scene, while UV coordinates are used to determine the distribution of textures on the model.
The main differences between the two are:
● Vertex position is a three-dimensional coordinate, describing the position of a vertex in three-dimensional space.
● UV coordinates are two-dimensional coordinates that describe the position of a vertex on the texture image.

  1. Set UV coordinates

Below we will use an example to show how to set UV coordinates in BufferGeometry.
Let's say we want to create a textured plane. First, we need a texture image. We will use THREE.TextureLoader to load the texture:

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

Next, we create a BufferGeometry and set the vertex positions:

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));

Then, we set the UV coordinates for each vertex. In this example, we map the texture evenly onto four vertices:

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));

Next, we need to define the two triangles that make up the plane. To do this, we set the index properties:

const indices = new Uint16Array([
  0, 1, 2,
  2, 3, 0
]);

geometry.setIndex(new THREE.BufferAttribute(indices, 1));

Now we create a material and pass the previously loaded texture to the material:

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

Finally, we create a mesh and pass the BufferGeometry and material to the Mesh object:

const plane = new THREE.Mesh(geometry, material);

// 将网格添加到场景中
scene.add(plane);

This example shows how to set UV coordinates in a BufferGeometry. We first create a plane with four vertices. Next, we assigned each vertex a 2D UV coordinate so that the texture could be mapped evenly onto the plane. Finally, we created a material using the texture and applied it to the plane.

2. Application of normal vector attributes and normal vector auxiliary

Insert image description here
The line perpendicular to the plane is the normal vector

// 导入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;
});

3. Geometric fixed-point transformation-fixed-point displacement, rotation, scaling

  1. Example using Geometry method

First, we create a simple cube Geometry and apply some transformations:

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个单位

Now we create a basic material for the cube and pass the Geometry and material to the Mesh object:

const material = new THREE.MeshBasicMaterial({
    
     color: 0x00ff00 });
const cube = new THREE.Mesh(geometry, material);

// 将网格添加到场景中
scene.add(cube);

● rotateX, rotateY, rotateZ: These methods are used to rotate the geometry along the X, Y, and Z axes respectively. They accept a radian value as an argument, representing the angle of rotation. By calling these methods we can change the orientation of the geometry.
● scale: This method is used to scale the geometry. It accepts three parameters: x, y, and z, which represent the scaling coefficients along the X, Y, and Z axes respectively. By calling this method we can change the size of the geometry.
● translate: This method is used to translate the geometry. It accepts three parameters: x, y, and z, which represent the translation distance along the X, Y, and Z axes respectively. By calling this method we can change the position of the geometry.
It is important to note that these methods directly modify the vertex data of the geometry and therefore affect all meshes that use that geometry.

  1. Example of using Object3D properties

We will create another cube and apply a similar transformation using the properties of 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. The difference between Geometry and Object3D methods

The following are the main differences between methods in Geometry and Object3D properties:

  • The methods in Geometry directly modify the vertex data of the geometry, while the properties of Object3D only affect the transformation of the object in the scene. Therefore, changing a method in a Geometry affects all meshes that use that geometry, while changing an Object3D property does not affect other objects that use the same geometry.

  • The methods in Geometry are more suitable for one-time modifications to the geometry during creation, while the properties of Object3D are more suitable for dynamic transformation of objects in the scene during real-time rendering.

  • In terms of performance, changing Object3D properties is usually more efficient than modifying vertex data in a Geometry, since the latter involves recalculating vertex data. Therefore, in practical applications, priority is given to using the properties of Object3D for transformation.

4. Use of bounding boxes and world matrix conversion

Insert image description here
Purpose: Detect collisions.

// 导入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);
  }
);

5. Geometry centering and obtaining the center of the geometry

// 导入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);
  }
);

6. Obtain the bounding boxes of multiple objects

// 导入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);
  }
);

7. Edge geometry and wireframe geometry

Insert image description here
Insert image description here

// 导入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);
      }
    });
  }
);

Guess you like

Origin blog.csdn.net/woyebuzhidao321/article/details/133798111