Three.js 材质教程 - 第三部分:Materials详解

Three.js 材质教程 - 第三部分:Materials详解

本教程深入讲解Three.js材质(Materials)的核心概念、API使用和高级技巧。通过丰富的示例和实践,帮助你掌握3D材质的创建、配置和优化。


📚 目录


🎯 学习目标

完成本教程后,你将能够:

  • ✅ 理解Three.js材质的核心概念和架构
  • ✅ 熟练使用各种基础材质类型
  • ✅ 掌握PBR(物理渲染)材质的配置
  • ✅ 理解高级材质的特性(清漆、透射、彩虹等)
  • ✅ 使用各种纹理贴图增强材质效果
  • ✅ 创建自定义着色器材质
  • ✅ 掌握材质的性能优化技巧

📋 前置知识

在开始学习之前,建议你具备以下基础知识:

必备知识

  • Three.js基础: 场景、相机、渲染器的基本概念
  • 几何体基础: 理解顶点、面、法线等概念
  • JavaScript (ES6+): 对象、类、模块等现代语法

推荐知识

  • 计算机图形学基础: 光照模型、着色器
  • 物理渲染理论: PBR、BRDF、能量守恒

第一章:材质基础概念

1.1 什么是材质(Material)?

材质定义了3D物体的外观特性,决定了物体如何与光线交互。

材质的作用
复制代码
几何体(Geometry)     +     材质(Material)     =     网格(Mesh)
    ↓                              ↓                          ↓
  形状定义                      外观定义                  可渲染的3D对象
  - 顶点                      - 颜色                   - 位置
  - 面                        - 纹理                   - 旋转
  - 法线                      - 光照属性                 - 缩放
  - UV坐标                    - 透明度
类比理解
  • 几何体 就像建筑的结构框架
  • 材质 就像建筑的装修材料(油漆、壁纸、玻璃等)
  • 网格 就像装修好的建筑

1.2 Three.js材质类型

Three.js提供了多种材质类型,每种都有特定的用途和性能特点。

材质类型 光照计算 性能 适用场景
MeshBasicMaterial 最高 线框、调试、UI元素
MeshLambertMaterial 漫反射 哑光表面、性能优先
MeshPhongMaterial 漫反射+高光 塑料、陶瓷等有光泽物体
MeshStandardMaterial PBR 真实感渲染(推荐)
MeshPhysicalMaterial 高级PBR 玻璃、清漆、织物等特殊材质
MeshToonMaterial 卡通着色 卡通风格渲染
MeshNormalMaterial 法线可视化 调试、特殊效果
MeshDepthMaterial 深度可视化 深度效果、阴影
ShaderMaterial 自定义 可变 自定义效果
RawShaderMaterial 完全自定义 可变 高级自定义效果

1.3 材质的通用属性

所有材质都共享以下基础属性:

可见性属性
javascript 复制代码
material.visible = true;           // 是否可见
material.transparent = false;       // 是否透明
material.opacity = 1.0;            // 透明度(0-1)
material.alphaTest = 0;             // 透明度测试阈值
渲染属性
javascript 复制代码
material.side = THREE.FrontSide;   // 渲染面:FrontSide, BackSide, DoubleSide
material.depthTest = true;          // 深度测试
material.depthWrite = true;         // 深度写入
material.colorWrite = true;         // 颜色写入
混合模式
javascript 复制代码
material.blending = THREE.NormalBlending;
// NormalBlending - 正常混合
// AdditiveBlending - 加法混合
// SubtractiveBlending - 减法混合
// MultiplyBlending - 乘法混合
// CustomBlending - 自定义混合
多边形偏移
javascript 复制代码
material.polygonOffset = false;          // 启用多边形偏移
material.polygonOffsetFactor = 0;         // 偏移因子
material.polygonOffsetUnits = 0;           // 偏移单位

用途:解决Z-fighting(深度冲突)问题

复制代码
Z-fighting问题:
    ┌─────┐
    │▓▓▓▓▓│  ← 两个面重叠,产生闪烁
    └─────┘

解决方案(多边形偏移):
    ┌─────┐
    │     │
    └─────┘
    ┌─────┐  ← 稍微偏移,避免重叠
    │     │
    └─────┘

第二章:基础材质类型

2.1 MeshBasicMaterial

最简单的材质,不受光照影响。

特点
  • ✅ 性能最高
  • ✅ 始终可见
  • ❌ 无光照效果
  • ❌ 无真实感
使用示例
javascript 复制代码
const material = new THREE.MeshBasicMaterial({
    color: 0xff0000,              // 颜色(十六进制)
    transparent: true,              // 透明
    opacity: 0.5,                 // 透明度
    side: THREE.DoubleSide,         // 双面渲染
    wireframe: false,               // 线框模式
    map: texture,                  // 颜色贴图
    alphaMap: alphaTexture,        // 透明度贴图
    envMap: envTexture,            // 环境贴图
    reflectivity: 1,               // 反射强度
    fog: true                      // 受雾影响
});

const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
使用场景
场景 说明
线框显示 wireframe: true
UI元素 无需光照的界面元素
调试 快速查看几何体结构
特殊效果 发光、全息等效果

2.2 MeshLambertMaterial

漫反射材质,性能优于Phong。

特点
  • ✅ 性能较高
  • ✅ 有漫反射光照
  • ❌ 无高光
  • ❌ 不适合金属
