threejs-scene-setup

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

three.js Scene Setup

three.js 场景搭建

Create the foundation of a three.js app: module loading, the scene/camera/renderer trio, the render loop, responsive resizing, and camera controls. Patterns target r165+ and are verified against r184.
创建three.js应用的基础:模块加载、场景/相机/渲染器三件套、渲染循环、响应式调整大小以及相机控制。模式针对r165+版本,并已在r184版本中验证。

When to use

使用场景

  • Use when bootstrapping a three.js scene, fixing a blank/black canvas, making the canvas responsive, setting up the animation loop, or adding
    OrbitControls
    .
  • Use when
    package.json
    depends on
    three
    and code does
    import * as THREE from 'three'
    .
When not to use: loading
.gltf
/
.glb
models or skinned animation →
threejs-gltf-loading
. Materials, lights, shadows, environment maps →
threejs-materials-lighting
. 2D rendering →
pixijs-rendering
.
  • 适用于初始化three.js场景、修复空白/黑色画布、实现画布响应式、设置动画循环或添加
    OrbitControls
    时。
  • 适用于
    package.json
    依赖
    three
    且代码中使用
    import * as THREE from 'three'
    的场景。
不适用于: 加载
.gltf
/
.glb
模型或蒙皮动画 → 使用
threejs-gltf-loading
。材质、灯光、阴影、环境贴图 → 使用
threejs-materials-lighting
。2D渲染 → 使用
pixijs-rendering

Core workflow

核心流程

  1. Load three.js as an ES module with an import map. Since r147 the bare specifier
    'three'
    and
    'three/addons/'
    must be mapped (in HTML or by a bundler). Addons (controls, loaders) live under
    three/addons/...
    .
  2. Create the trio. A
    Scene
    (root of the graph), a
    PerspectiveCamera(fov, aspect, near, far)
    moved back from the origin, and a
    WebGLRenderer
    whose
    domElement
    is in the DOM. Set size and
    pixelRatio
    .
  3. Add a mesh.
    new Mesh(geometry, material)
    and
    scene.add(mesh)
    . With a lit material you also need a light (see
    threejs-materials-lighting
    ).
  4. Drive a render loop with
    renderer.setAnimationLoop(fn)
    .
    It's the modern, WebXR-/WebGPU-safe replacement for hand-rolled
    requestAnimationFrame
    . Use a
    Clock
    for delta time.
  5. Handle resize so the camera aspect and renderer match the canvas; update
    camera.aspect
    , call
    updateProjectionMatrix()
    , and
    renderer.setSize(...)
    .
  6. Add
    OrbitControls
    for orbit/pan/zoom while developing. Confirm something actually renders (a lit cube, the controls responding) before assuming success.
  1. 通过导入映射以ES模块形式加载three.js。自r147版本起,必须映射裸标识符
    'three'
    'three/addons/'
    (在HTML中或通过打包工具)。扩展组件(控制器、加载器)位于
    three/addons/...
    路径下。
  2. 创建三件套
    Scene
    (场景图的根节点)、从原点向后移动的
    PerspectiveCamera(fov, aspect, near, far)
    ,以及其
    domElement
    已加入DOM的
    WebGLRenderer
    。设置尺寸和
    pixelRatio
  3. 添加网格。创建
    new Mesh(geometry, material)
    并调用
    scene.add(mesh)
    。若使用受光材质,还需要添加灯光(详见
    threejs-materials-lighting
    )。
  4. 使用
    renderer.setAnimationLoop(fn)
    驱动渲染循环
    。这是替代手动实现
    requestAnimationFrame
    的现代方案,支持WebXR/WebGPU。使用
    Clock
    获取增量时间。
  5. 处理窗口大小调整,使相机宽高比和渲染器匹配画布尺寸;更新
    camera.aspect
    ,调用
    updateProjectionMatrix()
    ,并执行
    renderer.setSize(...)
  6. 添加
    OrbitControls
    ,以便在开发时实现轨道漫游/平移/缩放操作。在确认渲染成功(如受光立方体、控制器响应)前,不要默认操作已完成。

Patterns

实现模式

