threejs(2) - ジオメトリ上級詳細解説

1. UVと用途を徹底解説

このセクションでは、UV マッピングの概念、頂点位置との関係と違い、Geometry での UV 座標の設定方法など、Three.js での UV マッピングについて説明します。たとえば、BufferGeometry を使用します。

カラー対応
ここに画像の説明を挿入します
ここに画像の説明を挿入します
ここに画像の説明を挿入します

  1. UVマッピングとは何ですか?

UV マッピングは、2 次元のテクスチャを 3 次元モデルの表面にマッピングする手法です。このプロセスでは、3D モデル上の各頂点に 2 次元座標 (U、V) が割り当てられます。U と V はそれぞれテクスチャ座標の水平方向と垂直方向を表します。これらの座標は、テクスチャ イメージ上のピクセルをモデル表面上の点に対応させるために使用されます。UV マッピングを使用すると、モデル上のテクスチャの位置と方向を正確に制御できます。

  1. UV座標と頂点位置の関係と違い

頂点位置 (Position) は、3D モデル内の各頂点の空間座標 (x、y、z) を表します。UV 座標は、テクスチャ上の頂点の 2 次元座標 (U、V) を表します。頂点の位置はシーン内のモデルの形状を決定するために使用され、UV 座標はモデル上のテクスチャの分布を決定するために使用されます。
2 つの主な違いは次のとおりです。
● 頂点位置は 3 次元座標であり、3 次元空間内の頂点の位置を表します。
● UV 座標は、テクスチャ イメージ上の頂点の位置を表す 2 次元座標です。

  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 座標を設定します。この例では、テクスチャを 4 つの頂点に均等にマッピングします。

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));

次に、平面を構成する 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 座標を設定する方法を示します。まず、4 つの頂点を持つ平面を作成します。次に、テクスチャを平面上に均等にマッピングできるように、各頂点に 2D UV 座標を割り当てました。最後に、テクスチャを使用してマテリアルを作成し、それをプレーンに適用します。

2. 法線ベクトル属性と法線ベクトル補助の適用

ここに画像の説明を挿入します
平面に垂直な線が法線ベクトルです

// 导入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;
});

3. 幾何学的な固定小数点変換 - 固定小数点の変位、回転、スケーリング

  1. 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个单位

次に、立方体の基本マテリアルを作成し、ジオメトリとマテリアルをメッシュ オブジェクトに渡します。

const material = new THREE.MeshBasicMaterial({
    
     color: 0x00ff00 });
const cube = new THREE.Mesh(geometry, material);

// 将网格添加到场景中
scene.add(cube);

●rotateX、rotateY、rotateZ: これらのメソッドは、それぞれ X、Y、Z 軸に沿ってジオメトリを回転するために使用されます。これらは、回転角度を表すラジアン値を引数として受け入れます。これらのメソッドを呼び出すことで、ジオメトリの方向を変更できます。
● スケール: このメソッドは、ジオメトリをスケールするために使用されます。これは 3 つのパラメーター x、y、z を受け入れます。これらはそれぞれ X、Y、Z 軸に沿ったスケーリング係数を表します。このメソッドを呼び出すことで、ジオメトリのサイズを変更できます。
● translation: このメソッドは、ジオメトリを変換するために使用されます。これは 3 つのパラメータ 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 のプロパティはシーン内のオブジェクトの変換にのみ影響します。したがって、ジオメトリ内のメソッドを変更すると、そのジオメトリを使用するすべてのメッシュに影響しますが、Object3D プロパティを変更しても、同じジオメトリを使用する他のオブジェクトには影響しません。

  • Geometry のメソッドは、作成中のジオメトリへの 1 回限りの変更に適していますが、Object3D のプロパティは、リアルタイム レンダリング中のシーン内のオブジェクトの動的変換に適しています。

  • パフォーマンスの観点から見ると、通常、Object3D プロパティの変更は、ジオメトリ内の頂点データの変更よりも効率的です。これは、後者には頂点データの再計算が含まれるためです。したがって、実際のアプリケーションでは、変換に Object3D のプロパティを使用することが優先されます。

4. バウンディングボックスとワールド行列変換の使用

ここに画像の説明を挿入します
目的: 衝突を検出します。

// 导入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);
  }
);

5. ジオメトリのセンタリングとジオメトリの中心の取得

// 导入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);
  }
);

6. 複数のオブジェクトの境界ボックスを取得する

// 导入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);
  }
);

7. エッジ ジオメトリとワイヤーフレーム ジオメトリ

ここに画像の説明を挿入します
ここに画像の説明を挿入します

// 导入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);
      }
    });
  }
);

おすすめ

転載: blog.csdn.net/woyebuzhidao321/article/details/133798111