Three.js 基础概念 #

Three.js 的核心是三个基本概念:场景(Scene)、相机(Camera)和渲染器(Renderer)。理解这三者的关系是学习 Three.js 的第一步。

核心三要素关系 #

text
┌─────────────────────────────────────────────────────────────┐
│                    Three.js 核心架构                          │
├─────────────────────────────────────────────────────────────┤
│                                                              │
│    ┌─────────────┐                                          │
│    │    场景      │                                          │
│    │   Scene     │◄──────────────────┐                      │
│    └──────┬──────┘                   │                      │
│           │                          │                      │
│           │ 包含所有 3D 对象           │                      │
│           │                          │                      │
│           ▼                          │                      │
│    ┌─────────────┐                   │                      │
│    │    相机      │                   │                      │
│    │   Camera    │───────────────────┤                      │
│    └──────┬──────┘                   │                      │
│           │                          │                      │
│           │ 决定观察角度和范围         │                      │
│           │                          │                      │
│           ▼                          │                      │
│    ┌─────────────┐                   │                      │
│    │   渲染器     │                   │                      │
│    │  Renderer   │───────────────────┘                      │
│    └─────────────┘                                          │
│           │                                                 │
│           │ 将场景渲染到屏幕                                  │
│           │                                                 │
│           ▼                                                 │
│    ┌─────────────┐                                          │
│    │   Canvas    │                                          │
│    │   画布      │                                          │
│    └─────────────┘                                          │
│                                                              │
└─────────────────────────────────────────────────────────────┘

场景(Scene) #

场景是所有 3D 对象的容器,相当于一个虚拟的 3D 空间。所有的物体、光源、相机都需要添加到场景中才能被渲染。

创建场景 #

javascript
import * as THREE from 'three';

const scene = new THREE.Scene();

场景属性 #

背景色 #

javascript
scene.background = new THREE.Color(0x000000);

scene.background = new THREE.Color(0.5, 0.5, 0.5);

scene.background = new THREE.Color('skyblue');

背景纹理 #

javascript
const textureLoader = new THREE.TextureLoader();
const texture = textureLoader.load('sky.jpg');
scene.background = texture;

雾效果 #

javascript
scene.fog = new THREE.Fog(0x000000, 1, 100);

scene.fog = new THREE.FogExp2(0x000000, 0.05);

场景方法 #

添加对象 #

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

const light = new THREE.DirectionalLight(0xffffff, 1);
scene.add(light);

移除对象 #

javascript
scene.remove(mesh);

查找对象 #

javascript
const object = scene.getObjectByName('myObject');

const objects = scene.getObjectsByProperty('material', material);

遍历场景 #

javascript
scene.traverse((object) => {
  if (object.isMesh) {
    object.material.wireframe = true;
  }
});

场景图结构 #

text
Scene
├── Mesh (立方体)
│   └── Material
├── Mesh (球体)
│   └── Material
├── Group (组)
│   ├── Mesh (子物体1)
│   └── Mesh (子物体2)
├── DirectionalLight
├── AmbientLight
└── Camera (相机通常也添加到场景)

相机(Camera) #

相机决定了观察者从哪个角度、以什么方式观察场景。Three.js 提供了多种相机类型。

透视相机(PerspectiveCamera) #

透视相机模拟人眼视觉,近大远小,是最常用的相机类型。

javascript
const camera = new THREE.PerspectiveCamera(
  75,                                     // FOV:视角(度)
  window.innerWidth / window.innerHeight, // Aspect:宽高比
  0.1,                                    // Near:近裁剪面
  1000                                    // Far:远裁剪面
);

参数详解 #

text
          FOV (视角)
            ▲
           /|\
          / | \
         /  |  \
        /   |   \
       /    |    \
      /     |     \
     /      |      \
    /       |       \
   /        |        \
  /    Near Plane    \
 /      (近裁剪面)    \
/_____________________\
      Far Plane
      (远裁剪面)
参数 描述 推荐值
FOV 视角范围(度) 45-90
Aspect 宽高比 window.innerWidth / window.innerHeight
Near 近裁剪面距离 0.1
Far 远裁剪面距离 1000-10000

