首页  |  学习总览  |  ← 返回专题总览 进阶专题 16 · Three.js

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.jsBabylon.js
定位轻量、灵活完整游戏引擎
学习曲线平缓陡峭
物理引擎需引入内置
TypeScript支持原生
社区更大较小
适用场景可视化、展示游戏、复杂应用
Q2:Three.js 性能优化有哪些方法?
查看答案 ▼
  • 使用 InstancedMesh 批量渲染
  • 合并几何体减少 draw call
  • 使用 LOD 多层次细节
  • 纹理压缩(KTX2/Basis)
  • 开启 Frustum Culling
  • 使用 Web Worker 处理物理
  • 降低像素比(pixelRatio)
  • 使用 Draco 压缩模型