Skip to content

Three.js

介绍

Three.js 是一个基于 WebGL 的 3D 图形渲染库,能够帮助你在 Web 环境中创建和渲染复杂的 3D 图形、场景和动画。它封装了底层的 WebGL API,使得 3D 渲染更加简单易用,广泛应用于游戏、虚拟现实、可视化等领域。

安装 Three.js

bash
npm i three
npm i -D @types/three
html
<!-- jsdelivr -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>

入门案例

html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Three.js 入门</title>
    <style>
      body {
        margin: 0;
      }
      canvas {
        display: block;
      }
    </style>
  </head>
  <body>
    <canvas class="canvas"></canvas>

    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
    <script>
      const canvas = document.querySelector('.canvas')

      // 创建场景
      const scene = new THREE.Scene()

      // 创建几何体(一个立方体)
      const geometry = new THREE.BoxGeometry()
      const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 })
      const mesh = new THREE.Mesh(geometry, material)
      scene.add(mesh)

      // 创建相机
      const size = {
        width: 800,
        height: 600,
      }
      const camera = new THREE.PerspectiveCamera(75, size.width / size.height, 0.1, 1000)
      // 相机方位:
      // x 轴从左到右,y 轴从下到上,z 轴从里到外
      camera.position.z = 3 // 设置相机位置

      // 创建渲染器
      const renderer = new THREE.WebGLRenderer({ canvas })
      renderer.setSize(size.width, size.height)

      // 创建光源
      const light = new THREE.AmbientLight(0x404040) // 环境光
      scene.add(light)

      // 动画循环
      function animate() {
        requestAnimationFrame(animate)

        mesh.rotation.x += 0.01
        mesh.rotation.y += 0.01

        // 渲染场景
        renderer.render(scene, camera)
      }

      animate()
    </script>
  </body>
</html>

核心对象

场景 (Scene)

用于添加、管理和渲染场景中的所有对象。

js
const scene = new THREE.Scene()

几何体 (Geometry)

Three.js 提供了多种几何体(如立方体、球体、平面等),这些几何体可以通过 THREE.BoxGeometryTHREE.SphereGeometry 等创建。

js
const geometry = new THREE.BoxGeometry()

材质 (Material)

每个物体都需要有材质,常见的材质包括 MeshBasicMaterialMeshLambertMaterialMeshPhongMaterial 等。

js
const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 })

物体 (Mesh)

Mesh 是几何体和材质的组合体,物体可以被添加到场景中。

js
const mesh = new THREE.Mesh(geometry, material)
scene.add(mesh)

相机 (Camera)

PerspectiveCamera

PerspectiveCamera 是最常用的相机类型,它模拟人眼视角。它的构造函数参数分别是:视野角度(FOV),宽高比,近剪裁面和远剪裁面。

js
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000)

参数说明:

  • fov:视野角度,单位为度(degree)。
  • aspect:宽高比,通常设置为浏览器窗口的宽高比。
  • near:近剪裁面,距离相机多近的物体会被渲染。
  • far:远剪裁面,距离相机多远的物体会被渲染

常见 api:

  • position:设置相机位置。
  • lookAt
    • camera.lookAt(x, y, z):让相机朝向指定位置。
    • camera.lookAt(vector3):让相机朝向指定的 Vector3 位置。
js

OrthographicCamera

OrthographicCamera 是正交相机,适用于需要保持物体尺寸不变的场景,如 2D 游戏或 CAD 应用。

js
const aspect = window.innerWidth / window.innerHeight // 计算宽高比
const d = 5
const camera = new THREE.OrthographicCamera(-d * aspect, d * aspect, d, -d, 1, 1000)

参数说明:

  • left:左边界。
  • right:右边界。
  • top:上边界。
  • bottom:下边界。
  • near:近剪裁面。
  • far:远剪裁面。

渲染器 (Renderer)

渲染器负责将场景绘制到屏幕上。我们使用 WebGLRenderer 来进行渲染。

js
const renderer = new THREE.WebGLRenderer()
renderer.setSize(window.innerWidth, window.innerHeight)
document.body.appendChild(renderer.domElement)

灯光(Light)

  • AmbientLight:环境光,不产生阴影。
  • DirectionalLight:平行光,模拟太阳光。
  • PointLight:点光源,光线从一点向各个方向辐射。
  • SpotLight:聚光灯,光线从一个点发射并具有聚焦效果。