使用示例
javascript 复制代码
const material = new THREE.MeshLambertMaterial({
    color: 0x00ff00,              // 漫反射颜色
    emissive: 0x111111,           // 自发光颜色
    emissiveIntensity: 1,           // 自发光强度
    map: texture,                  // 颜色贴图
    emissiveMap: emissiveTexture,  // 自发光贴图
    envMap: envTexture,            // 环境贴图
    reflectivity: 0.5,             // 反射强度
    side: THREE.DoubleSide
});
光照模型

Lambert使用简化的漫反射模型:

复制代码
Lambert光照 = 环境光 + 漫反射光

漫反射 = 光源强度 × 材质颜色 × max(0, 法线·光照方向)

2.3 MeshPhongMaterial

Phong材质,支持高光反射。

特点
  • ✅ 有高光效果
  • ✅ 适合塑料、陶瓷
  • ❌ 性能低于Lambert
  • ❌ 不符合物理规律
使用示例
javascript 复制代码
const material = new THREE.MeshPhongMaterial({
    color: 0x0000ff,              // 漫反射颜色
    specular: 0xffffff,            // 高光颜色
    shininess: 100,               // 高光强度(0-1000)
    emissive: 0x000000,           // 自发光颜色
    flatShading: false,            // 平面着色
    map: texture,                 // 颜色贴图
    specularMap: specTexture,      // 高光贴图
    normalMap: normalTexture,       // 法线贴图
    normalScale: new THREE.Vector2(1, 1),  // 法线强度
    bumpMap: bumpTexture,          // 凹凸贴图
    bumpScale: 1,                 // 凹凸强度
    displacementMap: dispTexture,  // 置换贴图
    displacementScale: 1           // 置换强度
});
光照模型

Phong使用Blinn-Phong光照模型:

复制代码
Phong光照 = 环境光 + 漫反射 + 高光

漫反射 = 光源强度 × 材质颜色 × max(0, 法线·光照方向)
高光 = 光源强度 × 高光颜色 × (法线·半角向量)^shininess
高光效果对比
复制代码
shininess = 10(低光)     shininess = 100(中光)     shininess = 1000(高光)
       ●                           ●                           ●
      ╱ ╲                        ╱ ╲                        ╱ ╲
     ╱   ╲                      ╱   ╲                      ╱   ╲
    ●─────●                    ●─────●                    ●─────●
   (大面积高光)              (中等高光)                (小面积高光)

2.4 MeshNormalMaterial

法线可视化材质,用于调试。

特点
  • ✅ 可视化法线方向
  • ✅ 无需光照
  • ❌ 不适合最终渲染
使用示例
javascript 复制代码
const material = new THREE.MeshNormalMaterial({
    flatShading: false,            // 平面着色
    wireframe: false               // 线框模式
});
法线颜色映射
复制代码
法线方向 → 颜色
(1, 0, 0) → 红色   +X
(-1, 0, 0) → 青色   -X
(0, 1, 0) → 绿色   +Y
(0, -1, 0) → 洋红色 -Y
(0, 0, 1) → 蓝色   +Z
(0, 0, -1) → 黄色  -Z
使用场景
  • 调试法线方向
  • 检查模型问题
  • 创建特殊视觉效果

2.5 MeshDepthMaterial

深度可视化材质。

特点
  • ✅ 可视化深度值
  • ✅ 用于深度效果
  • ❌ 不适合最终渲染
使用示例
javascript 复制代码
const material = new THREE.MeshDepthMaterial({
    depthPacking: THREE.RGBADepthPacking,  // 深度打包方式
});
使用场景
  • 深度效果(DOF)
  • 阴影贴图
  • 深度缓冲可视化

第三章:PBR材质

3.1 PBR(物理渲染)概述

PBR(Physically Based Rendering)基于物理规律的渲染方法。

PBR核心原则
  1. 能量守恒:反射光不能超过入射光
  2. 微表面理论:表面由无数微小镜面组成
  3. 菲涅尔效应:反射率随观察角度变化
PBR vs 传统渲染
复制代码
传统渲染(Phong):
- 不符合物理规律
- 参数不直观
- 需要大量调整

PBR渲染:
- 基于物理规律
- 参数直观(粗糙度、金属度)
- 一致性好

3.2 MeshStandardMaterial

标准PBR材质,推荐用于大多数场景。

核心属性
javascript 复制代码
const material = new THREE.MeshStandardMaterial({
    // 基础属性
    color: 0xffffff,              // 基础颜色
    roughness: 0.5,               // 粗糙度(0-1)
    metalness: 0.0,               // 金属度(0-1)

    // 纹理贴图
    map: colorTexture,             // 基础颜色贴图
    roughnessMap: roughTexture,    // 粗糙度贴图
    metalnessMap: metalTexture,    // 金属度贴图
    normalMap: normalTexture,      // 法线贴图
    normalScale: new THREE.Vector2(1, 1),  // 法线强度
    aoMap: aoTexture,             // 环境光遮蔽贴图
    aoMapIntensity: 1,            // AO强度
    displacementMap: dispTexture,  // 置换贴图
    displacementScale: 0.1,        // 置换强度
    displacementBias: 0,           // 置换偏移

    // 自发光
    emissive: 0x000000,           // 自发光颜色
    emissiveIntensity: 1,          // 自发光强度
    emissiveMap: emissiveTexture,  // 自发光贴图

    // 环境
    envMap: envTexture,           // 环境贴图
    envMapIntensity: 1,           // 环境贴图强度

    // 其他
    flatShading: false,            // 平面着色
    wireframe: false,              // 线框模式
    fog: true                     // 受雾影响
});
粗糙度(Roughness)

