4、几何体与材质:内置几何体与基础材质
好,咱们进入第四课。说实话,这一章是 Three.js 里最「出活儿」的部分。你想想看,前面几章我们搭好了场景、架好了相机、点亮了灯光,但屏幕上空荡荡的,啥也没有。这一章,就是往舞台上放演员的时候了。
几何体(Geometry)和材质(Material),说白了就是物体的「骨架」和「皮肤」。骨架决定形状,皮肤决定看起来什么样。我刚开始学的时候,总把这两者混为一谈,后来踩了不少坑才明白——它们是两个独立的东西,可以自由组合。
4.1 内置几何体:开箱即用的形状
Three.js 给我们准备了一套非常齐全的「乐高积木」。你不用自己画顶点、算法线,直接 new 一下就能用。我个人最常用的有这么几个:
| 几何体名称 | 构造函数 | 常用参数 | 典型用途 |
|---|---|---|---|
| 立方体 | BoxGeometry | width, height, depth | 盒子、墙壁、骰子 |
| 球体 | SphereGeometry | radius, widthSegments, heightSegments | 星球、球类、灯泡 |
| 圆柱体 | CylinderGeometry | radiusTop, radiusBottom, height | 柱子、罐子、树干 |
| 圆环 | TorusGeometry | radius, tube, radialSegments | 甜甜圈、戒指、轮胎 |
| 平面 | PlaneGeometry | width, height | 地面、墙面、图片展示 |
| 圆锥 | ConeGeometry | radius, height | 锥体、屋顶、路障 |
这里有个细节我想提一下。你看球体的参数里有 widthSegments 和 heightSegments,这俩东西决定了球体的「光滑程度」。数值越小,球体越像多面体;数值越大,面数越多,性能开销也越大。我在做移动端项目时,一般把球体分段数控制在 16 左右,既看不出棱角,又不会卡顿。
核心要点:几何体只负责「形状」,不负责「颜色」。你 new 了一个 BoxGeometry,它只是一堆顶点坐标和索引,没有任何视觉属性。
4.2 基础材质:给物体穿上衣服
材质决定了物体「看起来像什么」。Three.js 里材质种类很多,但入门阶段你只需要掌握两个:MeshBasicMaterial 和 MeshStandardMaterial。
4.2.1 MeshBasicMaterial —— 最简单的材质
这个材质不受光照影响。你给它什么颜色,它就显示什么颜色,亮堂堂的,像卡通片里的物体。适合做 UI 元素、辅助线、或者不需要光照效果的场景。
const geometry = new THREE.BoxGeometry(1, 1, 1);
const material = new THREE.MeshBasicMaterial({
color: 0xff6600, // 橙色
wireframe: false // 是否显示线框
});
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);
嗯,这里要注意:MeshBasicMaterial 不需要光源。你场景里一盏灯都不放,它照样亮。但缺点也很明显——没有立体感,看起来「平」。我刚开始做项目时,图省事全用 BasicMaterial,结果甲方说「你这模型像纸糊的」……
4.2.2 MeshStandardMaterial —— 标准材质
这才是真正干活用的材质。它基于物理渲染(PBR),能模拟真实世界的光线反射。你需要配合光源使用,效果才出得来。
const material = new THREE.MeshStandardMaterial({
color: 0x44aa88,
roughness: 0.4, // 粗糙度,0-1
metalness: 0.7, // 金属感,0-1
emissive: 0x000000 // 自发光颜色
});
这里有两个参数我特别想强调:roughness 和 metalness。粗糙度越低,表面越光滑,反光越强。金属感越高,物体越像金属。我曾经做过一个珠宝展示项目,调这两个参数调了整整两天——钻石的 roughness 要接近 0,metalness 要接近 1,但也不能完全拉满,否则会失真。
小技巧:如果你想让物体看起来像塑料,设 roughness: 0.5, metalness: 0。像金属,设 roughness: 0.2, metalness: 0.8。像木头,设 roughness: 0.9, metalness: 0。
4.3 颜色与纹理:从单色到丰富多彩
光用纯色太单调了。真实世界里的物体,哪个不是有花纹、有图案、有凹凸的?这就轮到纹理上场了。
4.3.1 颜色表示法
Three.js 里颜色用 THREE.Color 对象表示。你可以用三种方式指定:
- 十六进制:
0xff0000(红色),最常用 - 字符串:
'#ff0000'或'red' - RGB 分量:
new THREE.Color(1, 0, 0)(范围 0-1)
我个人习惯用十六进制,因为从设计稿里复制颜色值最方便。但要注意,JavaScript 里十六进制前面是 0x,不是 #,写错了不会报错,但颜色会不对——我因为这个 bug 排查了半小时。
4.3.2 纹理贴图
纹理就是把一张图片「贴」到几何体表面。你需要用 TextureLoader 加载图片,然后赋值给材质的 map 属性。
const loader = new THREE.TextureLoader();
const texture = loader.load('path/to/texture.jpg');
const material = new THREE.MeshStandardMaterial({
map: texture,
roughness: 0.5,
metalness: 0.1
});
这里有个坑:纹理加载是异步的。如果你在 loader.load() 之后立即渲染,纹理可能还没加载完,物体会显示为黑色。解决办法是用 load 方法的回调函数,或者等所有纹理加载完再开始渲染。
注意:纹理图片的尺寸最好是 2 的幂次方,比如 512x512、1024x1024、2048x2048。如果不是,Three.js 会自动缩放,但可能会产生性能问题或显示异常。我曾经用了一张 800x600 的图片,结果边缘出现了奇怪的条纹,折腾了好久才发现是纹理尺寸的问题。
4.3.3 纹理的重复与偏移
有时候你想让纹理铺满整个表面,或者只显示一部分。这时候可以用纹理的 repeat 和 offset 属性。
texture.wrapS = THREE.RepeatWrapping;
texture.wrapT = THREE.RepeatWrapping;
texture.repeat.set(2, 2); // 水平和垂直方向各重复2次
texture.offset.set(0.5, 0.5); // 偏移半个纹理
这个技巧在做地面、墙面时特别有用。一张小砖块纹理,重复几次就能铺满整个地板,省资源又好看。
4.4 实战组合:做一个带纹理的球体
说了这么多,咱们来写一个完整的例子。把前面学的几何体、材质、纹理串起来。
// 1. 创建场景、相机、渲染器(略,前面讲过)
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// 2. 加载纹理
const loader = new THREE.TextureLoader();
const texture = loader.load('earth.jpg'); // 假设有一张地球贴图
// 3. 创建几何体 + 材质
const geometry = new THREE.SphereGeometry(2, 32, 32);
const material = new THREE.MeshStandardMaterial({
map: texture,
roughness: 0.3,
metalness: 0.1
});
const earth = new THREE.Mesh(geometry, material);
scene.add(earth);
// 4. 添加光源
const light = new THREE.DirectionalLight(0xffffff, 1);
light.position.set(5, 5, 5);
scene.add(light);
// 5. 渲染循环
function animate() {
requestAnimationFrame(animate);
earth.rotation.y += 0.01;
renderer.render(scene, camera);
}
animate();
你看,代码其实不多。但这里每一步都有讲究:球体的分段数设了 32,既保证光滑又不太耗性能;光源用了方向光,模拟太阳;纹理用了地球贴图,旋转起来就像一颗小星球在自转。
总结一下:几何体是骨架,材质是皮肤,纹理是皮肤上的花纹。三者组合,就能创造出千变万化的 3D 物体。下一章我们会深入材质的高级属性,比如法线贴图、环境贴图,让物体更逼真。
好,这一章就到这儿。记住我开头说的——多动手试。把 BoxGeometry 换成 CylinderGeometry,把 MeshStandardMaterial 换成 MeshBasicMaterial,看看效果有什么不同。实践出真知,这话在 3D 开发里尤其适用。