threejs(2)-Geometry进阶详解

一、全面讲解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;// 获取边缘geometrylet 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.rhkb.cn/news/162677.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Ubuntu系统如何进行网络连接-连接电脑局域网-物联网开发-Ubuntu系统维护

一、前言 在Ubuntu系统的维护中,我们常常需要对VMware中的Ubuntu虚拟机配置网络连接,以连接服务器下载或安装软件包以及进行网络通信等。 基于上述问题,本文将着重分享Ubuntu配置网络链接的若干方法。 二、网络连接模式 打开VM,右…

【Java 进阶篇】JavaScript 动态表格案例

在这篇博客中,我们将深入了解JavaScript如何创建和操作动态表格。我们将从头开始构建一个动态表格,并逐步添加各种功能,使其能够实现数据的添加、删除和编辑。这个示例将有助于理解如何在前端开发中使用JavaScript创建交互性强大的表格。 准…

网站如何优化加速,让网站降低延迟

优化网站架构 精简页面加载过程:通过消除冗余代码和不必要的图像,并采用CDN资源分发,以减少加载时间。 精心规划内容架构:通过使用恰当的标题和描述,使搜索引擎能够快速理解页面的内涵。 选择性能出众的前端框架&…

RT-Thread学习笔记(三):线程管理

线程管理 线程管理相关概念什么是时间片轮转调度器锁线程运行机制线程的五种状态 动态和静态创建线程区别动态和静态创建线程优缺点RT-Thread动态线程管理函数动态创建线程动态删除线程 RT-Thread静态线程管理函数静态创建线程 线程其他操作线程启动线程延时获得当前执行的线程…

Java并发面试题:(六)悲观锁和乐观锁和Java内存模型和CAS原理

悲观锁和乐观锁的区别 什么是悲观锁? 基本上我们理解的操作前对资源加锁,操作完后释放锁。说的都是悲观锁。悲观锁认为所有的资源都是不安全的,随时会被其他线程操作、更改。所以操作资源前一定要加一把锁、防止其他线程访问。 什么是乐观锁&…

【23种设计模式】装饰器模式

个人主页:金鳞踏雨 个人简介:大家好,我是金鳞,一个初出茅庐的Java小白 目前状况:22届普通本科毕业生,几经波折了,现在任职于一家国内大型知名日化公司,从事Java开发工作 我的博客&am…

Excel·VBA单元格区域数据对比差异标记颜色

之前的一篇博客《ExcelVBA单元格重复值标记颜色》,是对重复的整行标记颜色 而本文是按行对比2个单元格区域的数据,并对有差异的区域(一个单元格区域有的,而另一个单元格区域没有的)标记颜色,且只要存在任意…

单链表经典OJ题:合并有序链表

目录 ​编辑 题目: 图例: 分析: 解法: 解法1: 解法2: 解法的对比: 解法2: 注意事项: 图例: 代码演示: 代码分析: 代码缺点: 重复…

[MySQL]BLOB/TEXT column ‘xxx‘ used in key specification without a key length

报错信息: SQLSTATE[42000]: Syntax error or access violation: 1170 BLOB/TEXT column xxx used in key specification without a key length 原因: MySQL的唯一索引不支持text类型的字段!

C++初阶-类和对象(上)

类和对象(上) 一、面向过程和面向对象初步认识二、类的引入三、类的定义四、类的访问限定符及封装访问限定符封装 五、类的作用域六、类的实例化七、类的对象大小的计算如何计算类对象的大小类对象的存储方式猜测 八、类成员函数的this指针this指针的引出…

网站如何才能不被黑,如何做好网络安全

当企业网站受到攻击时,首页文件可能被篡改,百度快照也可能被劫持并重定向到其他网站。首要任务是加强网站的安全防护。然而,许多企业缺乏建立完善的网站安全防护体系的知识。因此,需要专业的网站安全公司来提供相应的保护措施。今…

番外8.1 配置+管理文件系统

Task01: Linux 文件系统结构; 可以进行Linux操作系统的文件权限管理与方式切换,可以应用磁盘与文件权限管理工具; 01:常见文件系统类型(Ext4[rhel6默认文件管理系统], 存储容量1 EB1073741824 GB; XFS[rhel 7/8默认的文…

Radius OTP完成堡垒机登录认证 安当加密

Radius OTP(One-Time Password)是一种用于身份验证的协议,它通过向用户发送一个一次性密码来验证用户的身份。使用Radius OTP可以实现堡垒机登录,以下是一些实现步骤: 1、安装Radius服务器 首先需要安装Radius服务器…

【量化交易笔记】9.量化投资理论及一般流程

前言 在第7篇文章中指出,量化交易的主要有两方面应用,基于的数据主要是两个类型,如前面讲的用之前的数据预测股价,这类数据我们可归为纵向研究数据,又称时间序列数据,另一类是横截面数据,以称截…

关于CW32单片机pack包安装 KEIL IAR

CW32 系列微控制器软件开发工具入门 芯片包 1. 下载芯片包 官方下载链接:武汉鑫源半导体 2. 安装芯片包 双击芯片包.pack文件 支持 CW32F 系列的 IDE 支持 CW32F 系列的工具链: • • EWARM v7.70 或更高版本 MDK-ARM v5.17 或更高版本 2.1 EW…

Android MediaMetadataRetriever setDataSource failed: status = 0xFFFFFFEA

Android MediaMetadataRetriever setDataSource抛错: java.lang.RuntimeException: setDataSource failed: status 0xFFFFFFEA 原因是 setDataSource(String path) path指向的视频文件大小为0或者是破损视频资源。 Android AppGlideModule,DataFetcher,ModelLoad…

环境变量【使用命令行参数引出环境变量】

前提:命令行参数 大家在写C/C程序的时候肯定见过下面这种情况: main函数里面携带的参数,平常写代码过程中很少用到这两个参数,接下来我们就研究一下 我们也不知道 指针数组argv里面到底保存的是什么,也不知道这个a…

Spring 国际化:i18n

文章目录 i18n概述Java国际化Spring6国际化MessageSource接口使用Spring6国际化 i18n概述 国际化也称作i18n,其来源是英文单词 internationalization的首末字符i和n,18为中间的字符数。由于软件发行可能面向多个国家,对于不同国家的用户&…

Apacheb Shiro 1.2.4反序列化漏洞(CVE-2016-4437)

Apache Shiro 1.2.4反序列化漏洞(CVE-2016-4437) 1 在线漏洞解读: https://vulhub.org/#/environments/shiro/CVE-2016-4437/2 环境搭建 cd /home/kali/vulhub/shiro/CVE-2016-4437启动: sudo docker-compose up -d # 拉取下载并启动sud…

PyQt 小程序

设备管理程序 v0.0.1.0, 终于出了一个基础版本,… … 两个字典的键值判断 辛亏用的是Python 这个编码时间大大缩短了 对已有的命令行进行GUI 化 from typing import Optional import PySide6.QtCore import PySide6.QtWidgets from cmd_ui import Ui_MainWindow from PySide6.…