Three.js Web 3D 开发 🔥 热门
从基础场景到高级渲染,掌握 Three.js 核心原理与实战技巧
学习时长:约 18 小时 | 前置:JavaScript、WebGL 基础 | 产出:3D 可视化项目
一、Three.js 概述
什么是 Three.js?
Three.js 是一个基于 WebGL 的 JavaScript 3D 库,由 Ricardo Cabello(Mr.doob)创建,是目前最流行的 Web 3D 库。
Three.js
→
WebGL
→
GPU
→
屏幕
| 特性 | 说明 |
| 易用性 | 封装 WebGL,API 友好 |
| 功能丰富 | 几何体、材质、灯光、动画、物理 |
| 社区活跃 | 大量示例、插件、文档 |
| 性能 | 支持 InstancedMesh、LOD 等优化 |
| 扩展性 | 可自定义 Shader、后处理 |
二、核心概念
2.1 场景(Scene)
// 创建场景
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x1a1a2e);
scene.fog = new THREE.Fog(0x1a1a2e, 10, 50);
2.2 相机(Camera)
// 透视相机
const camera = new THREE.PerspectiveCamera(
75, // FOV 视场角
window.innerWidth / window.innerHeight, // 宽高比
0.1, // 近裁剪面
1000 // 远裁剪面
);
camera.position.set(0, 5, 10);
camera.lookAt(0, 0, 0);
// 轨道控制器
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.dampingFactor = 0.05;
2.3 渲染器(Renderer)
// 创建渲染器
const renderer = new THREE.WebGLRenderer({
antialias: true, // 抗锯齿
alpha: true, // 透明背景
powerPreference: "high-performance"
});
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
renderer.toneMapping = THREE.ACESFilmicToneMapping;
renderer.toneMappingExposure = 1;
document.body.appendChild(renderer.domElement);
三、几何体与材质
3.1 几何体(Geometry)
// 基础几何体
const box = new THREE.BoxGeometry(1, 1, 1);
const sphere = new THREE.SphereGeometry(1, 32, 32);
const plane = new THREE.PlaneGeometry(10, 10);
const torus = new THREE.TorusGeometry(1, 0.4, 16, 100);
// 自定义几何体
const geometry = new THREE.BufferGeometry();
const vertices = new Float32Array([
-1, -1, 0,
1, -1, 0,
0, 1, 0
]);
geometry.setAttribute('position', new THREE.BufferAttribute(vertices, 3));
3.2 材质(Material)
// 基础材质
const basic = new THREE.MeshBasicMaterial({ color: 0xff0000 });
const standard = new THREE.MeshStandardMaterial({
color: 0x2194ce,
metalness: 0.5,
roughness: 0.5,
side: THREE.DoubleSide
});
// PBR 材质
const physical = new THREE.MeshPhysicalMaterial({
color: 0xffffff,
metalness: 0,
roughness: 0.5,
transmission: 1, // 透光
thickness: 0.5, // 厚度
ior: 1.5 // 折射率
});
// 着色器材质
const shader = new THREE.ShaderMaterial({
vertexShader: vertexShaderCode,
fragmentShader: fragmentShaderCode,
uniforms: {
uTime: { value: 0 },
uColor: { value: new THREE.Color(0xff0000) }
}
});
四、灯光与阴影
灯光类型
| 类型 | 说明 | 性能 |
| AmbientLight | 环境光,均匀照亮 | 低 |
| DirectionalLight | 平行光,如太阳 | 中 |
| PointLight | 点光源,如灯泡 | 中 |
| SpotLight | 聚光灯 | 高 |
| RectAreaLight | 面光源 | 高 |
| HemisphereLight | 半球光 | 低 |
// 灯光设置
const ambient = new THREE.AmbientLight(0xffffff, 0.5);
scene.add(ambient);
const directional = new THREE.DirectionalLight(0xffffff, 1);
directional.position.set(5, 10, 7);
directional.castShadow = true;
directional.shadow.mapSize.width = 2048;
directional.shadow.mapSize.height = 2048;
directional.shadow.camera.near = 0.5;
directional.shadow.camera.far = 50;
scene.add(directional);
五、动画系统
5.1 基础动画
// 动画循环
function animate() {
requestAnimationFrame(animate);
// 旋转
mesh.rotation.x += 0.01;
mesh.rotation.y += 0.01;
// 更新控制器
controls.update();
// 渲染
renderer.render(scene, camera);
}
animate();
5.2 使用 Clock 控制时间
const clock = new THREE.Clock();
function animate() {
requestAnimationFrame(animate);
const elapsed = clock.getElapsedTime();
mesh.position.y = Math.sin(elapsed) * 2;
mesh.rotation.x = elapsed * 0.5;
renderer.render(scene, camera);
}
5.3 GSAP 动画库
import gsap from 'gsap';
gsap.to(mesh.position, {
x: 5,
duration: 1,
ease: "power2.inOut",
yoyo: true,
repeat: -1
});
gsap.to(mesh.rotation, {
y: Math.PI * 2,
duration: 2,
repeat: -1,
ease: "none"
});
六、高级渲染
6.1 后处理(Post-processing)
import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js';
import { RenderPass } from 'three/addons/postprocessing/RenderPass.js';
import { UnrealBloomPass } from 'three/addons/postprocessing/UnrealBloomPass.js';
import { OutputPass } from 'three/addons/postprocessing/OutputPass.js';
const composer = new EffectComposer(renderer);
composer.addPass(new RenderPass(scene, camera));
const bloom = new UnrealBloomPass(
new THREE.Vector2(window.innerWidth, window.innerHeight),
1.5, // 强度
0.4, // 半径
0.85 // 阈值
);
composer.addPass(bloom);
composer.addPass(new OutputPass());
// 渲染时使用 composer 替代 renderer
composer.render();
6.2 自定义 Shader
// 顶点着色器
const vertexShader = `
varying vec2 vUv;
varying vec3 vNormal;
uniform float uTime;
void main() {
vUv = uv;
vNormal = normal;
vec3 pos = position;
pos.y += sin(pos.x * 2.0 + uTime) * 0.1;
gl_Position = projectionMatrix * modelViewMatrix * vec4(pos, 1.0);
}
`;
// 片段着色器
const fragmentShader = `
varying vec2 vUv;
varying vec3 vNormal;
uniform float uTime;
uniform vec3 uColor;
void main() {
float intensity = pow(0.7 - dot(vNormal, vec3(0,0,1.0)), 2.0);
vec3 color = uColor * (1.0 + sin(uTime) * 0.2);
gl_FragColor = vec4(color, 1.0);
}
`;
七、性能优化
优化策略
| 策略 | 说明 |
| InstancedMesh | 批量渲染相同几何体 |
| LOD | 多层次细节 |
| 合并几何体 | 减少 draw call |
| 纹理压缩 | 使用 KTX2 格式 |
| Frustum Culling | 视锥体剔除 |
| Occlusion Culling | 遮挡剔除 |
| Web Worker | 物理计算放后台 |
// InstancedMesh 示例
const geometry = new THREE.BoxGeometry(1, 1, 1);
const material = new THREE.MeshStandardMaterial();
const count = 10000;
const instancedMesh = new THREE.InstancedMesh(geometry, material, count);
const dummy = new THREE.Object3D();
for (let i = 0; i < count; i++) {
dummy.position.set(
Math.random() * 100 - 50,
Math.random() * 100 - 50,
Math.random() * 100 - 50
);
dummy.updateMatrix();
instancedMesh.setMatrixAt(i, dummy.matrix);
}
scene.add(instancedMesh);
八、React Three Fiber
8.1 基础使用
import { Canvas, useFrame } from '@react-three/fiber';
import { OrbitControls, Stars } from '@react-three/drei';
function Box(props) {
const meshRef = useRef();
useFrame((state, delta) => {
meshRef.current.rotation.x += delta;
meshRef.current.rotation.y += delta * 0.5;
});
return (
<mesh ref={meshRef} {...props}>
<boxGeometry args={[1, 1, 1]} />
<meshStandardMaterial color="orange" />
</mesh>
);
}
export default function App() {
return (
<Canvas camera={{ position: [0, 0, 5] }}>
<ambientLight intensity={0.5} />
<pointLight position={[10, 10, 10]} />
<Box position={[0, 0, 0]} />
<Stars />
<OrbitControls />
</Canvas>
);
}
九、实战项目
项目:3D 地球可视化
import { Canvas, useFrame, useLoader } from '@react-three/fiber';
import { TextureLoader } from 'three';
import * as THREE from 'three';
function Earth() {
const earthMap = useLoader(TextureLoader, '/earth.jpg');
const bumpMap = useLoader(TextureLoader, '/earth-bump.jpg');
const cloudsMap = useLoader(TextureLoader, '/clouds.jpg');
const earthRef = useRef();
const cloudsRef = useRef();
useFrame(({ clock }) => {
const elapsed = clock.getElapsedTime();
earthRef.current.rotation.y = elapsed * 0.05;
cloudsRef.current.rotation.y = elapsed * 0.06;
});
return (
<>
<mesh ref={cloudsRef}>
<sphereGeometry args={[2.02, 64, 64]} />
<meshPhongMaterial map={cloudsMap} transparent opacity={0.4} />
</mesh>
<mesh ref={earthRef}>
<sphereGeometry args={[2, 64, 64]} />
<meshPhongMaterial map={earthMap} bumpMap={bumpMap} bumpScale={0.05} />
</mesh>
</>
);
}
十、面试高频问题
Q1:Three.js 和 Babylon.js 的区别?
查看答案 ▼
| 维度 | Three.js | Babylon.js |
| 定位 | 轻量、灵活 | 完整游戏引擎 |
| 学习曲线 | 平缓 | 陡峭 |
| 物理引擎 | 需引入 | 内置 |
| TypeScript | 支持 | 原生 |
| 社区 | 更大 | 较小 |
| 适用场景 | 可视化、展示 | 游戏、复杂应用 |
Q2:Three.js 性能优化有哪些方法?
查看答案 ▼
- 使用 InstancedMesh 批量渲染
- 合并几何体减少 draw call
- 使用 LOD 多层次细节
- 纹理压缩(KTX2/Basis)
- 开启 Frustum Culling
- 使用 Web Worker 处理物理
- 降低像素比(pixelRatio)
- 使用 Draco 压缩模型