WebGL 很强大,但 API 偏底层。Three.js 作为基于 WebGL 的高层封装,让我们能够用更少的代码做更多的 3D 事:模型展示、交互可视化、Web 游戏、VR/AR 等。本文通过清晰的概念、代码范例与实践建议,帮你从入门到进阶,快速建立可落地的 3D 开发能力。
<canvas>
上进行 GPU 加速渲染。2d
上下文用于二维绘制,webgl
上下文用于三维绘制。关系:OpenGL → OpenGL ES → WebGL → Three.js
<script src="https://threejs.org/build/three.js"></script>
npm install three --save
import * as THREE from 'three';
了解 threejs 的场景 Scene、相机 Camera、渲染器 Renderer 概念
相当于一个 3D 虚拟世界容器,所有的人、物、灯光都在这个场景容器里。
import { Scene } from 'three';
const scene = new Scene();
scene.add(/* 物体、灯光、环境等 */);
// 创建一个长方体几何对象
const geometry = new THREE.BoxGeometry(1, 1, 1);
更多几何体:TorusGeometry
、TorusKnotGeometry
、IcosahedronGeometry
、LatheGeometry
、ExtrudeGeometry
(可用于路径拉伸建模)等。
const material = new THREE.MeshStandardMaterial({
color: 0xff5555,
metalness: 0.2,
roughness: 0.6,
});
建议以 PBR 材质(
MeshStandardMaterial
/MeshPhysicalMaterial
)为默认选择,写实效果与灯光/环境贴图兼容性更好。
几何体 + 材质 = 可渲染的网格。
const mesh = new THREE.Mesh(geometry, material);
mesh.position.set(0, 0.5, 0);
scene.add(mesh);
const camera = new THREE.PerspectiveCamera(
60,
window.innerWidth / window.innerHeight,
0.1,
1000
);
camera.position.set(3, 2, 6);
camera.lookAt(0, 0, 0);
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
document.body.appendChild(renderer.domElement);
const ambient = new THREE.AmbientLight(0xffffff, 0.4);
const dir = new THREE.DirectionalLight(0xffffff, 1);
dir.position.set(5, 8, 5);
dir.castShadow = true;
scene.add(ambient, dir);
renderer.shadowMap.enabled = true;
mesh.castShadow = true;
// 接收阴影的地面
const ground = new THREE.Mesh(
new THREE.PlaneGeometry(20, 20),
new THREE.MeshStandardMaterial({ color: 0xdddddd })
);
ground.receiveShadow = true;
ground.rotation.x = -Math.PI / 2;
scene.add(ground);
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
const texture = new THREE.TextureLoader().load('/textures/wood.jpg');
const mat = new THREE.MeshStandardMaterial({ map: texture });
// 环境贴图(更真实的高光/反射)
import { RGBELoader } from 'three/examples/jsm/loaders/RGBELoader.js';
new RGBELoader().load('/hdr/venice_sunset_1k.hdr', (envMap) => {
envMap.mapping = THREE.EquirectangularReflectionMapping;
scene.environment = envMap;
scene.background = envMap; // 可选:把 hdr 作为背景
});
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
const loader = new GLTFLoader();
loader.load('/models/robot.glb', (gltf) => {
scene.add(gltf.scene);
});
小贴士:用 Draco/Meshopt 压缩 glTF,显著减小体积、提速加载。
requestAnimationFrame
循环修改 position/rotation/material
等。AnimationMixer
播放 glTF 内建动画。const clock = new THREE.Clock();
function animate() {
requestAnimationFrame(animate);
controls.update();
renderer.render(scene, camera);
}
animate();
Bloom、FXAA、景深、色调映射等。
import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer.js';
import { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass.js';
const composer = new EffectComposer(renderer);
composer.addPass(new RenderPass(scene, camera));
// ... 添加其它特效 Pass
function animate() {
requestAnimationFrame(animate);
controls.update();
composer.render();
}
Points
+ PointsMaterial
绘制大量粒子。InstancedMesh
批量渲染成千上万相同几何体,极大减少 draw call。可直接运行,体现「场景-相机-网格-渲染循环」最小闭环。
<canvas id="c"></canvas>
<script type="module">
import * as THREE from 'https://unpkg.com/three@0.165.0/build/three.module.js';
const renderer = new THREE.WebGLRenderer({
canvas: document.getElementById('c'),
antialias: true,
});
renderer.setSize(innerWidth, innerHeight);
renderer.setPixelRatio(Math.min(devicePixelRatio, 2));
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(
60,
innerWidth / innerHeight,
0.1,
100
);
camera.position.set(3, 2, 6);
camera.lookAt(0, 0, 0);
const cube = new THREE.Mesh(
new THREE.BoxGeometry(1, 1, 1),
new THREE.MeshStandardMaterial({ color: 0x4e8cff, roughness: 0.5 })
);
scene.add(cube);
const ambient = new THREE.AmbientLight(0xffffff, 0.5);
const dir = new THREE.DirectionalLight(0xffffff, 1);
dir.position.set(5, 8, 5);
scene.add(ambient, dir);
addEventListener('resize', () => {
camera.aspect = innerWidth / innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(innerWidth, innerHeight);
});
const clock = new THREE.Clock();
(function loop() {
requestAnimationFrame(loop);
const t = clock.getElapsedTime();
cube.rotation.x = t * 0.6;
cube.rotation.y = t * 1.0;
renderer.render(scene, camera);
})();
</script>
减小资源体积
glb
)+ Draco/Meshopt 压缩;贴图用 WebP/AVIF;合理分辨率与重复纹理(repeat
)。控制 draw call
BufferGeometryUtils.mergeVertices/mergeGeometries
)、使用 InstancedMesh、共享材质与纹理。合理的像素比与抗锯齿
renderer.setPixelRatio(Math.min(devicePixelRatio, 2))
,在高 DPI 设备防止过度渲染。只在需要时开阴影
castShadow/receiveShadow
的对象数量;降低阴影贴图大小;使用方向光阴影时优化正交相机包围盒。视锥裁剪与 LOD
THREE.LOD
。后处理节制
动画优化
AnimationMixer
控制播放/暂停;减少每帧大量对象属性写入;必要时改为 Shader 动画或 GPU Instancing。加载与并发
near
/far
)。renderer.shadowMap.enabled
;光、物体、地面未正确设置 castShadow/receiveShadow
。three/examples/*