1. HTML import map + module entry (no bundler)

1. HTML导入映射 + 模块入口(无打包工具)

html
<canvas id="c"></canvas>
<script type="importmap">
{
  "imports": {
    "three": "https://cdn.jsdelivr.net/npm/three@0.184.0/build/three.module.js",
    "three/addons/": "https://cdn.jsdelivr.net/npm/three@0.184.0/examples/jsm/"
  }
}
</script>
<script type="module" src="./main.js"></script>
With a bundler (Vite/webpack), skip the import map and just
npm i three
; the same
import
statements resolve.
html
<canvas id="c"></canvas>
<script type="importmap">
{
  "imports": {
    "three": "https://cdn.jsdelivr.net/npm/three@0.184.0/build/three.module.js",
    "three/addons/": "https://cdn.jsdelivr.net/npm/three@0.184.0/examples/jsm/"
  }
}
</script>
<script type="module" src="./main.js"></script>
若使用打包工具(Vite/webpack),可跳过导入映射,直接执行
npm i three
;相同的
import
语句会自动解析。

2. Scene + camera + renderer

2. 场景 + 相机 + 渲染器

js
// main.js
import * as THREE from 'three';

const canvas = document.querySelector('#c');
const renderer = new THREE.WebGLRenderer({ canvas, antialias: true });
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2)); // cap for perf
renderer.setSize(window.innerWidth, window.innerHeight);

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

const camera = new THREE.PerspectiveCamera(
  60,                                   // vertical field of view (degrees)
  window.innerWidth / window.innerHeight, // aspect
  0.1,                                  // near
  100                                   // far
);
camera.position.set(3, 2, 5);
camera.lookAt(0, 0, 0);

const cube = new THREE.Mesh(
  new THREE.BoxGeometry(1, 1, 1),
  new THREE.MeshNormalMaterial()        // unlit; shows orientation without a light
);
scene.add(cube);
js
// main.js
import * as THREE from 'three';

const canvas = document.querySelector('#c');
const renderer = new THREE.WebGLRenderer({ canvas, antialias: true });
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2)); // 为性能限制最大值
renderer.setSize(window.innerWidth, window.innerHeight);

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

const camera = new THREE.PerspectiveCamera(
  60,                                   // 垂直视野角度(度)
  window.innerWidth / window.innerHeight, // 宽高比
  0.1,                                  // 近裁剪面
  100                                   // 远裁剪面
);
camera.position.set(3, 2, 5);
camera.lookAt(0, 0, 0);

const cube = new THREE.Mesh(
  new THREE.BoxGeometry(1, 1, 1),
  new THREE.MeshNormalMaterial()        // 不受光材质;无需灯光即可显示方向
);
scene.add(cube);

3. The render loop (setAnimationLoop + Clock)

3. 渲染循环(setAnimationLoop + Clock)

js
const clock = new THREE.Clock();

renderer.setAnimationLoop(() => {
  const dt = clock.getDelta();          // seconds since last frame
  cube.rotation.x += dt;                // frame-rate independent
  cube.rotation.y += dt * 0.7;
  renderer.render(scene, camera);
});
// renderer.setAnimationLoop(null); // stop the loop
js
const clock = new THREE.Clock();

renderer.setAnimationLoop(() => {
  const dt = clock.getDelta();          // 距上一帧的时间(秒)
  cube.rotation.x += dt;                // 帧率无关
  cube.rotation.y += dt * 0.7;
  renderer.render(scene, camera);
});
// renderer.setAnimationLoop(null); // 停止循环

4. Responsive resize

4. 响应式大小调整

js
function onResize() {
  const w = window.innerWidth, h = window.innerHeight;
  camera.aspect = w / h;
  camera.updateProjectionMatrix();      // required after changing aspect
  renderer.setSize(w, h);
}
window.addEventListener('resize', onResize);
js
function onResize() {
  const w = window.innerWidth, h = window.innerHeight;
  camera.aspect = w / h;
  camera.updateProjectionMatrix();      // 更改宽高比后必须调用
  renderer.setSize(w, h);
}
window.addEventListener('resize', onResize);

