threejs(2)-Geometry进阶详解

一、全面讲解UV与应用

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

颜色对应
threejs(2)-Geometry进阶详解_第1张图片
threejs(2)-Geometry进阶详解_第2张图片
threejs(2)-Geometry进阶详解_第3张图片

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

接着,我们创建一个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));

然后,我们为每个顶点设置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));

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

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

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

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

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

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

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

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

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

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

threejs(2)-Geometry进阶详解_第4张图片
垂直面的线就是法向量

// 导入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. 使用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个单位

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

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

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

● 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. Geometry与Object3D方法的区别

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

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

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

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

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

threejs(2)-Geometry进阶详解_第5张图片
用途:检测碰撞。

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

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

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

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

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

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

threejs(2)-Geometry进阶详解_第6张图片

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

你可能感兴趣的:(3d)