Three.js案例
Photo

Three.js 动态网格渲染和动画教程

在这篇教程中,我们将使用 Three.js 来创建一个动态的网格渲染和动画展示。看下效果

初始设置

首先,我们需要导入必要的 Three.js 模块和其他依赖,如下所示:

import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import { gsap } from "gsap";
import "./style.css";
  • THREE 是主库,提供了创建和显示 3D 图形的核心功能。
  • OrbitControls 允许用户通过鼠标操作来旋转、缩放和平移相机。
  • gsap 是一个强大的动画库,用于创建平滑的动画效果。

创建渲染器、场景和相机

创建并配置渲染器、场景和相机:

const renderer = new THREE.WebGLRenderer({ alpha: true });
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, 2, 0.1, 100);
const controls = new OrbitControls(camera, renderer.domElement);
  • WebGLRenderer 用于在网页上渲染 3D 图形。
  • 设置 alpha: true 允许背景透明。
  • PerspectiveCamera 提供了一种透视投影的相机视角。
  • 相机参数解释:视角 75 度,纵横比 2,最近裁剪面 0.1,最远裁剪面 100。

窗口调整和动画循环

确保渲染器的尺寸随窗口大小改变而调整,并设置动画循环:

window.addEventListener("resize", () => {
  const { clientWidth, clientHeight } = renderer.domElement;
  renderer.setPixelRatio(window.devicePixelRatio);
  renderer.setSize(clientWidth, clientHeight, false);
  camera.aspect = clientWidth / clientHeight;
  camera.updateProjectionMatrix();
});
  • 这段代码监听窗口尺寸变化,并相应地调整渲染器和相机的配置。

定义尺寸和图片 URLs

const W = 10,
  H = 10,
  SW = W * 20,
  SH = H * 20;
 
const IMG_URLS = ["https://picsum.photos/600/600", "https://picsum.photos/800/800"];
  • WH 分别代表网格的宽度和高度,这里设置为 10 个单位。
  • SWSH 是整个场景的网格的宽度和高度的扩展,通过乘以 20 来创建更细分的网格,从而可以创建更复杂的几何形状和细节。
  • IMG_URLS 数组包含了用于纹理的图片 URL,这些图片会被加载并映射到几何体的表面,提供视觉上的丰富性。

设置相机位置

camera.position.set(0, 0, 8);
  • 这行代码设置相机的位置,使其在 Z 轴上位于原点之前 8 个单位处,为观察者提供了一个正面的视角来查看整个 3D 场景。

添加聚光灯

for (const { color, intensity, x, y, z } of [
  { color: "white", intensity: 1, x: -W, y: 0, z: 0 },
  { color: "white", intensity: 1, x: W, y: 0, z: 0 },
]) {
  const L = new THREE.SpotLight(color, intensity, W, Math.PI / 2, 0, 0);
  L.position.set(x, y, z);
  scene.add(L);
}

循环遍历一个包含聚光灯配置的数组,设置两个聚光灯来照亮场景。每个灯的参数如下:

  • color 指定了灯光的颜色,这里为白色,提供了中性的照明,适合大多数场景。
  • intensity 是灯光的强度,设置为 1,代表标准的光照强度。
  • x, y, z 定义了灯光的位置。灯光被放置在网格的左右两侧(-WW),这样可以从两侧对场景进行均匀的照明,减少阴影区域,提高视觉效果的立体感。
  • W 是聚光灯的距离参数,控制光源的范围。
  • Math.PI / 2 设置聚光灯的角度,指的是聚光灯光线发散的宽度。
  • 最后两个参数(0, 0)代表聚光灯的衰减和阴影。

创建顶点数组

const vs = [];
for (let i = 0; i < SH; ++i) {
  vs[i] = [];
  for (let j = 0; j < SW; ++j) {
    vs[i][j] = {
      // 顶点数据将在这里定义
    };
  }
}
  • vs 是一个二维数组,用于存储顶点数据。每个顶点包含网格中的位置、纹理坐标等信息。
  • SHSW 是之前定义的网格的高度和宽度,这里通过双层循环为每一个网格单元生成一个顶点对象。
  • 这些顶点数据随后会用来构建 3D 模型的几何体。

构建几何体

const geoms = [];
for (let k = 0; k <= 1; ++k) {
  const geom = new THREE.BufferGeometry();
  // 设置属性和计算法线
  geoms.push(geom);
}
  • geoms 数组用于存储几何体。
  • 使用 THREE.BufferGeometry() 构建高效的几何体对象,该对象适合复杂的 3D 模型。
  • 在这个循环中,可以针对每种不同的顶点数组配置和使用不同的绘制方式,例如使用三角剖分方法来生成网格。
  • 几何体的属性(如顶点位置、纹理坐标)和法线会在这里设置,法线是重要的渲染属性,影响材质对光线的反应。

加载纹理并创建材料和网格

const g = new THREE.Group();
for (const [i, geom] of geoms.entries()) {
  const map = new THREE.TextureLoader().load(IMG_URLS[i]);
  const mat = new THREE.MeshLambertMaterial({ map, side: THREE.DoubleSide });
  const mesh = new THREE.Mesh(geom, mat);
  g.add(mesh);
}
scene.add(g);
  • 使用 THREE.Group() 创建一个组,这使得可以一次性对多个对象进行变换、控制和动画处理。
  • THREE.TextureLoader().load() 方法用于加载纹理图像,这些图像从 IMG_URLS 数组中获取,将为每个几何体的表面提供视觉纹理。
  • THREE.MeshLambertMaterial 是一种对光照反应良好的材料,适合创建现实感的效果。side: THREE.DoubleSide 参数指明材料应该在几何体的两侧都可见。
  • THREE.Mesh 对象将几何体和材料结合起来,创建最终的网格对象。
  • 将每个网格添加到组中,然后将整个组添加到场景中,这样做的好处是可以统一管理、变换或动画化这些网格。

动画循环

最后,添加动画循环:

renderer.setAnimationLoop((t) => {
  g.rotation.y += rotationSpeed;
  renderer.render(scene, camera);
  controls.update();
});

代码

github

https://github.com/calmound/threejs-demo/tree/main/photo (opens in a new tab)

gitee

https://gitee.com/calmound/threejs-demo/tree/main/photo (opens in a new tab)