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,肉眼几乎看不出区别,帧率却翻了一倍。
4.2 材质(Material)—— 表面的灵魂
几何体决定了形状,材质决定了它看起来像什么。是金属?是塑料?还是粗糙的石头?
Three.js提供了好几种材质类型,我挑几个最常用的说说:
- MeshStandardMaterial —— 标准材质,支持PBR(基于物理的渲染)。我90%的项目都用它。
- MeshPhongMaterial —— 冯氏材质,性能比标准材质好一点,但效果差一些。
- MeshBasicMaterial —— 基础材质,不受光照影响。适合做UI元素或调试用。
const material = new THREE.MeshStandardMaterial({
color: 0x44aa88,
roughness: 0.4,
metalness: 0.6
});
这里有个坑:roughness和metalness这两个参数,很多人调不好。粗糙度越高,表面越哑光;金属度越高,反射越强。我曾经做一个机械零件展示,金属度设到0.9,结果整个物体亮得像个镜子,完全看不清细节。后来降到0.3,才把质感做出来。
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);
这段代码跑起来,你应该能看到一个橙色的金属质感立方体。如果看不到,先检查相机位置,再检查光照方向。我当年第一次写这个,忘了加光照,盯着屏幕看了十分钟,心想「这材质怎么是黑的?」——嗯,谁还没犯过这种错呢。
最后说一句:几何体、材质、网格、光照,这四个东西是Three.js的基石。你花时间把它们搞透,后面学纹理、动画、粒子系统都会轻松很多。别急,慢慢来。