相机位置 #

javascript
camera.position.set(0, 0, 5);

camera.position.x = 0;
camera.position.y = 2;
camera.position.z = 5;

camera.lookAt(0, 0, 0);

camera.lookAt(new THREE.Vector3(0, 0, 0));

正交相机(OrthographicCamera) #

正交相机没有透视效果,物体大小不随距离变化,常用于 2D 游戏、CAD 软件等。

javascript
const frustumSize = 10;
const aspect = window.innerWidth / window.innerHeight;

const camera = new THREE.OrthographicCamera(
  frustumSize * aspect / -2,  // Left
  frustumSize * aspect / 2,   // Right
  frustumSize / 2,            // Top
  frustumSize / -2,           // Bottom
  0.1,                        // Near
  1000                        // Far
);

参数详解 #

text
    Top
     ▲
     │
Left ├──┤ Right
     │
     ▼
   Bottom

正交投影视图:物体大小不随距离变化

相机对比 #

特性 透视相机 正交相机
透视效果
近大远小
适用场景 3D 游戏、产品展示 2D 游戏、CAD
性能 相同 相同
真实感

相机控制 #

javascript
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';

const controls = new OrbitControls(camera, renderer.domElement);

controls.enableDamping = true;
controls.dampingFactor = 0.05;
controls.screenSpacePanning = false;
controls.minDistance = 1;
controls.maxDistance = 100;
controls.maxPolarAngle = Math.PI / 2;

相机动画 #

javascript
function animate() {
  requestAnimationFrame(animate);
  
  camera.position.x = Math.sin(Date.now() * 0.001) * 5;
  camera.position.z = Math.cos(Date.now() * 0.001) * 5;
  camera.lookAt(0, 0, 0);
  
  renderer.render(scene, camera);
}
animate();

渲染器(Renderer) #

渲染器负责将场景和相机的内容绘制到屏幕上。

创建渲染器 #

javascript
const renderer = new THREE.WebGLRenderer({
  antialias: true,
  alpha: true,
  powerPreference: 'high-performance'
});

renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(window.devicePixelRatio);

document.body.appendChild(renderer.domElement);

渲染器配置选项 #

javascript
const renderer = new THREE.WebGLRenderer({
  canvas: document.getElementById('canvas'),
  antialias: true,
  alpha: false,
  premultipliedAlpha: true,
  preserveDrawingBuffer: false,
  powerPreference: 'default',
  failIfMajorPerformanceCaveat: false,
  depth: true,
  stencil: true
});
选项 描述 默认值
canvas 指定 Canvas 元素 自动创建
antialias 抗锯齿 false
alpha 透明背景 false
powerPreference GPU 偏好 default
depth 深度缓冲 true
stencil 模板缓冲 true

渲染器方法 #

设置尺寸 #

javascript
renderer.setSize(width, height);

renderer.setSize(width, height, false);

设置像素比 #

javascript
renderer.setPixelRatio(window.devicePixelRatio);

renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));

设置背景色 #

javascript
renderer.setClearColor(0x000000);

renderer.setClearColor(0x000000, 1);

渲染场景 #

javascript
renderer.render(scene, camera);

清除缓冲 #

javascript
renderer.clear();

renderer.clearColor();
renderer.clearDepth();
renderer.clearStencil();

高级渲染设置 #

阴影 #

javascript
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;

色调映射 #

javascript
renderer.toneMapping = THREE.ACESFilmicToneMapping;
renderer.toneMappingExposure = 1.0;

输出编码 #

javascript
renderer.outputColorSpace = THREE.SRGBColorSpace;

响应式处理 #

javascript
window.addEventListener('resize', () => {
  camera.aspect = window.innerWidth / window.innerHeight;
  camera.updateProjectionMatrix();
  
  renderer.setSize(window.innerWidth, window.innerHeight);
  renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
});

完整示例 #

基础场景 #

javascript
import * as THREE from 'three';

const scene = new THREE.Scene();
scene.background = new THREE.Color(0x1a1a2e);

