概述
本文档将介绍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的进阶用法:
-
UV映射:学会了如何为自定义几何体设置UV坐标,使纹理能够正确映射到几何体表面
-
法向量:了解了法向量的重要性及其设置方法,包括手动设置和自动计算
-
几何体变换:掌握了如何对几何体进行平移、旋转和缩放操作
-
几何体居中:学会了如何计算几何体的中心并将其居中
-
包围盒和包围球:理解了包围盒和包围球的概念及应用,包括可视化方法
-
合并包围盒:掌握了如何为多个对象计算共同的包围盒
-
线框几何体:学会了如何显示几何体的边缘和线框,用于模型可视化
这些技能对于创建复杂的3D场景和优化渲染性能至关重要。掌握这些几何体操作技巧将使您能够更精确地控制3D对象的行为和外观。