粗糙度控制表面的光滑程度:

复制代码
roughness = 0(镜面)      roughness = 0.5(中等)      roughness = 1(漫反射)
       ●                          ●                          ●
      ╱ ╲                       ╱ ╲                       ╱ ╲
     ╱   ╲                     ╱   ╲                     ╱   ╲
    ●─────●                   ●─────●                   ●─────●
   (清晰反射)                (模糊反射)                (无反射)
金属度(Metalness)

金属度控制材质的金属特性:

复制代码
metalness = 0(非金属)      metalness = 0.5(混合)      metalness = 1(金属)
  塑料、木材                 半金属                   金属、合金
  - 漫反射强                 - 混合特性                 - 高反射
  - 反射弱                   - 中等反射                 - 无漫反射
材质组合示例
javascript 复制代码
// 金属材质
const metalMaterial = new THREE.MeshStandardMaterial({
    color: 0xffffff,
    roughness: 0.2,
    metalness: 1.0
});

// 塑料材质
const plasticMaterial = new THREE.MeshStandardMaterial({
    color: 0xff0000,
    roughness: 0.5,
    metalness: 0.0
});

// 粗糙金属
const roughMetalMaterial = new THREE.MeshStandardMaterial({
    color: 0x00ff00,
    roughness: 0.8,
    metalness: 1.0
});

// 光滑塑料
const smoothPlasticMaterial = new THREE.MeshStandardMaterial({
    color: 0x0000ff,
    roughness: 0.2,
    metalness: 0.0
});

3.3 MeshPhysicalMaterial

高级PBR材质,扩展了StandardMaterial。

新增属性
javascript 复制代码
const material = new THREE.MeshPhysicalMaterial({
    // 继承MeshStandardMaterial的所有属性

    // 清漆涂层(汽车漆、清漆)
    clearcoat: 1.0,                    // 清漆强度(0-1)
    clearcoatRoughness: 0.1,            // 清漆粗糙度
    clearcoatMap: ccTexture,            // 清漆贴图
    clearcoatRoughnessMap: ccrTexture,  // 清漆粗糙度贴图
    clearcoatNormalMap: ccnTexture,      // 清漆法线贴图
    clearcoatNormalScale: new THREE.Vector2(1, 1),

    // 透射(玻璃、水)
    transmission: 1.0,                  // 透射率(0-1)
    transmissionMap: transTexture,       // 透射贴图
    thickness: 0.5,                    // 厚度
    thicknessMap: thickTexture,          // 厚度贴图
    attenuationDistance: 1,             // 吸收距离
    attenuationColor: new THREE.Color(0xffffff),  // 吸收颜色

    // 折射
    ior: 1.5,                         // 折射率(1-2.333)

    // 绒布(织物、天鹅绒)
    sheen: 1.0,                       // 绒布强度
    sheenRoughness: 0.5,               // 绒布粗糙度
    sheenColor: new THREE.Color(0xffffff),  // 绒布颜色
    sheenColorMap: sheenTexture,       // 绒布颜色贴图
    sheenRoughnessMap: sheenRoughTexture,  // 绒布粗糙度贴图

    // 彩虹(肥皂泡、油膜)
    iridescence: 1.0,                 // 彩虹强度
    iridescenceIOR: 1.3,              // 彩虹折射率
    iridescenceThicknessRange: [100, 400],  // 彩虹厚度范围
    iridescenceMap: iridTexture,       // 彩虹贴图
    iridescenceThicknessMap: iridThickTexture,  // 彩虹厚度贴图

    // 各向异性(拉丝金属)
    anisotropy: 1.0,                  // 各向异性强度
    anisotropyRotation: 0,             // 各向异性旋转
    anisotropyMap: anisoTexture,       // 各向异性贴图

    // 高光
    specularIntensity: 1,              // 高光强度
    specularColor: new THREE.Color(0xffffff),  // 高光颜色
    specularIntensityMap: specIntTexture,  // 高光强度贴图
    specularColorMap: specColorTexture   // 高光颜色贴图
});
清漆涂层(Clearcoat)

模拟表面涂层的额外反射层:

复制代码
清漆效果:
    ┌─────────────┐
    │  清漆层     │  ← 额外反射层
    ├─────────────┤
    │  基础材质   │
    └─────────────┘

应用:
- 汽车漆
- 清漆表面
- 抛光木材
透射(Transmission)

模拟透明材质的折射效果:

javascript 复制代码
// 玻璃材质
const glassMaterial = new THREE.MeshPhysicalMaterial({
    color: 0xffffff,
    roughness: 0,
    metalness: 0,
    transmission: 1.0,    // 完全透明
    thickness: 0.5,        // 玻璃厚度
    ior: 1.5              // 玻璃折射率
});
折射率(IOR)参考
材质 IOR值
空气 1.0
1.33
玻璃 1.5
钻石 2.42
塑料 1.4-1.6
绒布效果(Sheen)

模拟织物、天鹅绒等材质:

javascript 复制代码
// 绒布材质
const velvetMaterial = new THREE.MeshPhysicalMaterial({
    color: 0xff0066,
    roughness: 0.8,
    metalness: 0,
    sheen: 1.0,
    sheenRoughness: 0.5,
    sheenColor: new THREE.Color(0xffffff)
});
彩虹效果(Iridescence)

模拟肥皂泡、油膜等彩虹效果:

javascript 复制代码
// 彩虹材质
const iridescenceMaterial = new THREE.MeshPhysicalMaterial({
    color: 0x0000ff,
    roughness: 0.2,
    metalness: 0.8,
    iridescence: 1.0,
    iridescenceIOR: 1.3,
    iridescenceThicknessRange: [100, 400]
});
各向异性(Anisotropy)

模拟拉丝金属等方向性反射:

javascript 复制代码
// 拉丝金属
const brushedMetalMaterial = new THREE.MeshPhysicalMaterial({
    color: 0xffff00,
    roughness: 0.3,
    metalness: 0.9,
    anisotropy: 1.0,
    anisotropyRotation: 0
});

第四章:高级材质

4.1 MeshToonMaterial

卡通着色材质,实现Cel-shading效果。

使用示例
javascript 复制代码
const material = new THREE.MeshToonMaterial({
    color: 0x00ff00,
    gradientMap: gradientTexture  // 可选:自定义渐变贴图
});
创建渐变贴图
javascript 复制代码
// 创建阶梯渐变贴图
const colors = new Uint8Array([0, 128, 255]);
const gradientMap = new THREE.DataTexture(colors, 3, 1, THREE.RedFormat);
gradientMap.minFilter = THREE.NearestFilter;
gradientMap.magFilter = THREE.NearestFilter;
gradientMap.needsUpdate = true;

const toonMaterial = new THREE.MeshToonMaterial({
    color: 0x00ff00,
    gradientMap: gradientMap
});
效果对比
复制代码
标准着色                  卡通着色
    ●                          ●
   ╱ ╲                       ╱ ╲
  ╱   ╲                     ╱───╲
 ●─────●                   ●─────●
(平滑过渡)                (阶梯过渡)

4.2 PointsMaterial

点云材质,用于渲染粒子系统。

使用示例
javascript 复制代码
const material = new THREE.PointsMaterial({
    color: 0xffffff,
    size: 0.1,                    // 点的大小
    sizeAttenuation: true,          // 大小随距离衰减
    map: pointTexture,             // 点纹理
    alphaMap: alphaTexture,         // 透明度贴图
    transparent: true,             // 透明
    alphaTest: 0.5,               // 透明度测试
    vertexColors: true,            // 使用顶点颜色
    fog: true                     // 受雾影响
});

const points = new THREE.Points(geometry, material);
scene.add(points);
点形状
javascript 复制代码
// 使用自定义纹理
const texture = new THREE.TextureLoader().load('particle.png');
const material = new THREE.PointsMaterial({
    size: 0.5,
    map: texture,
    transparent: true,
    alphaTest: 0.5,
    depthWrite: false
});

4.3 LineBasicMaterial & LineDashedMaterial

线段材质。

LineBasicMaterial
javascript 复制代码
const material = new THREE.LineBasicMaterial({
    color: 0xffffff,
    linewidth: 1,              // 线宽(注意:>1在某些系统上不工作)
    linecap: 'round',          // 线端点样式:'butt', 'round', 'square'
    linejoin: 'round'          // 线连接样式:'round', 'bevel', 'miter'
});
LineDashedMaterial
javascript 复制代码
const material = new THREE.LineDashedMaterial({
    color: 0xffffff,
    dashSize: 0.5,            // 虚线段长度
    gapSize: 0.25,            // 间隙长度
    scale: 1                   // 缩放因子
});

// 虚线必须计算距离
const line = new THREE.Line(geometry, material);
line.computeLineDistances();

第五章:纹理贴图

5.1 纹理类型

Three.js支持多种纹理贴图类型:

纹理类型 用途 颜色通道
map 基础颜色 RGB
roughnessMap 粗糙度 单通道
metalnessMap 金属度 单通道
normalMap 法线 RGB
bumpMap 凹凸 单通道
displacementMap 置换 单通道
aoMap 环境光遮蔽 单通道
emissiveMap 自发光 RGB
alphaMap 透明度 单通道

5.2 基础颜色贴图(map)

javascript 复制代码
const textureLoader = new THREE.TextureLoader();
const colorTexture = textureLoader.load('texture.jpg');

const material = new THREE.MeshStandardMaterial({
    map: colorTexture,
    roughness: 0.5,
    metalness: 0.0
});
纹理参数
javascript 复制代码
const texture = textureLoader.load('texture.jpg');

// 重复模式
texture.wrapS = THREE.RepeatWrapping;  // 水平重复
texture.wrapT = THREE.RepeatWrapping;  // 垂直重复

// 重复次数
texture.repeat.set(2, 2);  // 水平2次,垂直2次

// 过滤模式
texture.minFilter = THREE.LinearFilter;    // 缩小过滤
texture.magFilter = THREE.LinearFilter;    // 放大过滤

// 翻转
texture.flipY = false;  // 不翻转Y轴

// 偏移
texture.offset.set(0.5, 0.5);  // 偏移50%

// 旋转
texture.rotation = Math.PI / 4;  // 旋转45度
texture.center.set(0.5, 0.5);  // 旋转中心

