Chapter 03

材质与纹理

从简单颜色到 PBR 物理渲染——掌握 Five Mesh Materials、TextureLoader、法线贴图和环境反射,让模型栩栩如生

1. 五种核心材质对比

Three.js 提供多种 Mesh 材质,性能和效果各有侧重。理解它们的差异,才能在正确场合选对材质:

材质需要光照性能特点
MeshBasicMaterial最高纯色/纹理,不受光照影响,用于 UI 元素、线框、背景
MeshLambertMaterial漫反射模型,无高光,适合低性能场景的哑光物体
MeshPhongMaterialPhong 光照模型,有高光(shininess),适合塑料/光滑表面
MeshStandardMaterial中低PBR 标准材质,roughness + metalness,现代 3D 首选
MeshPhysicalMaterial最低PBR 扩展,增加 clearcoat(清漆)、transmission(透明)、iridescence(虹彩)
// MeshBasicMaterial — 不受光照影响
const basic = new THREE.MeshBasicMaterial({
  color: 0x049EF4,
  transparent: true,
  opacity: 0.8,
  side: THREE.DoubleSide  // 双面渲染
});

// MeshStandardMaterial — PBR 首选
const standard = new THREE.MeshStandardMaterial({
  color: 0xffffff,
  roughness: 0.3,  // 0=镜面, 1=完全粗糙
  metalness: 0.8,  // 0=非金属, 1=金属
  envMapIntensity: 1.0
});

// MeshPhysicalMaterial — 高级效果
const physical = new THREE.MeshPhysicalMaterial({
  color: 0x88ccff,
  roughness: 0.05,
  metalness: 0.0,
  transmission: 0.95,   // 透明度(玻璃效果)
  thickness: 0.5,        // 材质厚度(影响折射)
  ior: 1.5,              // 折射率(玻璃≈1.5)
  clearcoat: 1.0,        // 清漆层强度(汽车漆)
  clearcoatRoughness: 0.1
});

2. TextureLoader 加载图片纹理

纹理(Texture)是贴在几何体表面的图片。Three.js 的 TextureLoader 负责异步加载图片并转为 GPU 纹理:

const loader = new THREE.TextureLoader();

// 方式一:回调函数
const texture = loader.load(
  '/textures/brick.jpg',
  (tex) => { console.log('纹理加载完成', tex); },
  undefined,
  (err) => { console.error('加载失败', err); }
);

// 方式二:Promise 封装(推荐)
const loadTexture = (url) =>
  new Promise((resolve, reject) => {
    loader.load(url, resolve, undefined, reject);
  });

const [colorMap, normalMap, roughnessMap] = await Promise.all([
  loadTexture('/textures/color.jpg'),
  loadTexture('/textures/normal.jpg'),
  loadTexture('/textures/roughness.jpg')
]);

const material = new THREE.MeshStandardMaterial({
  map: colorMap,          // 颜色纹理
  normalMap: normalMap,   // 法线贴图
  roughnessMap: roughnessMap  // 粗糙度贴图
});

3. UV 映射

UV 坐标定义了 3D 几何体表面的每个点对应纹理图片上的哪个位置。U 轴对应横向(0~1),V 轴对应纵向(0~1)。Three.js 的内置几何体都自带 UV 坐标。

// 纹理重复(平铺地板)
const floorTex = await loadTexture('/textures/floor.jpg');
floorTex.wrapS = THREE.RepeatWrapping;  // U 方向重复
floorTex.wrapT = THREE.RepeatWrapping;  // V 方向重复
floorTex.repeat.set(4, 4);             // 4x4 平铺

// 纹理旋转
floorTex.rotation = Math.PI / 4;     // 旋转 45°
floorTex.center.set(0.5, 0.5);        // 旋转中心(UV 中点)

// 纹理偏移
floorTex.offset.set(0.1, 0);           // 平移纹理