js
const light = new THREE.AmbientLight(0x404040)
scene.add(light)

动画

在 Three.js 中,动画通常是通过更新物体的位置、旋转、缩放等属性来实现的。使用 requestAnimationFrame 进行递归渲染。

js
function animate() {
  requestAnimationFrame(animate)

  // 动画:让立方体旋转
  mesh.rotation.x += 0.01
  mesh.rotation.y += 0.01

  renderer.render(scene, camera)
}

animate()

transform 控制

  • position:控制物体的位置。
  • scale:控制物体的缩放。
  • rotation:控制物体的旋转。

提示

需要理解的概念:

  • 欧拉角(Euler Angles)
  • 万向锁(Gimbal Lock)
  • 四元数(Quaternion)
js
// === position ===
// mesh.position.x = 1
// mesh.position.y = 2
// mesh.position.z = 3
mesh.position.set(1, 2, 3)

// === scale ===
// mesh.scale.x = 2
// mesh.scale.y = 2
// mesh.scale.z = 2
mesh.scale.set(2, 2, 2)

// === rotation ===
// 需要注意轴顺序的问题
mesh.rotation.reorder('YXZ') // 旋转顺序,例如:射击游戏
mesh.rotation.x = Math.PI * 0.25 // 45度
mesh.rotation.y = Math.PI * 0.25 // 45度

轴辅助器

在开发时,可以使用 AxesHelper 来显示坐标轴,帮助理解物体在 3D 空间中的位置和方向。

js
const axesHelper = new THREE.AxesHelper(5)
scene.add(axesHelper)

// 后续可根据需要调整相机位置
camera.position.set(3, 3, 3)
// camera.lookAt(0, 0, 0)

group 组

在 Three.js 中,Group 用于将多个物体组合在一起,以便统一控制它们的位置、旋转和缩放。

js
const group = new THREE.Group()
group.position.y = 1
group.scale.y = 2
group.rotation.y = Math.PI / 4
scene.add(group)

const cube1 = new THREE.Mesh(new THREE.BoxGeometry(1, 1, 1), new THREE.MeshBasicMaterial({ color: 0xff0000 }))
cube1.position.x = -2
group.add(cube1)

const cube2 = new THREE.Mesh(new THREE.BoxGeometry(1, 1, 1), new THREE.MeshBasicMaterial({ color: 0x00ff00 }))
group.add(cube2)

const cube3 = new THREE.Mesh(new THREE.BoxGeometry(1, 1, 1), new THREE.MeshBasicMaterial({ color: 0x0000ff }))
cube3.position.x = 2
group.add(cube3)

统一帧时间

js
let time = Date.now()
const tick = () => {
  const curTime = Date.now()
  const deltaTime = curTime - time
  time = curTime

  mesh.rotation.y += 0.001 * deltaTime
  requestAnimationFrame(tick)
  renderer.render(scene, camera)
}
tick()
js
const clock = new THREE.Clock()
const tick = () => {
  const elapsedTime = clock.getElapsedTime()

  mesh.rotation.y = elapsedTime * Math.PI * 2 // 一秒旋转一圈
  requestAnimationFrame(tick)
  renderer.render(scene, camera)
}
tick()

提示

THREE.Clock 尽量使用 getElapsedTime(),而不是 getDelta()

  • 优先使用 Clock.getElapsedTime() 并自己在代码里计算 delta(elapsed - lastElapsed),比直接调用 Clock.getDelta() 更可靠、可控、可重现,也更适合做固定步长(fixed-timestep) 和多子系统协同更新的场景。
  • getDelta() 方便但有副作用(是有状态/消费性的),在复杂项目里容易引入 subtle 的 bug。

响应窗口大小变化

为了使渲染器适应浏览器窗口的大小变化,你可以监听 resize 事件并更新相机和渲染器的大小。

js
window.addEventListener('resize', () => {
  renderer.setSize(window.innerWidth, window.innerHeight)
  camera.aspect = window.innerWidth / window.innerHeight
  camera.updateProjectionMatrix()
})

加载外部模型

Three.js 支持多种格式的 3D 模型加载,如 GLTF、OBJ、FBX 等。通过加载器可以轻松加载外部模型。

js
const loader = new THREE.GLTFLoader()
loader.load('path/to/model.gltf', function (gltf) {
  scene.add(gltf.scene)
})

基于 MIT 许可发布