Skip to main content

概览

写在前面

在高级前端或图形开发面试中,仅仅会调用 Three.js 的 API 拼凑一个场景是远远不够的。面试官更看重你对 WebGL 渲染流水线、矩阵变换、场景图(Scene Graph)、性能瓶颈与优化方案 的深度理解。

Three.js 本质上是对原生 WebGL 的高度面向对象封装,它屏蔽了复杂的着色器(Shader)编写、上下文管理和底层 Buffer 数据绑定,提供了一套更符合人类直觉的 3D 编程模型。

注意:Three.js 默认使用右手坐标系(x轴向右,y轴向上,z轴向屏幕外),这与 OpenGL/WebGL 的默认坐标系一致。


核心架构:场景图与渲染流

1. 场景图 (Scene Graph) 与 矩阵变换

这是 Three.js 中最重要的架构概念。整个 3D 场景在底层是一个有向无环图 (DAG),我们称之为场景图。

  • 场景图中的每一个节点都是一个 Object3D 实例(Mesh, Camera, Light, Group 都继承自它)。
  • 局部矩阵与世界矩阵:每个对象都有一个局部变换矩阵(记录相对于父节点的平移、旋转、缩放)。当渲染器进行渲染时,它会从 Scene 根节点开始递归遍历整棵树,把局部矩阵与父节点的世界矩阵相乘,计算出当前对象的世界矩阵(World Matrix),最终决定它在 3D 空间中的绝对位置。

2. 相机 (Camera) 与 视锥体

决定场景中哪些角度的内容会显示出来。最常用的是基于透视投影的 PerspectiveCamera,它会模拟人眼的视觉效果(近大远小)。

🎯 面试考点:视锥体裁剪 (Frustum Culling) 透视相机的四个参数(fov, aspect, near, far)定义了一个截头锥体(Frustum)。在每一帧渲染前,Three.js 会计算物体的包围盒(Bounding Box/Sphere),如果包围盒完全在这个椎体之外,该物体就不会被提交给 GPU 渲染。这是 Three.js 默认开启的核心性能优化手段。

3. 渲染器 (Renderer) 底层工作流

执行 renderer.render(scene, camera) 时,底层发生了什么?

  1. 更新矩阵:递归遍历 Scene Graph,更新所有有变动对象的世界矩阵。
  2. 收集与分类:收集所有的光源、网格对象(Mesh)。
  3. 视椎体裁剪:丢弃不在相机视野内的 Mesh。
  4. 排序 (Sorting):将不透明对象(从前向后渲染,利用 GPU 深度测试 Early-Z 剔除被遮挡像素)和透明对象(从后向前渲染,利用 Alpha Blending 正确混合颜色)分别排序。
  5. Draw Call 提交:将数据绑定到 WebGL 的 Buffer 中,挂载 Shader,向 GPU 发出 Draw Call 绘制指令。

场景基石:物体组成要素

在 WebGL 层,只有点、线、三角形。Three.js 通过以下概念将其抽象:

几何体 (Geometry / BufferGeometry)

定义了物体的形状和顶点数据。

  • 面试注意点:现代 Three.js 已经废弃了低效的 Geometry,全面拥抱 BufferGeometry。它的底层直接对应 WebGL 的 BufferAttribute(如 position, normal, uv),数据以一维强类型数组(TypedArray)的形式存储在内存中,极大提升了向 GPU 传输数据的效率。

材质 (Material) 与 纹理 (Texture)

  • 材质:定义了几何体表面的光学属性(如何对光照产生反应)。比如不受光照影响的 MeshBasicMaterial,以及基于物理渲染的 MeshStandardMaterial (PBR)。
  • 纹理:包裹到几何体表面的图像数据。一张 PBR 材质通常需要组合多张纹理(如颜色贴图、法线贴图、粗糙度贴图)。

网格 (Mesh)

Mesh = Geometry + Material。它是 3D 场景中最常见的可见对象。除了 Mesh,还有 Points(粒子点云)、Line(线段)等基类。


交互与动画

1. 动画驱动 (RequestAnimationFrame)

3D 场景的动画本质上是不断改变物体的属性并重新调用 render。必须使用 requestAnimationFrame 而不是 setInterval,以保证屏幕刷新率同步并避免后台标签页的无效渲染。复杂骨骼动画通常由 AnimationMixer 驱动。

2. 射线拾取 (Raycaster)

🎯 面试高频题:如何在 3D 场景中实现鼠标点击物体的交互? 因为屏幕是 2D 的,场景是 3D 的,直接绑定 click 事件是无效的。需要使用光线投射技术 (Raycasting)

  1. 将鼠标在屏幕上的 2D 坐标归一化(映射到 -1 到 1 的标准化设备坐标 NDC)。
  2. 基于相机的位置和方向,从鼠标位置向 3D 空间发射一条射线。
  3. 计算射线与场景中物体包围盒/三角面的相交情况,按距离排序返回结果。

进阶:性能优化

3D 应用的性能瓶颈通常在于 Draw Call 过多和显存溢出。关于如何进行几何体合并、实例化渲染、纹理模型压缩以及内存泄漏管理等大厂必考的优化方案,详见 性能优化


知识延伸导航