5.3 法线贴图(normalMap)

法线贴图用于模拟表面细节。

javascript 复制代码
const normalTexture = textureLoader.load('normal.jpg');

const material = new THREE.MeshStandardMaterial({
    map: colorTexture,
    normalMap: normalTexture,
    normalScale: new THREE.Vector2(1, 1)  // 法线强度
});
法线贴图原理
复制代码
法线贴图颜色:
- 红色通道(R):X方向法线
- 绿色通道(G):Y方向法线
- 蓝色通道(B):Z方向法线

颜色映射:
RGB(128, 128, 255) → 法线(0, 0, 1)  // 默认法线
RGB(255, 128, 255) → 法线(1, 0, 1)   // 向右偏移
RGB(128, 255, 255) → 法线(0, 1, 1)   // 向上偏移

5.4 粗糙度贴图(roughnessMap)

javascript 复制代码
const roughnessTexture = textureLoader.load('roughness.jpg');

const material = new THREE.MeshStandardMaterial({
    map: colorTexture,
    roughnessMap: roughnessTexture,
    roughness: 0.5  // 基础粗糙度
});
粗糙度贴图颜色
复制代码
黑色(0)→ 光滑
白色(1)→ 粗糙

应用:
- 磨损效果
- 污渍效果
- 材质混合

5.5 金属度贴图(metalnessMap)

javascript 复制代码
const metalnessTexture = textureLoader.load('metalness.jpg');

const material = new THREE.MeshStandardMaterial({
    map: colorTexture,
    metalnessMap: metalnessTexture,
    metalness: 0.5  // 基础金属度
});
金属度贴图颜色
复制代码
黑色(0)→ 非金属
白色(1)→ 金属

应用:
- 锈蚀效果
- 材质混合
- 表面处理

5.6 环境光遮蔽贴图(aoMap)

AO贴图模拟环境光遮蔽效果。

javascript 复制代码
const aoTexture = textureLoader.load('ao.jpg');

const material = new THREE.MeshStandardMaterial({
    map: colorTexture,
    aoMap: aoTexture,
    aoMapIntensity: 1.0  // AO强度
});

// AO贴图需要第二UV通道
geometry.setAttribute('uv2', geometry.attributes.uv);
AO效果
复制代码
无AO                    有AO
    ┌─────┐                ┌─────┐
    │     │                │     │
    │     │                │╲   ╱│
    │     │                │ ╲ ╱ │
    └─────┘                └─────┘
  (均匀光照)              (角落变暗)

5.7 置换贴图(displacementMap)

置换贴图修改顶点位置,创建真实的几何细节。

javascript 复制代码
const displacementTexture = textureLoader.load('displacement.jpg');

const material = new THREE.MeshStandardMaterial({
    map: colorTexture,
    displacementMap: displacementTexture,
    displacementScale: 0.1,    // 置换强度
    displacementBias: 0         // 置换偏移
});
置换 vs 凹凸
复制代码
凹凸贴图(假效果):
    ┌─────┐
    │╲   ╱│
    │ ╲ ╱ │
    └─────┘
  (法线偏移,顶点不变)

置换贴图(真效果):
    ┌─────┐
    │╲   ╱│
    │ ╲ ╱ │
    └─────┘
  (顶点移动,真实几何)

第六章:着色器材质

6.1 ShaderMaterial概述

ShaderMaterial允许使用自定义GLSL着色器。

基本结构
javascript 复制代码
const material = new THREE.ShaderMaterial({
    uniforms: {
        time: { value: 0 },
        color: { value: new THREE.Color(0xff0000) },
        texture1: { value: texture }
    },
    vertexShader: `
        varying vec2 vUv;
        uniform float time;

        void main() {
            vUv = uv;
            vec3 pos = position;
            pos.z += sin(pos.x * 10.0 + time) * 0.1;
            gl_Position = projectionMatrix * modelViewMatrix * vec4(pos, 1.0);
        }
    `,
    fragmentShader: `
        varying vec2 vUv;
        uniform vec3 color;
        uniform sampler2D texture1;

        void main() {
            vec4 texColor = texture2D(texture1, vUv);
            gl_FragColor = vec4(color * texColor.rgb, 1.0);
        }
    `,
    transparent: true,
    side: THREE.DoubleSide
});

6.2 Uniforms

Uniforms是从JavaScript传递到着色器的变量。

javascript 复制代码
const material = new THREE.ShaderMaterial({
    uniforms: {
        time: { value: 0 },
        color: { value: new THREE.Color(0xff0000) },
        texture1: { value: texture },
        resolution: { value: new THREE.Vector2(window.innerWidth, window.innerHeight) }
    }
});

// 更新uniform
material.uniforms.time.value = clock.getElapsedTime();

6.3 内置Uniforms

Three.js自动提供以下uniforms:

glsl 复制代码
// 顶点着色器
uniform mat4 modelMatrix;         // 模型矩阵
uniform mat4 modelViewMatrix;     // 模型视图矩阵
uniform mat4 projectionMatrix;    // 投影矩阵
uniform mat4 viewMatrix;          // 视图矩阵
uniform mat3 normalMatrix;        // 法线矩阵
uniform vec3 cameraPosition;      // 相机位置

// 属性
attribute vec3 position;          // 顶点位置
attribute vec3 normal;            // 顶点法线
attribute vec2 uv;                // UV坐标

6.4 Varyings

