Three.js Geometry进阶

概述

本文档将介绍Three.js中Geometry的进阶用法,包括UV映射、法向量、几何体变换、包围盒计算等重要概念。通过这些知识点,您将能够更好地控制和操作3D几何体。

第一部分:UV属性设置

1. UV映射的概念

UV映射是将2D纹理映射到3D几何体表面的过程。U和V分别对应纹理坐标的水平和垂直轴,与3D空间中的X、Y、Z轴相对应。

2. 基本UV设置

javascript 复制代码
// 创建几何体
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,
]);
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,   // 第一个顶点的UV坐标
  1, 0,   // 第二个顶点的UV坐标
  1, 1,   // 第三个顶点的UV坐标
  0, 1,   // 第四个顶点的UV坐标
]);
// 创建UV属性
geometry.setAttribute("uv", new THREE.BufferAttribute(uv, 2));

// 应用纹理
const texture = new THREE.TextureLoader().load("./texture/uv_grid_opengl.jpg");
const material = new THREE.MeshBasicMaterial({ map: texture });
const plane = new THREE.Mesh(geometry, material);

3. UV坐标详解

UV坐标系的原点(0,0)通常位于纹理的左下角,而(1,1)位于右上角。每个顶点都需要分配相应的UV坐标,以便正确映射纹理。

第二部分:法向量

1. 法向量的概念

法向量是垂直于表面的向量,用于确定光照计算、背面剔除等。每个顶点都可以有自己的法向量。

2. 手动设置法向量

javascript 复制代码
// 设置法向量
const normals = new Float32Array([
  0, 0, 1,  // 第一个顶点的法向量
  0, 0, 1,  // 第二个顶点的法向量
  0, 0, 1,  // 第三个顶点的法向量
  0, 0, 1,  // 第四个顶点的法向量
]);
// 创建法向量属性
geometry.setAttribute("normal", new THREE.BufferAttribute(normals, 3));

3. 自动计算法向量

javascript 复制代码
// 计算法向量
geometry.computeVertexNormals();

4. 显示法向量辅助器

javascript 复制代码
import { VertexNormalsHelper } from "three/examples/jsm/helpers/VertexNormalsHelper.js";

// 创建法向量辅助器
const helper = new VertexNormalsHelper(plane, 0.2, 0xff0000);
scene.add(helper);

第三部分:几何体顶点转换

1. 几何体变换方法

BufferGeometry提供了多种变换方法来修改顶点数据:

javascript 复制代码
// 平移几何体
geometry.translate(x, y, z);

// 旋转几何体
geometry.rotateX(Math.PI / 2);
geometry.rotateY(angle);
geometry.rotateZ(angle);

// 缩放几何体
geometry.scale(scaleX, scaleY, scaleZ);

2. 变换的顺序

变换的顺序很重要,通常是先缩放,再旋转,最后平移(SRT顺序)。

第四部分:几何体居中

1. 计算包围盒

javascript 复制代码
// 计算包围盒
geometry.computeBoundingBox();

// 获取包围盒
let boundingBox = geometry.boundingBox;

// 获取包围盒中心点
let center = boundingBox.getCenter(new THREE.Vector3());

2. 几何体居中

javascript 复制代码
// 将几何体居中
geometry.center();

3. 处理模型的世界变换

javascript 复制代码
// 对于复杂模型,需要考虑世界矩阵
mesh.updateWorldMatrix(true, true);
boundingBox.applyMatrix4(mesh.matrixWorld);
let center = boundingBox.getCenter(new THREE.Vector3());

第五部分:包围盒

1. 包围盒的概念

包围盒是包含几何体所有顶点的最小矩形框,常用于碰撞检测和视锥剔除。

2. 计算包围盒

javascript 复制代码
// 计算几何体的边界框
geometry.computeBoundingBox();

// 获取边界框
let boundingBox = geometry.boundingBox;
console.log(boundingBox);

3. 包围球

包围球是包含几何体所有顶点的最小球体:

javascript 复制代码
// 计算包围球
geometry.computeBoundingSphere();

// 获取包围球
let boundingSphere = geometry.boundingSphere;
console.log(boundingSphere);

4. 可视化包围盒

javascript 复制代码
// 创建包围盒辅助器
import { Box3Helper } from "three";

let boxHelper = new THREE.Box3Helper(boundingBox, 0xffff00);
scene.add(boxHelper);

// 创建包围球辅助器
let sphereGeometry = new THREE.SphereGeometry(boundingSphere.radius, 16, 16);
let sphereMaterial = new THREE.MeshBasicMaterial({
  color: 0xff0000,
  wireframe: true,
});
let sphereMesh = new THREE.Mesh(sphereGeometry, sphereMaterial);
sphereMesh.position.copy(boundingSphere.center);
scene.add(sphereMesh);