5. OrbitControls (orbit / pan / zoom)

5. OrbitControls(轨道漫游/平移/缩放)

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

const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;          // inertial feel
controls.target.set(0, 0, 0);

renderer.setAnimationLoop(() => {
  controls.update();                    // needed every frame when damping is on
  renderer.render(scene, camera);
});
js
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';

const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;          // 惯性效果
controls.target.set(0, 0, 0);

renderer.setAnimationLoop(() => {
  controls.update();                    // 启用阻尼时必须每帧调用
  renderer.render(scene, camera);
});

Pitfalls

常见陷阱

  • Failed to resolve module specifier "three"
    → missing import map (or bundler config). Map both
    "three"
    and
    "three/addons/"
    ; addon paths must end with
    /
    .
  • Black canvas, no errors → camera is at the origin (inside/behind the object), or you used a lit material (
    MeshStandardMaterial
    ) with no light. Move the camera back; use
    MeshNormalMaterial
    /
    MeshBasicMaterial
    to verify geometry first.
  • Nothing animates → you never called
    renderer.render
    inside the loop, or you call
    setAnimationLoop
    but render outside it.
  • Stretched / squashed view on resize → you resized the renderer but didn't update
    camera.aspect
    +
    updateProjectionMatrix()
    .
  • Blurry or jagged on HiDPI → set
    renderer.setPixelRatio(...)
    ; cap it (≈2) so 4K/retina screens don't tank performance.
  • OrbitControls feel dead → with
    enableDamping = true
    you must call
    controls.update()
    every frame.
  • Old tutorials use
    <script src="three.min.js">
    → since r147 three.js ships ES modules only; use
    type="module"
    + import maps.
  • Failed to resolve module specifier "three"
    → 缺少导入映射(或打包工具配置)。需同时映射
    "three"
    "three/addons/"
    ;扩展组件路径必须以
    /
    结尾。
  • 黑色画布,无报错 → 相机位于原点(在物体内部/后方),或使用了受光材质(
    MeshStandardMaterial
    )但未添加灯光。将相机向后移动;先使用
    MeshNormalMaterial
    /
    MeshBasicMaterial
    验证几何体是否正确。
  • 无动画效果 → 未在循环内调用
    renderer.render
    ,或调用了
    setAnimationLoop
    但在循环外执行渲染。
  • 窗口调整大小时视图拉伸/挤压 → 调整了渲染器尺寸但未更新
    camera.aspect
    +
    updateProjectionMatrix()
  • 高DPI屏幕下模糊或锯齿 → 设置
    renderer.setPixelRatio(...)
    ;限制最大值(≈2),避免4K/视网膜屏幕导致性能下降。
  • OrbitControls无响应 → 启用
    enableDamping = true
    时必须每帧调用
    controls.update()
  • 旧教程使用
    <script src="three.min.js">
    → 自r147版本起three.js仅提供ES模块;需使用
    type="module"
    + 导入映射。

References

参考资料

  • For coordinate conventions, the scene-graph (
    Group
    , parent/child transforms,
    Object3D
    add/remove),
    OrthographicCamera
    for 2.5D, and disposing of geometries/materials/textures to avoid leaks, read
    references/scene-graph.md
    .
  • 关于坐标规范、场景图(
    Group
    、父子变换、
    Object3D
    添加/移除)、用于2.5D的
    OrthographicCamera
    ,以及销毁几何体/材质/纹理以避免内存泄漏,请阅读
    references/scene-graph.md

Related skills

相关技能

  • threejs-materials-lighting
    — give surfaces a lit look (lights, shadows, PBR).
  • threejs-gltf-loading
    — load 3D models and play their animations.
  • pixijs-rendering
    — 2D rendering in the browser.
  • fps-shooter
    — a 3D genre template that composes three.js skills.
  • threejs-materials-lighting
    — 实现表面受光效果(灯光、阴影、PBR)。
  • threejs-gltf-loading
    — 加载3D模型并播放动画。
  • pixijs-rendering
    — 浏览器中的2D渲染。
  • fps-shooter
    — 组合three.js技能的3D类型模板。