threejs(2)-Geometry进阶详解

news2024/11/18 19:28:12

一、全面讲解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');

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1109838.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【根据车间号[81321000]未找到ERP逻辑仓】

以条码Z42310062781622举例,WMS集成报错。 先说业务逻辑: 新建包装工单,维护包装批次管制时,会把包装批次管制里的部门信息传给115。 赛龙捷包装后,会根据115里的这个部门对应的车间号,返还给MES。 MES会…

程序连接oracle查询数据的环境配置

连接oracle 数据库真麻烦,还是MySQL方便 Oracle Instant Client 这个东西的版本跟oracle的版本是有讲究的,引用文档的说明 Oracle 标准的客户端-服务器网络互操作性允许不同版本的 Oracle 客户端和 Oracle 数据库之间的连接。有关经过认证的配置&#…

springboot之quartz动态可控定时任务

Quartz Quartz是一个开源的任务调度框架,可以用来实现定时任务的调度,如定时发送邮件、定时备份数据等。Quartz具有很高的可靠性和灵活性,支持集群部署和分布式调度,并且提供了丰富的API和插件,可以轻松实现复杂的调度…

2023年中国GPS导航设备产量、销量及市场规模分析[图]

GPS导航设备行业是指生产和销售用于导航、定位和监控目的的GPS设备的行业,可以用于汽车、船只、飞机、人员和其他物体的定位和导航,以及用于地理信息系统(GIS)、测绘、海洋抢险、森林监测、地质勘探、气象预报、交通管理、物流跟踪…

中国地级市-环境规制18个相关指标数据(2002-2021年)

本次数据为地级市-环境规制相关指标,主要用于计算城市的环境规制、污染排放水平。指标数目总计18个,数据来源为《中国城市统计年鉴》,存在一定缺失。数据包含原始、线性插值、回归填补3个版本,希望对大家有用 一、数据介绍 数据…

uni-app:js二维数组与对象数组之间的转换

一、二维数组整理成对象数组 效果 [ ["前绿箭","DI10","RO1"], ["前红叉","DI2","RO2"], ["后绿箭","DI12","RO3"], ["后红叉","DI4","RO6"] ] …

2023国考证件照要求什么底色?证件照换背景底色的方法

2023年国家公务员考试报名已经开始了,我们在考试平台提交报名信息的时候,有一项就是需要上传证件照片,对于证件照片也会有具体的要求,比如背景底色、尺寸大小、dpi和kb大小。今天就为大家详细介绍一下关于国考证件照背景色的内容&…

数据结构与算法课后题-第六章(图的存储及基本操作)

文章目录 1、选择题12、选择题23、选择题34、选择题45、选择题56、选择题67、选择题78、选择题89、选择题910、选择题1011、选择题1112、选择题1214、选择题1415、选择题1516、选择题16 1、选择题1 2、选择题2 3、选择题3 4、选择题4 5、选择题5 6、选择题6 7、选择题7 8、选择…

zzy-project-cli,提供多个框架的脚手架

npm地址 install npm install zzy-project-cli -g做什么? 将多个可选的框架提供给使用者选择,选中后自动下载对应模板,快捷使用。 使用 step1 zzy-cli create [项目名称]step2 获取模板之后选取任一进行下载 下载完成之后即可使用 模…

藏在超级应用背后的逻辑和哲学

众所周知,Elon Musk 想将 Twitter 重新设计定位成一款“超级应用 - X”的野心已经不再是秘密。伴随着应用商店中 Twitter 标志性的蓝鸟 Logo 被 X 取代后,赛博世界充满了对这件事情各种角度的探讨与分析。 Musk 曾经无数次通过微信这一样本来推广他的“超…

龙芯3A3000源码编译安装deepin-ide

安装环境 系统为统领专业版1050 CPU为龙芯3A3000 安装步骤 1.安装所有依赖库 sudo apt-get install git debhelper cmake qt5-qmake qtbase5-dev qttools5-dev qttools5-dev-tools lxqt-build-tools libssl-dev llvm llvm-dev libclang-dev libutf8proc-dev libmicrohttpd-d…

利用MixProxy自动录制生成Pytest案例:轻松实现测试脚本编写!

前言 进行接口自动化时,有时候往往没有接口文档,或者文档更新并不及时,此时,想要获取相关接口,通过抓包是一种快速便捷的手段。抓包获取到接口后,开始写接口用例,此时需要复制请求url、请求参数…

人工智能驱动的个性化学习:技术如何彻底改变教育

随着计算机辅助教学的出现,人工智能在教育领域的发展始于20世纪50年代。然而,在20世纪90年代,由于机器学习和数据处理的进步,该领域开始出现大幅增长。人工智能在教育领域的早期应用之一是智能辅导系统(ITS&#xff09…

最新最全网络安全专业毕业设计选题精华汇总-持续更新中

文章目录 0 前言1 网络安全(信息安全)毕设选题推荐2 开题指导3 最后 0 前言 Hi,大家好,随着毕业季的临近,许多同学开始向学长咨询关于选题和开题的问题。在这里,学长分享一些关于网络安全(信息安全)毕业设计选题的内容。 以下为…

2023人工智能全景报告《State of AI Report》出炉!AI未来一年的10大预测:GPT-4仍是全球最强,GenAI 大爆发,...

文章目录 2023 人工智能全景报告《State of AI Report》出炉!给出AI未来一年的10大预测,GPT-4仍是全球最强,GenAI 大爆发,...1. 研究进展1.1 GPT-4仍是全球最强1.2 闭源模型趋于技术封闭,开源模型紧追不舍1.3 小模型的…

日常中msvcr120.dll丢失怎么解决?有效的5个解决方法分享

在我日常的计算机维护和故障排除中,我经常会遇到一些常见的问题,其中之一就是“msvcr120.dll丢失”。这是一个非常常见的错误,通常出现在运行某些程序或游戏时。这个问题可能会对用户的电脑操作造成不便,甚至导致一些重要的应用程…

风力发电场安科瑞集中监控系统解决方案

安科瑞 崔丽洁 风力发电场集中监控系统解决方案 作为清洁能源之一,风力发电场近几年装机容量快速增长。8月17日,国家能源局发布1-7月份全国电力工业统计数据。截至7月底,全国累计发电装机容量约27.4亿千瓦,同比增长11.5%。其中&am…

1024程序员节特辑 | ELK+ 用户画像构建个性化推荐引擎,智能实现“千人千面”

专栏集锦,赶紧收藏以备不时之需 Spring Cloud实战专栏:https://blog.csdn.net/superdangbo/category_9270827.html Python 实战专栏:https://blog.csdn.net/superdangbo/category_9271194.html Logback 详解专栏:https://blog.…

Mac苹果电脑开不了机怎么办,该怎么修复

台式机Mac或MacBook无法打开,或者可能无法通过Apple图标启动?不用担心,虽然会让人烦躁不安,但通常是可以修复的。 以下就是重新启动Mac所需的所有步骤。只需按顺序进行操作即可,除非操作系统更新失败后Mac无法启动。在…

[1024]程序员节 一晃6年过去了

加入开发者大军,一晃已是6年有余,从最初的Andoird开发如火如荼,到现在的秋风萧瑟,宛如被秋风吹得只剩躯干的树木,等待来年的焕发新芽。 我本不是一个科班出身的开发者,但是为了生活,说白了为了钱…