主题
Three.js
介绍
Three.js 是一个基于 WebGL 的 3D 图形渲染库,能够帮助你在 Web 环境中创建和渲染复杂的 3D 图形、场景和动画。它封装了底层的 WebGL API,使得 3D 渲染更加简单易用,广泛应用于游戏、虚拟现实、可视化等领域。
安装 Three.js
bash
npm i three
npm i -D @types/threehtml
<!-- 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.BoxGeometry、THREE.SphereGeometry 等创建。
js
const geometry = new THREE.BoxGeometry()材质 (Material)
每个物体都需要有材质,常见的材质包括 MeshBasicMaterial、MeshLambertMaterial、MeshPhongMaterial 等。
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:设置相机位置。lookAtcamera.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)
})