// wrapMode 选项:
// THREE.ClampToEdgeWrapping  — 边缘像素拉伸(默认)
// THREE.RepeatWrapping       — 重复平铺
// THREE.MirroredRepeatWrapping — 镜像重复

4. 法线贴图(Normal Map)

法线贴图是一种低成本制造高细节表面凹凸感的技术。它不改变几何体的实际形状,而是通过改变表面法线方向来欺骗光照计算,使平面看起来有凹凸纹理。

const stoneMat = new THREE.MeshStandardMaterial({
  map:          loader.load('/stone/color.jpg'),
  normalMap:    loader.load('/stone/normal.jpg'),
  roughnessMap: loader.load('/stone/roughness.jpg'),
  aoMap:        loader.load('/stone/ao.jpg'),  // 环境光遮蔽贴图
  normalScale:  new THREE.Vector2(2, 2)  // 增强法线效果
});

5. PBR 材质与环境贴图

PBR(Physically Based Rendering)是现代实时渲染的标准——材质参数对应真实世界的物理属性,在不同光照环境下都能产生可信的效果。

PBR 材质需要环境贴图(envMap)才能产生真实的反射效果。通常使用 HDR(高动态范围)全景图作为环境光源:

import { RGBELoader } from 'three/addons/loaders/RGBELoader.js';

const rgbeLoader = new RGBELoader();
const envMap = await new Promise((resolve) =>
  rgbeLoader.load('/env/studio.hdr', resolve)
);

// 设置为 EquirectangularReflectionMapping
envMap.mapping = THREE.EquirectangularReflectionMapping;

// 同时作为场景背景和所有物体的反射源
scene.background = envMap;
scene.environment = envMap;  // 所有 PBR 材质自动使用

// 金属球(高反射)
const metalBall = new THREE.Mesh(
  new THREE.SphereGeometry(1, 64, 32),
  new THREE.MeshStandardMaterial({
    color: 0xcccccc,
    metalness: 1.0,
    roughness: 0.05  // 高光泽金属
  })
);

// 磨砂金属
const brushedMetal = new THREE.MeshStandardMaterial({
  color: 0x8899aa,
  metalness: 0.9,
  roughness: 0.4
});

// 橡胶/塑料
const rubber = new THREE.MeshStandardMaterial({
  color: 0x222222,
  metalness: 0.0,
  roughness: 0.9
});
ℹ️

免费 HDR 环境贴图资源
Poly Haven(polyhaven.com)— 免版权 HDR 环境贴图,质量极高
ambientCG(ambientcg.com)— 免版权 PBR 材质纹理包
Three.js Editor 内置示例环境贴图

6. 纹理压缩与性能

// 纹理过滤——影响缩放时的质量
texture.magFilter = THREE.LinearFilter;          // 放大过滤(默认)
texture.minFilter = THREE.LinearMipmapLinearFilter; // 缩小过滤(MipMap)
texture.generateMipmaps = true;  // 自动生成 MipMap(默认开启)

// 各向异性过滤——斜视时纹理更清晰
const maxAnisotropy = renderer.capabilities.getMaxAnisotropy();
texture.anisotropy = maxAnisotropy; // 最大值(通常 8 或 16)

// 纹理格式建议:
// JPG  —— 颜色/漫反射贴图(有损压缩,体积小)
// PNG  —— 需要透明通道时
// HDR/EXR —— 环境贴图(高动态范围)
// KTX2/Basis —— GPU 压缩纹理(最佳性能)

// 释放不再使用的纹理(防止内存泄漏)
texture.dispose();
material.dispose();
geometry.dispose();

本章小结:材质决定物体的视觉效果。开发中优先使用 MeshStandardMaterial(PBR);需要玻璃/透明/清漆效果时升级为 MeshPhysicalMaterial;UI 元素用 MeshBasicMaterial。配合 HDR 环境贴图,即使简单场景也能呈现专业品质。