3、场景核心:场景(Scene)、相机(Camera)、渲染器(Renderer)三要素详解、坐标系概念

好,咱们今天聊点真正核心的东西。

Three.js 说白了,就是一个帮你把 3D 世界搬到浏览器里的工具。但不管它多强大,都离不开三个最基础的零件:场景相机渲染器

我刚开始学的时候,总觉得这三样东西很抽象。后来带项目时发现,很多新手出问题,都是因为没搞懂它们各自管什么。今天咱们就把它们掰开揉碎了讲清楚。

3.1 场景(Scene)—— 你的虚拟世界容器

场景是什么?说白了,它就是一个大容器

你往里面放模型、放灯光、放辅助线,它都接着。你可以把它想象成一个空房间,所有 3D 物体都是房间里的家具。

核心要点:场景本身不渲染任何东西,它只负责「装」。真正干活的是渲染器。

创建场景非常简单:

const scene = new THREE.Scene();

就这一行。但别小看它。我在项目中遇到过一个问题:场景里明明有物体,但就是看不见。查了半天,发现是场景的 background 没设置,默认是黑色,而物体也是黑色……嗯,你想想看,黑色物体在黑色背景里,当然看不见。

所以,我建议你一开始就给场景加个背景色:

scene.background = new THREE.Color(0x87CEEB); // 天蓝色

这样至少能确认场景是「活」的。

3.2 相机(Camera)—— 你的眼睛

场景有了,但你看不到它。为什么?因为没有「眼睛」。

相机就是你的眼睛。它决定了你从哪个角度、以什么方式观察这个 3D 世界。

Three.js 里最常用的有两种相机:

相机类型 特点 适用场景
透视相机(PerspectiveCamera) 近大远小,符合人眼视觉 游戏、建筑漫游、产品展示
正交相机(OrthographicCamera) 物体大小不随距离变化 2D 游戏、UI 界面、技术图纸

我个人习惯用透视相机,因为它看起来更真实。创建方式如下:

const camera = new THREE.PerspectiveCamera(
  75,          // 视野角度(FOV),越大看到的范围越广
  window.innerWidth / window.innerHeight, // 宽高比
  0.1,         // 近裁剪面
  1000         // 远裁剪面
);
camera.position.set(0, 2, 5); // 把相机放在 (0, 2, 5) 位置
camera.lookAt(0, 0, 0);       // 让相机看向原点

避坑指南:我曾经把近裁剪面设成 0.001,远裁剪面设成 100000。结果渲染时出现了严重的 Z-fighting(深度冲突),物体表面闪烁得像坏掉的电视。后来我改成 0.1 和 1000,问题就解决了。记住:裁剪面范围不是越大越好。

3.3 渲染器(Renderer)—— 把世界画出来

场景和相机都有了,但还差最后一步:把画面「画」到屏幕上。

这就是渲染器的工作。它读取场景里的所有数据,结合相机的视角,最终生成一张 2D 图片,显示在你的浏览器里。

const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

这里要注意:renderer.domElement 是一个 canvas 元素。你把它挂到页面上,才能看到画面。

渲染器还有一个重要方法:render。它负责执行一次渲染:

renderer.render(scene, camera);

但如果你只调用一次,画面是静止的。要实现动画,你需要一个循环:

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

注意:千万别忘了在窗口大小变化时更新渲染器。我见过不少项目,窗口一拉大,画面就变形了。加个监听事件就能解决:

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

3.4 坐标系概念—— 你的空间定位系统

好了,三要素讲完了。但还有一个基础概念必须搞懂:坐标系

Three.js 使用的是右手坐标系。什么意思?

  • X 轴:向右为正
  • Y 轴:向上为正
  • Z 轴:朝向屏幕外为正(也就是「朝你」的方向)

你想想看,如果你把右手伸出来,拇指指向右(X 正),食指指向上(Y 正),中指指向自己(Z 正),这就是右手坐标系。

为什么强调这个?因为我在项目中遇到过一个问题:从 Blender 导出的模型,导入 Three.js 后方向全乱了。后来发现,Blender 用的是 Z 轴向上,而 Three.js 是 Y 轴向上。这就是坐标系不一致导致的。

经验之谈:如果你从其他软件导入模型,一定要先确认它的坐标系。否则模型可能「躺」在地上,或者「飞」在天上。

另外,场景中每个物体都有自己的局部坐标系。当你旋转或移动物体时,实际上是改变它相对于父级坐标系的位置。这个后面讲变换时再细说。

3.5 三要素的协作流程

最后,咱们总结一下这三样东西是怎么配合的:

  1. 场景:装东西(模型、灯光、辅助线)
  2. 相机:决定怎么看(位置、角度、视野)
  3. 渲染器:把看到的画面画出来

每次调用 renderer.render(scene, camera),就是一次完整的「拍照」过程。

我个人习惯把这三要素的创建放在一起,作为项目的初始化代码。这样结构清晰,后期维护也方便。

小技巧:调试时可以在场景里加一个 AxesHelper,它能显示坐标轴方向,帮你快速定位问题:

const axesHelper = new THREE.AxesHelper(5);
scene.add(axesHelper);

红色是 X 轴,绿色是 Y 轴,蓝色是 Z 轴。一眼就能看出物体在哪个方向。

嗯,今天就先聊到这儿。下一章咱们开始往场景里放真正的物体——几何体。到时候你会发现,有了这三要素打底,后面的事情就顺多了。