Skip to content

场景 Scene

在 Three.js 中,场景(Scene) 是所有可见对象的根容器。你看到的一切物体、光源、辅助对象,最终都必须存在于一个场景中,才能被渲染出来。

javascript
const scene = new THREE.Scene();

什么是 Scene

从技术角度讲,Scene 是所有 3D 物体、光源、相机和辅助工具的根容器 (Root Container)。可以把它想象成一个层级树结构(Scene Graph)的“根节点”。当你渲染画面时,Three.js 渲染器会从这个根节点开始,遍历其中包含的所有子节点,计算它们的位置、旋转、缩放和材质,最终绘制出画面。

在 Three.js 的 Hello World 中,场景永远是三大核心要素之一:

javascript
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 1000);
const renderer = new THREE.WebGLRenderer();

// 渲染循环的核心也就是这一句
renderer.render(scene, camera);

场景的核心属性

场景的核心属性有:

  • background:场景的背景颜色或贴图。
  • fog:场景的全局雾效。
  • environment:环境贴图。

1、background

默认情况下,Three.js 的背景是黑色的。你可以将其设置为纯色、纹理图片,甚至是全景图(CubeTexture)。

纯色背景:

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

图片背景 (Skybox): 通常使用 CubeTextureLoader 加载 6 张图来模拟天空盒。

javascript
const loader = new THREE.CubeTextureLoader();
const texture = loader.load([
  'px.jpg', 'nx.jpg',
  'py.jpg', 'ny.jpg',
  'pz.jpg', 'nz.jpg',
]);
scene.background = texture;

2、fog

雾效是增强 3D 场景纵深感和真实感的神器。它会让远处的物体逐渐褪色到某种颜色,模拟大气透视,同时也能巧妙地遮挡远处未渲染的区域(避免“穿帮”)。

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

Three.js 提供了两种雾:

  • THREE.Fog:雾的浓度随着距离线性增加。你需要定义雾的“开始距离”和“结束距离”。

    javascript
    // 颜色, 近处裁切距离, 远处裁切距离
    scene.fog = new THREE.Fog(0xffffff, 10, 50);
  • THREE.FogExp2:更接近真实的物理雾效,浓度随距离呈指数级增长。你只需要定义“密度”。

    javascript
    // 颜色, 密度
    scene.fog = new THREE.FogExp2(0xffffff, 0.02);

3、environment

这是现代 WebGL 开发中非常重要的属性(尤其是对于 PBR 材质)。scene.environment 允许你设置一个全局的纹理,场景中所有的“物理材质”(如 MeshStandardMaterial)都会反射这个纹理,而不需要为每个物体单独设置 envMap。

javascript
// 即使背景是纯色,物体表面也能反射出街道的倒影
scene.environment = pmremGenerator.fromScene( skyboxScene ).texture;

操作场景图 (The Scene Graph)

Three.js 的核心理念是“万物皆对象”(Object-Oriented Programming)。在场景中,所有物体、光源、相机、辅助工具,都是通过一个叫做“场景图”(Scene Graph)的层级结构来管理的。

1、坐标系与层级关系

在场景中,物体的位置 $(x, y, z)$ 默认是相对于世界坐标原点 $(0, 0, 0)$ 的。但是,如果你将物体 B 添加到物体 A 中(A.add(B)),而不是直接添加到 scene 中,那么物体 B 的坐标就是相对于 A 的局部坐标。

TIP

利用 THREE.Group 来管理复杂的物体组合。比如一辆车,你可以把车轮、车身都 add 到一个名为 carGroup 的组里,然后把 carGroup add 到 scene。这样移动车子时,轮子会自动跟着走。

Scene 继承自 THREE.Object3D,这意味着它拥有操纵子对象的所有方法。

2、添加与移除

这是最基本的操作。任何你想显示的东西(网格、灯光、辅助线)都必须被 add 进去。

javascript
const cube = new THREE.Mesh(geometry, material);
const light = new THREE.DirectionalLight(0xffffff);

// 添加到场景
scene.add(cube);
scene.add(light);

// 从场景移除 (注意:这不会释放内存,只是从画面中拿走)
scene.remove(cube);

3、查找与遍历

当场景变得复杂时,你可能找不到你的物体了。Three.js 提供了查找工具:

  • getObjectByName: 给物体起个名字,然后通过名字找到它。

    javascript
    cube.name = 'mySuperCube';
    // ... 在其他地方 ...
    const object = scene.getObjectByName('mySuperCube');
  • traverse (遍历): 这是一个强大的递归函数,它会遍历场景中的每一个后代节点。常用于批量修改材质或产生阴影。

    javascript
    scene.traverse(function(child) {
        // 判断子节点是否是 Mesh (网格)
        if (child.isMesh) {
            child.castShadow = true; // 开启所有物体的阴影投射
            child.receiveShadow = true;
        }
    });

性能优化

在管理 Scene 时,新手容易犯的一个错误是内存泄漏。

当你使用 scene.remove(object) 时,Three.js 仅仅是将物体从渲染列表中移除。它并没有销毁物体的几何体 (Geometry) 和材质 (Material)。

如果你频繁地创建和删除物体,记得手动清理资源:

javascript
scene.remove(cube);
// 必须手动释放显存
cube.geometry.dispose();
cube.material.dispose();