Varyings在顶点和片段着色器之间传递数据。

glsl 复制代码
// 顶点着色器
varying vec2 vUv;
varying vec3 vNormal;

void main() {
    vUv = uv;
    vNormal = normal;
    gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}

// 片段着色器
varying vec2 vUv;
varying vec3 vNormal;

void main() {
    float intensity = dot(vNormal, vec3(0.0, 0.0, 1.0));
    gl_FragColor = vec4(vec3(intensity), 1.0);
}

6.5 RawShaderMaterial

RawShaderMaterial提供完全控制,无内置uniforms。

javascript 复制代码
const material = new THREE.RawShaderMaterial({
    uniforms: {
        projectionMatrix: { value: camera.projectionMatrix },
        modelViewMatrix: { value: new THREE.Matrix4() }
    },
    vertexShader: `
        precision highp float;
        attribute vec3 position;
        uniform mat4 projectionMatrix;
        uniform mat4 modelViewMatrix;

        void main() {
            gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
        }
    `,
    fragmentShader: `
        precision highp float;

        void main() {
            gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
        }
    `
});

6.6 常见着色器效果

波动效果
glsl 复制代码
// 顶点着色器
uniform float time;
varying vec2 vUv;
varying vec3 vNormal;

void main() {
    vUv = uv;
    vNormal = normal;

    vec3 pos = position;
    pos += normal * sin(pos.x * 10.0 + time) * 0.1;
    pos += normal * sin(pos.y * 10.0 + time * 1.5) * 0.1;

    gl_Position = projectionMatrix * modelViewMatrix * vec4(pos, 1.0);
}
渐变效果
glsl 复制代码
// 片段着色器
varying vec2 vUv;
varying vec3 vPosition;

void main() {
    vec3 color1 = vec3(1.0, 0.0, 0.0);
    vec3 color2 = vec3(0.0, 1.0, 0.0);

    float t = sin(vPosition.y * 2.0 + time) * 0.5 + 0.5;
    vec3 color = mix(color1, color2, t);

    gl_FragColor = vec4(color, 1.0);
}
噪声效果
glsl 复制代码
// 片段着色器
float random(vec2 st) {
    return fract(sin(dot(st.xy, vec2(12.9898, 78.233))) * 43758.5453123);
}

float noise(vec2 st) {
    vec2 i = floor(st);
    vec2 f = fract(st);

    float a = random(i);
    float b = random(i + vec2(1.0, 0.0));
    float c = random(i + vec2(0.0, 1.0));
    float d = random(i + vec2(1.0, 1.0));

    vec2 u = f * f * (3.0 - 2.0 * f);

    return mix(a, b, u.x) +
           (c - a) * u.y * (1.0 - u.x) +
           (d - b) * u.x * u.y;
}

void main() {
    float n = noise(vPosition.xy * 5.0 + time);
    vec3 color = vec3(n, n * 0.5, n * 0.2);

    gl_FragColor = vec4(color, 1.0);
}
菲涅尔效果
glsl 复制代码
// 顶点着色器
uniform vec3 viewVector;
varying float vIntensity;

void main() {
    vec3 vNormal = normalize(normalMatrix * normal);
    vec3 vNormel = normalize(normalMatrix * viewVector);
    vIntensity = pow(0.7 - dot(vNormal, vNormel), 2.0);

    gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}

// 片段着色器
varying float vIntensity;

void main() {
    vec3 color = vec3(0.0, 1.0, 1.0);
    color *= vIntensity;

    gl_FragColor = vec4(color, vIntensity * 0.8);
}

第七章:材质优化

7.1 性能优化技巧

1. 选择合适的材质
javascript 复制代码
// 性能优先
const basicMaterial = new THREE.MeshBasicMaterial({ color: 0xff0000 });

// 质量优先
const pbrMaterial = new THREE.MeshStandardMaterial({
    color: 0xff0000,
    roughness: 0.5,
    metalness: 0.0
});
2. 复用材质
javascript 复制代码
// 创建材质缓存
const materialCache = new Map();

function getMaterial(color) {
    const key = color.toString(16);
    if (!materialCache.has(key)) {
        materialCache.set(key, new THREE.MeshStandardMaterial({ color }));
    }
    return materialCache.get(key);
}

// 使用
const material1 = getMaterial(0xff0000);
const material2 = getMaterial(0xff0000);  // 复用
3. 避免透明材质
javascript 复制代码
// 使用alphaTest代替透明
const material = new THREE.MeshStandardMaterial({
    map: texture,
    alphaTest: 0.5,  // 丢弃alpha < 0.5的像素
    transparent: false
});
4. 优化纹理
javascript 复制代码
// 压缩纹理
const texture = textureLoader.load('texture.jpg');
texture.minFilter = THREE.LinearMipmapLinearFilter;
texture.generateMipmaps = true;

// 使用合适的分辨率
const texture = textureLoader.load('texture_512.jpg');  // 而不是4096
5. 减少Draw Calls
javascript 复制代码
// 使用InstancedMesh
const instancedMesh = new THREE.InstancedMesh(geometry, material, 1000);

// 合并静态几何体
const mergedGeometry = mergeGeometries([geo1, geo2, geo3]);

7.2 内存管理