const camera = new THREE.PerspectiveCamera(
  75,
  window.innerWidth / window.innerHeight,
  0.1,
  1000
);
camera.position.z = 5;

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 geometry = new THREE.BoxGeometry(1, 1, 1);
const material = new THREE.MeshBasicMaterial({ color: 0x00ff88 });
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);

function animate() {
  requestAnimationFrame(animate);
  
  cube.rotation.x += 0.01;
  cube.rotation.y += 0.01;
  
  renderer.render(scene, camera);
}
animate();

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

带光照的场景 #

javascript
import * as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';

const scene = new THREE.Scene();
scene.background = new THREE.Color(0x1a1a2e);

const camera = new THREE.PerspectiveCamera(
  75,
  window.innerWidth / window.innerHeight,
  0.1,
  1000
);
camera.position.set(2, 2, 5);

const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
document.body.appendChild(renderer.domElement);

const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;

const ambientLight = new THREE.AmbientLight(0x404040, 0.5);
scene.add(ambientLight);

const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
directionalLight.position.set(5, 5, 5);
directionalLight.castShadow = true;
scene.add(directionalLight);

const geometry = new THREE.BoxGeometry(1, 1, 1);
const material = new THREE.MeshStandardMaterial({ color: 0x00ff88 });
const cube = new THREE.Mesh(geometry, material);
cube.castShadow = true;
cube.receiveShadow = true;
scene.add(cube);

const planeGeometry = new THREE.PlaneGeometry(10, 10);
const planeMaterial = new THREE.MeshStandardMaterial({ color: 0x333333 });
const plane = new THREE.Mesh(planeGeometry, planeMaterial);
plane.rotation.x = -Math.PI / 2;
plane.position.y = -0.5;
plane.receiveShadow = true;
scene.add(plane);

function animate() {
  requestAnimationFrame(animate);
  
  controls.update();
  
  cube.rotation.x += 0.01;
  cube.rotation.y += 0.01;
  
  renderer.render(scene, camera);
}
animate();

坐标系统 #

右手坐标系 #

Three.js 使用右手坐标系:

text
        Y (上)
        ▲
        │
        │
        │
        └────────► X (右)
       /
      /
     /
    ▼
   Z (前/朝向观察者)

坐标单位 #

javascript
mesh.position.set(1, 2, 3);

mesh.position.x = 1;
mesh.position.y = 2;
mesh.position.z = 3;

旋转 #

javascript
mesh.rotation.set(Math.PI / 4, Math.PI / 2, 0);

mesh.rotation.x = Math.PI / 4;
mesh.rotation.y = Math.PI / 2;
mesh.rotation.z = 0;

缩放 #

javascript
mesh.scale.set(1, 2, 1);

mesh.scale.x = 1;
mesh.scale.y = 2;
mesh.scale.z = 1;

渲染循环 #

requestAnimationFrame #

javascript
function animate() {
  requestAnimationFrame(animate);
  
  renderer.render(scene, camera);
}
animate();

带时间控制 #

javascript
const clock = new THREE.Clock();

function animate() {
  requestAnimationFrame(animate);
  
  const elapsedTime = clock.getElapsedTime();
  
  cube.rotation.y = elapsedTime;
  
  renderer.render(scene, camera);
}
animate();

固定帧率 #

javascript
const clock = new THREE.Clock();
const targetFPS = 60;
const interval = 1 / targetFPS;
let delta = 0;

function animate() {
  requestAnimationFrame(animate);
  
  delta += clock.getDelta();
  
  if (delta >= interval) {
    delta = delta % interval;
    
    cube.rotation.y += 0.01;
    
    renderer.render(scene, camera);
  }
}
animate();

常见问题 #

场景是黑屏 #

  1. 检查相机位置是否正确
  2. 检查物体是否在相机视野内
  3. 检查光照是否添加
  4. 检查材质是否正确

物体显示不完整 #

  1. 检查相机的 near 和 far 参数
  2. 检查物体是否超出裁剪范围

性能问题 #

  1. 降低像素比:renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
  2. 减少物体数量
  3. 使用简化几何体
  4. 开启视锥裁剪

下一步 #

现在你已经掌握了 Three.js 的核心三要素,接下来学习 几何体,创建各种 3D 形状!

最后更新:2026-03-28