4、Three.js进阶:几何体、材质、网格、光照详解

好,咱们进入Three.js的核心地带了。

前面你学会了怎么搭一个3D场景,怎么让相机动起来。但说实话,那些都是基本功。真正让画面「活」起来的,是几何体、材质、网格和光照这四个东西。我当年刚接触Three.js时,花了好长时间才搞明白它们之间的关系。今天咱们一次性说透。

4.1 几何体(Geometry)—— 形状的骨架

几何体是什么?说白了就是一堆顶点数据。它定义了物体的形状,但不包含颜色、纹理这些视觉信息。

Three.js内置了很多常用几何体,比如立方体、球体、圆柱体。我建议你刚开始时多用这些现成的,别急着自己造轮子。

// 创建一个半径为1的球体,分段数32
const geometry = new THREE.SphereGeometry(1, 32, 32);

这里的分段数是什么意思?你想想看,球体其实是用很多小平面拼出来的。分段数越高,球越圆,但性能开销也越大。我在项目中做过一个地球模型,分段数设到64,结果手机端直接卡成PPT。后来降到24,肉眼几乎看不出区别,帧率却翻了一倍。

我的习惯:桌面端分段数32-48,移动端16-24。除非你的模型需要特写镜头,否则别贪多。

4.2 材质(Material)—— 表面的灵魂

几何体决定了形状,材质决定了它看起来像什么。是金属?是塑料?还是粗糙的石头?

Three.js提供了好几种材质类型,我挑几个最常用的说说:

  • MeshStandardMaterial —— 标准材质,支持PBR(基于物理的渲染)。我90%的项目都用它。
  • MeshPhongMaterial —— 冯氏材质,性能比标准材质好一点,但效果差一些。
  • MeshBasicMaterial —— 基础材质,不受光照影响。适合做UI元素或调试用。
const material = new THREE.MeshStandardMaterial({
  color: 0x44aa88,
  roughness: 0.4,
  metalness: 0.6
});

这里有个坑:roughnessmetalness这两个参数,很多人调不好。粗糙度越高,表面越哑光;金属度越高,反射越强。我曾经做一个机械零件展示,金属度设到0.9,结果整个物体亮得像个镜子,完全看不清细节。后来降到0.3,才把质感做出来。

注意:MeshStandardMaterial需要配合合适的光照才能出效果。如果你发现材质看起来发黑,先检查光照,别急着调颜色。

4.3 网格(Mesh)—— 几何体+材质的结合体

网格就是把几何体和材质组合在一起的东西。你可以把它理解成「一个完整的3D物体」。

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

就这么简单。但这里有个细节:一个几何体可以被多个网格共用,材质也可以被多个网格共用。我在做批量渲染时经常这么干,能省不少内存。

举个例子,你要在场景里放100个球,每个球颜色不同。你可以创建一个几何体,然后创建100个材质,每个材质颜色不同,再创建100个网格。这样几何体只存一份,材质存100份。反过来,如果你每个球形状不同但颜色相同,那就材质共用,几何体各存各的。

核心思路:能共用的就别重复创建。这是优化Three.js性能的第一条铁律。

4.4 光照(Light)—— 场景的灵魂

没有光照,材质再漂亮也白搭。Three.js的光照系统其实挺丰富的,我常用的就这几种:

光照类型 特点 适用场景
AmbientLight 环境光,均匀照亮所有面 作为基础照明,避免死黑
DirectionalLight 平行光,模拟太阳 室外场景、主光源
PointLight 点光源,从一点向四周发光 灯泡、烛光效果
SpotLight 聚光灯,锥形光束 舞台灯光、手电筒
// 一个典型的光照组合
const ambient = new THREE.AmbientLight(0x404040);
const sun = new THREE.DirectionalLight(0xffffff, 1);
sun.position.set(10, 20, 10);
scene.add(ambient, sun);

我个人习惯:先用环境光把场景整体照亮,避免完全黑暗的区域。然后用一个方向光做主光源,再根据需要加辅助光。这样出来的效果比较自然。

嗯,这里要注意:光照数量不是越多越好。每增加一个光源,渲染开销就会增加。我在一个展厅项目里,为了追求效果加了8个点光源,结果帧率直接掉到20。后来改成2个方向光+1个环境光,效果差不多,帧率回到了60。

4.5 把它们组合起来

好了,现在咱们把四个东西串起来,写一个完整的例子:

// 1. 创建几何体
const geometry = new THREE.BoxGeometry(2, 2, 2);

// 2. 创建材质
const material = new THREE.MeshStandardMaterial({
  color: 0xff6600,
  roughness: 0.3,
  metalness: 0.7
});

// 3. 组合成网格
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);

// 4. 添加光照
const ambient = new THREE.AmbientLight(0x404040);
const light = new THREE.DirectionalLight(0xffffff, 1);
light.position.set(5, 10, 7);
scene.add(ambient, light);

这段代码跑起来,你应该能看到一个橙色的金属质感立方体。如果看不到,先检查相机位置,再检查光照方向。我当年第一次写这个,忘了加光照,盯着屏幕看了十分钟,心想「这材质怎么是黑的?」——嗯,谁还没犯过这种错呢。

避坑指南:调试时先用MeshBasicMaterial,它不受光照影响,能帮你快速确认几何体和相机是否正常。确认没问题了,再换成MeshStandardMaterial加光照。

最后说一句:几何体、材质、网格、光照,这四个东西是Three.js的基石。你花时间把它们搞透,后面学纹理、动画、粒子系统都会轻松很多。别急,慢慢来。