第六部分:多个几何体合并包围盒

1. 合并包围盒的原理

当需要为多个对象计算一个共同的包围盒时,可以使用union方法。

2. 实现方法

javascript 复制代码
// 创建一个空的包围盒
var box = new THREE.Box3();

// 遍历所有对象
let arrSphere = [sphere1, sphere2, sphere3];
for (let i = 0; i < arrSphere.length; i++) {
  // 方法一:获取单个对象的包围盒并应用世界矩阵
  // arrSphere[i].geometry.computeBoundingBox();
  // let box3 = arrSphere[i].geometry.boundingBox;
  // arrSphere[i].updateWorldMatrix(true, true);
  // box3.applyMatrix4(arrSphere[i].matrixWorld);
  
  // 方法二:直接从对象创建包围盒
  let box3 = new THREE.Box3().setFromObject(arrSphere[i]);
  // 合并包围盒
  box.union(box3);
}

console.log(box);

// 创建合并后的包围盒辅助器
let boxHelper = new THREE.Box3Helper(box, 0xffff00);
scene.add(boxHelper);

第七部分:线框几何体

1. EdgesGeometry

EdgesGeometry用于显示几何体的边缘线条:

javascript 复制代码
// 获取边缘几何体
let edgesGeometry = new THREE.EdgesGeometry(geometry);

// 创建线段材质
let edgesMaterial = new THREE.LineBasicMaterial({
  color: 0xffffff,
});

// 创建线段
let edges = new THREE.LineSegments(edgesGeometry, edgesMaterial);
scene.add(edges);

2. WireframeGeometry

WireframeGeometry用于显示几何体的线框模式:

javascript 复制代码
// 线框几何体
let wireframeGeometry = new THREE.WireframeGeometry(geometry);

// 创建线段
let wireframe = new THREE.LineSegments(wireframeGeometry, edgesMaterial);
scene.add(wireframe);

3. 处理模型的线框显示

对于复杂的GLTF模型,需要遍历所有网格对象:

javascript 复制代码
gltf.scene.traverse((child) => {
  if (child.isMesh) {
    let mesh = child;
    let geometry = mesh.geometry;

    // 获取边缘几何体
    let edgesGeometry = new THREE.EdgesGeometry(geometry);
    let edgesMaterial = new THREE.LineBasicMaterial({
      color: 0xffffff,
    });

    // 创建线段
    let edges = new THREE.LineSegments(edgesGeometry, edgesMaterial);

    // 应用原始对象的变换矩阵
    mesh.updateWorldMatrix(true, true);
    edges.matrix.copy(mesh.matrixWorld);
    edges.matrix.decompose(edges.position, edges.quaternion, edges.scale);

    // 添加到场景
    scene.add(edges);
  }
});

总结

通过本教程,我们学习了Three.js中Geometry的进阶用法:

  1. UV映射:学会了如何为自定义几何体设置UV坐标,使纹理能够正确映射到几何体表面

  2. 法向量:了解了法向量的重要性及其设置方法,包括手动设置和自动计算

  3. 几何体变换:掌握了如何对几何体进行平移、旋转和缩放操作

  4. 几何体居中:学会了如何计算几何体的中心并将其居中

  5. 包围盒和包围球:理解了包围盒和包围球的概念及应用,包括可视化方法

  6. 合并包围盒:掌握了如何为多个对象计算共同的包围盒

  7. 线框几何体:学会了如何显示几何体的边缘和线框,用于模型可视化

这些技能对于创建复杂的3D场景和优化渲染性能至关重要。掌握这些几何体操作技巧将使您能够更精确地控制3D对象的行为和外观。

相关推荐
全栈王校长2 小时前
Three.js 材质进阶
webgl·three.js
烛阴21 小时前
3D字体TextGeometry
前端·webgl·three.js
全栈王校长1 天前
Three.js 开发快速入门
three.js
全栈王校长1 天前
Three.js 环境搭建与开发初识
three.js
图素1 天前
Cesium 深入浅出 《一》WGS84、ECEF、经纬高:Cesium 世界坐标到底是什么?
webgl
supermapsupport1 天前
SuperMap GIS基础产品FAQ集锦(20260112)
webgl·supermap·iserver·idesktopx
DaMu1 天前
Dreamcore3D ARPG IDE “手搓”游戏引擎,轻量级实时3D创作工具,丝滑操作,即使小白也能轻松愉快的创作出属于你自己的游戏世界!
前端·架构·three.js
刘一说2 天前
腾讯位置服务JavaScript API GL地图组件库深度解析:Vue生态中的地理空间可视化利器
javascript·vue.js·信息可视化·webgl·webgis
烛阴3 天前
从“无”到“有”:手动实现一个 3D 渲染循环全过程
前端·webgl·three.js