一、全面讲解UV与应用
在本节中,我们将讨论Three.js中的UV映射,包括UV映射的概念、与顶点位置的关系和区别以及如何在Geometry中设置UV坐标。我们将使用BufferGeometry进行示例说明。
颜色对应
- 什么是UV映射?
UV映射是一种将二维纹理映射到三维模型表面的技术。在这个过程中,3D模型上的每个顶点都会被赋予一个二维坐标(U, V)。U和V分别表示纹理坐标的水平和垂直方向。这些坐标用于将纹理图像上的像素与模型表面上的点进行对应。通过UV映射,我们可以在模型上精确地控制纹理的位置和方向。
- UV坐标与顶点位置的关系和区别
顶点位置(Position)表示3D模型中每个顶点的空间坐标(x, y, z)。UV坐标则表示该顶点在纹理上的二维坐标(U, V)。顶点位置用于确定模型在场景中的形状,而UV坐标用于确定纹理在模型上的分布。
两者之间的主要区别在于:
● 顶点位置是三维坐标,描述了一个顶点在三维空间中的位置。
● UV坐标是二维坐标,描述了一个顶点在纹理图像上的位置。
- 设置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;
});
三、几何定点转化-定点位移、旋转、缩放
- 使用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轴的平移距离。通过调用此方法,我们可以更改几何体的位置。
需要注意的是,这些方法直接修改几何体的顶点数据,因此会影响所有使用该几何体的网格。
- 使用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);
- 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);
}
});
}
);