释放材质
javascript 复制代码
function disposeMaterial(material) {
    // 释放纹理
    if (material.map) material.map.dispose();
    if (material.normalMap) material.normalMap.dispose();
    if (material.roughnessMap) material.roughnessMap.dispose();
    if (material.metalnessMap) material.metalnessMap.dispose();
    if (material.aoMap) material.aoMap.dispose();
    if (material.emissiveMap) material.emissiveMap.dispose();
    if (material.displacementMap) material.displacementMap.dispose();

    // 释放材质
    material.dispose();
}

// 使用
disposeMaterial(mesh.material);
释放网格
javascript 复制代码
function disposeMesh(mesh) {
    // 释放几何体
    if (mesh.geometry) {
        mesh.geometry.dispose();
    }

    // 释放材质
    if (mesh.material) {
        if (Array.isArray(mesh.material)) {
            mesh.material.forEach(m => disposeMaterial(m));
        } else {
            disposeMaterial(mesh.material);
        }
    }

    // 从场景中移除
    scene.remove(mesh);
}

// 使用
disposeMesh(mesh);

7.3 LOD(Level of Detail)

根据距离使用不同细节的材质。

javascript 复制代码
// 创建不同细节的材质
const lowDetailMaterial = new THREE.MeshStandardMaterial({
    color: 0xff0000,
    roughness: 0.8
});

const highDetailMaterial = new THREE.MeshPhysicalMaterial({
    color: 0xff0000,
    roughness: 0.8,
    clearcoat: 1.0,
    clearcoatRoughness: 0.1
});

// 创建LOD对象
const lod = new THREE.LOD();

// 添加不同细节的网格
lod.addLevel(new THREE.Mesh(geometry, highDetailMaterial), 0);
lod.addLevel(new THREE.Mesh(geometry, lowDetailMaterial), 10);

scene.add(lod);

常见问题与故障排除

问题1:材质显示为黑色

可能原因:

  1. 没有添加光源
  2. 材质颜色设置错误
  3. 环境贴图未设置

解决方案:

javascript 复制代码
// 添加光源
const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
scene.add(ambientLight);

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

// 设置环境贴图
scene.environment = envTexture;

// 检查材质颜色
material.color.set(0xff0000);

问题2:透明材质不透明

可能原因:

  • transparent未设置为true
  • 深度写入问题

解决方案:

javascript 复制代码
const material = new THREE.MeshStandardMaterial({
    color: 0xff0000,
    transparent: true,
    opacity: 0.5,
    depthWrite: false  // 透明物体禁用深度写入
});

问题3:纹理不显示

可能原因:

  • 纹理路径错误
  • UV坐标未设置
  • 纹理加载失败

解决方案:

javascript 复制代码
// 检查纹理路径
const texture = textureLoader.load('textures/texture.jpg');

// 检查UV坐标
if (!geometry.attributes.uv) {
    geometry.setAttribute('uv', new THREE.BufferAttribute(uvs, 2));
}

// 监听加载错误
textureLoader.load('texture.jpg', (texture) => {
    console.log('纹理加载成功');
}, undefined, (error) => {
    console.error('纹理加载失败', error);
});

问题4:PBR材质效果不真实

可能原因:

  • 环境贴图未设置
  • 光照不足
  • 参数设置不当

解决方案:

javascript 复制代码
// 设置环境贴图
const pmremGenerator = new THREE.PMREMGenerator(renderer);
const envTexture = pmremGenerator.fromScene(sceneEnv).texture;
scene.environment = envTexture;

// 添加足够的光源
const ambientLight = new THREE.AmbientLight(0x404040, 0.3);
scene.add(ambientLight);

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

// 调整材质参数
material.roughness = 0.5;
material.metalness = 0.0;

问题5:着色器材质不工作

可能原因:

  • GLSL语法错误
  • Uniforms未定义
  • Varyings不匹配

解决方案:

javascript 复制代码
// 检查GLSL语法
const vertexShader = `
    varying vec2 vUv;
    void main() {
        vUv = uv;
        gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
    }
`;

// 检查Uniforms
material.uniforms.time.value = clock.getElapsedTime();

// 检查Varyings
// 确保顶点和片段着色器中的varying名称一致

最佳实践

1. 选择合适的材质类型

场景 推荐材质 原因
性能优先 MeshBasicMaterial 最快
哑光表面 MeshLambertMaterial 简单漫反射
塑料/陶瓷 MeshPhongMaterial 有高光
真实渲染 MeshStandardMaterial PBR
玻璃/清漆 MeshPhysicalMaterial 高级特性
卡通风格 MeshToonMaterial Cel-shading
调试 MeshNormalMaterial 法线可视化
自定义效果 ShaderMaterial 完全控制

2. 合理设置材质参数

javascript 复制代码
// 金属材质
const metalMaterial = new THREE.MeshStandardMaterial({
    color: 0xffffff,
    roughness: 0.2,
    metalness: 1.0
});

// 塑料材质
const plasticMaterial = new THREE.MeshStandardMaterial({
    color: 0xff0000,
    roughness: 0.5,
    metalness: 0.0
});

// 玻璃材质
const glassMaterial = new THREE.MeshPhysicalMaterial({
    color: 0xffffff,
    roughness: 0,
    metalness: 0,
    transmission: 1.0,
    thickness: 0.5,
    ior: 1.5
});

3. 使用环境贴图

javascript 复制代码
// 加载环境贴图
const cubeLoader = new THREE.CubeTextureLoader();
const envMap = cubeLoader.load([
    'px.jpg', 'nx.jpg',
    'py.jpg', 'ny.jpg',
    'pz.jpg', 'nz.jpg'
]);

