2、几何体与材质:BoxGeometry、SphereGeometry等基础几何体,MeshStandardMaterial、MeshPhongMaterial等材质,以及纹理贴图

好,咱们正式开始动手了。

上一章我们把 Three.js 的“舞台”搭好了——场景、相机、渲染器。现在该往舞台上放“演员”了。演员是谁?就是几何体(Geometry)和材质(Material)。

我个人习惯把几何体理解为“骨架”,材质理解为“皮肤”。骨架决定了形状,皮肤决定了看起来像什么。两者一结合,就成了一个网格对象(Mesh),也就是我们最终在屏幕上看到的东西。

2.1 基础几何体:从立方体到球体

Three.js 内置了很多基础几何体。说白了,就是帮你把顶点、面、法线这些底层数据都算好了,你直接拿来用就行。

BoxGeometry(立方体)——最常用的几何体,没有之一。

const geometry = new THREE.BoxGeometry(1, 1, 1);

三个参数分别是宽、高、深度。默认都是1,单位是“世界单位”,你可以理解为米。

我在项目中遇到过一个问题:用 BoxGeometry 做墙的时候,如果只给一个面贴图,其他面会显示黑色。后来发现是材质没设置透明,或者 UV 坐标没处理好。嗯,这个后面讲纹理时会细说。

SphereGeometry(球体)——做星球、灯泡、球状装饰必备。

const geometry = new THREE.SphereGeometry(1, 32, 16);

参数依次是:半径、水平分段数、垂直分段数。分段数越高,球越圆,但性能消耗也越大。32×16 是个不错的平衡点。

小技巧: 如果你做低多边形风格,分段数可以降到 8×6,会有一种复古的“像素球”效果。我早期做一个小游戏时就用过,反而成了特色。

其他常用的还有:

  • CylinderGeometry(圆柱体)——做柱子、罐子
  • ConeGeometry(圆锥体)——做锥形、屋顶
  • TorusGeometry(圆环体)——做甜甜圈、戒指
  • PlaneGeometry(平面)——做地面、墙面、广告牌

你想想看,这些几何体其实覆盖了 90% 的日常需求。复杂的模型可以靠组合它们来实现,或者直接导入外部模型文件。

2.2 材质:让物体“看起来”像什么

几何体决定了形状,材质决定了外观。Three.js 的材质系统很丰富,但初学者掌握两个就够了:MeshStandardMaterialMeshPhongMaterial

MeshStandardMaterial(标准材质)——这是我现在最常用的材质。它基于物理渲染(PBR),能模拟真实世界的光照效果。

const material = new THREE.MeshStandardMaterial({
    color: 0xff6600,
    roughness: 0.4,
    metalness: 0.1
});

三个核心属性:

  • color:颜色,十六进制或 Color 对象
  • roughness:粗糙度,0 到 1。0 像镜子,1 像砂纸
  • metalness:金属度,0 到 1。0 非金属,1 纯金属

MeshPhongMaterial(冯氏材质)——老牌材质,性能比 Standard 好一点,但效果没那么真实。

const material = new THREE.MeshPhongMaterial({
    color: 0x00aaff,
    shininess: 30,
    specular: 0x111111
});

shininess 控制高光强度,specular 控制高光颜色。如果你做卡通风格或者对性能要求高,可以用它。

我的建议: 除非有特殊需求,否则直接上 MeshStandardMaterial。它更接近真实世界,而且配合环境贴图效果极佳。我曾经在一个展厅项目中全部用 Standard 材质,客户反馈说“像照片一样真实”。

这里有个坑要注意:Standard 材质需要环境光和方向光才能显示正常。如果你只用了环境光,物体会显得很平,没有立体感。我刚开始学的时候就犯过这个错,调了半天颜色,结果发现是光照没配好。

2.3 纹理贴图:给物体穿上“衣服”

纯色材质太单调了。真实世界的东西都有纹理——木头的纹路、砖墙的凹凸、金属的划痕。纹理贴图就是干这个的。

Three.js 用 TextureLoader 加载图片,然后赋给材质的 map 属性。

const textureLoader = new THREE.TextureLoader();
const texture = textureLoader.load('wood.jpg');

const material = new THREE.MeshStandardMaterial({
    map: texture
});

就这么简单。但实际项目中,纹理贴图远不止这一种。常用的还有:

贴图类型 属性名 作用
颜色贴图 map 定义物体表面颜色
法线贴图 normalMap 模拟表面凹凸细节,不增加顶点
粗糙度贴图 roughnessMap 控制不同区域的粗糙程度
金属度贴图 metalnessMap 控制不同区域的金属感
环境光遮蔽贴图 aoMap 增加阴影细节,让物体更真实

我曾经做一个古建筑复原项目,只用颜色贴图时,木梁看起来像塑料。后来加了法线贴图和粗糙度贴图,瞬间有了木头的质感。嗯,这就是纹理的威力。

注意: 纹理图片的尺寸最好是 2 的幂次方,比如 512×512、1024×1024、2048×2048。否则 Three.js 会报 warning,而且性能会下降。我踩过这个坑,一张 800×600 的图片让帧率掉了 20%。

还有一个常用技巧:纹理重复。比如做地板,一张小瓷砖图片重复铺满地面。

texture.wrapS = THREE.RepeatWrapping;
texture.wrapT = THREE.RepeatWrapping;
texture.repeat.set(4, 4);  // 水平和垂直各重复4次

这样一张 256×256 的小图就能铺满 4×4 的世界单位,既省资源又清晰。

2.4 组合起来:一个完整的例子

说了这么多,咱们来写一个完整的例子。创建一个带纹理的立方体和一个金属球体。

// 立方体:带木纹
const boxGeo = new THREE.BoxGeometry(2, 2, 2);
const woodTexture = textureLoader.load('wood.jpg');
const boxMat = new THREE.MeshStandardMaterial({
    map: woodTexture,
    roughness: 0.8,
    metalness: 0.0
});
const box = new THREE.Mesh(boxGeo, boxMat);
box.position.set(-2, 1, 0);
scene.add(box);

// 球体:金属质感
const sphereGeo = new THREE.SphereGeometry(1, 32, 32);
const sphereMat = new THREE.MeshStandardMaterial({
    color: 0xcccccc,
    roughness: 0.2,
    metalness: 0.9
});
const sphere = new THREE.Mesh(sphereGeo, sphereMat);
sphere.position.set(2, 1, 0);
scene.add(sphere);

你看,代码结构很清晰:创建几何体 → 创建材质 → 组合成 Mesh → 设置位置 → 添加到场景。

最后别忘了加光照,否则材质再漂亮也显示不出来。

const ambientLight = new THREE.AmbientLight(0x404040);
scene.add(ambientLight);

const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
directionalLight.position.set(5, 10, 7);
scene.add(directionalLight);

好了,这一章的内容就这些。几何体是骨架,材质是皮肤,纹理是细节。三者配合,就能做出很棒的 3D 物体。

下一章我们聊聊光照和阴影——没有光,再好的材质也白搭。