// 应用到材质
material.envMap = envMap;
material.envMapIntensity = 1;

// 或应用到场景
scene.environment = envMap;

4. 优化纹理使用

javascript 复制代码
// 压缩纹理
texture.minFilter = THREE.LinearMipmapLinearFilter;
texture.generateMipmaps = true;

// 使用合适的分辨率
const texture = textureLoader.load('texture_512.jpg');

// 复用纹理
const textureCache = new Map();
function getTexture(url) {
    if (!textureCache.has(url)) {
        textureCache.set(url, textureLoader.load(url));
    }
    return textureCache.get(url);
}

5. 及时释放资源

javascript 复制代码
// 销毁材质
function disposeMaterial(material) {
    if (material.map) material.map.dispose();
    if (material.normalMap) material.normalMap.dispose();
    if (material.roughnessMap) material.roughnessMap.dispose();
    if (material.metalnessMap) material.metalnessMap.dispose();
    material.dispose();
}

// 销毁网格
function disposeMesh(mesh) {
    if (mesh.geometry) mesh.geometry.dispose();
    if (mesh.material) disposeMaterial(mesh.material);
    scene.remove(mesh);
}

推荐资源

官方资源
学习资源

实践项目建议

  1. 初级项目

    • 创建一个材质展示场景
    • 制作一个玻璃效果
    • 实现一个卡通风格场景
  2. 中级项目

    • 创建一个程序化材质系统
    • 制作一个自定义着色器效果
    • 实现一个材质编辑器
  3. 高级项目

    • 创建一个PBR材质库
    • 制作一个高级着色器效果
    • 实现一个实时材质预览系统

总结

本教程涵盖了Three.js材质的核心知识,包括:

✅ 材质的基础概念和架构

✅ 各种基础材质类型的使用

✅ PBR(物理渲染)材质的配置

✅ 高级材质的特性(清漆、透射、彩虹等)

✅ 各种纹理贴图的应用

✅ 自定义着色器材质的创建

✅ 材质的性能优化技巧

通过学习本教程,你应该能够:

  • 选择合适的材质类型
  • 配置PBR材质参数
  • 使用各种纹理贴图
  • 创建自定义着色器
  • 优化材质性能

附录

A. 材质类型速查

材质类型 光照 性能 主要用途
MeshBasicMaterial 最高 线框、调试
MeshLambertMaterial 漫反射 哑光表面
MeshPhongMaterial 漫反射+高光 塑料、陶瓷
MeshStandardMaterial PBR 真实渲染
MeshPhysicalMaterial 高级PBR 玻璃、清漆
MeshToonMaterial 卡通 卡通风格
MeshNormalMaterial 法线 调试
MeshDepthMaterial 深度 深度效果
ShaderMaterial 自定义 可变 自定义效果
RawShaderMaterial 完全自定义 可变 高级自定义

B. 纹理类型速查

纹理类型 用途 颜色通道
map 基础颜色 RGB
roughnessMap 粗糙度 单通道
metalnessMap 金属度 单通道
normalMap 法线 RGB
bumpMap 凹凸 单通道
displacementMap 置换 单通道
aoMap 环境光遮蔽 单通道
emissiveMap 自发光 RGB
alphaMap 透明度 单通道

C. PBR参数速查

参数 范围 说明
roughness 0-1 粗糙度(0=镜面,1=漫反射)
metalness 0-1 金属度(0=非金属,1=金属)
clearcoat 0-1 清漆强度
transmission 0-1 透射率(0=不透明,1=完全透明)
ior 1-2.333 折射率
sheen 0-1 绒布强度
iridescence 0-1 彩虹强度
anisotropy 0-1 各向异性强度

D. 性能优化清单

  • 选择合适的材质类型
  • 复用材质实例
  • 避免透明材质(使用alphaTest)
  • 优化纹理分辨率
  • 使用纹理压缩
  • 减少Draw Calls
  • 使用InstancedMesh
  • 及时释放资源
  • 使用LOD系统
  • 避免频繁更新材质
相关推荐
gis分享者8 小时前
学习threejs,打造原生3D高斯溅落实时渲染器
spark·threejs·ply·高斯·splat·溅落·实时渲染器
loriloy1 天前
Three.js 几何体教程 - 第二部分:Geometry详解
javascript·threejs
军军君019 天前
Three.js基础功能学习十一:动画与音频
前端·javascript·3d·js·threejs·三维
gis分享者9 天前
学习threejs,实现发光信息流效果
threejs·发光·shadermaterial·spheregeometry·信息流·linedashed·vector3
军军君0113 天前
Three.js基础功能学习十二:常量与核心
前端·javascript·学习·3d·threejs·three·三维
军军君0119 天前
Three.js基础功能学习七:加载器与管理器
开发语言·前端·javascript·学习·3d·threejs·三维
患得患失9491 个月前
【ThreeJS】camera-controls---高性能动画相机控制库
threejs·camera-controls
接着奏乐接着舞。1 个月前
3D地球可视化教程 - 第6篇:蜂巢网格与自定义几何体
前端·vue.js·3d·threejs
军军君011 个月前
Three.js基础功能学习一:环境资源及基础知识
开发语言·javascript·学习·3d·前端框架·threejs·三维