Three.js 材质(Material)详解 —— 区别、原理、场景与示例

一、概念

Three.js 中,材质(Material)决定了几何体表面的外观------包括颜色、光照反应、透明度、反射率、甚至物理属性。

简单来说,几何体是形状,材质是皮肤

不同的材质会模拟不同的现实渲染效果,从低成本的"纯色贴图",到高质量的 PBR(基于物理的渲染)


二、原理

材质的核心是 着色器(Shader) ,即 GPU 上运行的小程序。

Three.js 提供了两类材质:

  1. 内置材质 :如 MeshPhongMaterialMeshStandardMaterial,直接调用,简单易用。
  2. 自定义材质 :如 ShaderMaterialRawShaderMaterial,需要自己写 GLSL。

不同材质的底层实现基于不同的 光照模型

  • Lambert(漫反射) → 粗糙表面,无镜面高光。
  • Phong(冯氏模型) → 漆面、陶瓷,带镜面反射。
  • Standard / Physical(PBR 模型) → 金属、玻璃、水,逼真模拟。

三、对比

材质 是否受光照影响 主要参数 适用场景 性能开销
SpriteMaterial map, color 2D 图标、UI、特效
ShadowMaterial 特殊 opacity 阴影接收
ShaderMaterial 自定义 uniforms, vertex/fragmentShader 特效、波纹、火焰 ★★★★
RawShaderMaterial 自定义 完全自写 GLSL 高级渲染 ★★★★★
PointsMaterial size, map 粒子系统、星空、点云
MeshToonMaterial gradientMap 卡通渲染、漫画风 ★★
MeshStandardMaterial metalness, roughness, map 木材、金属、石头 ★★★
MeshPhysicalMaterial transmission, clearcoat 玻璃、水、宝石 ★★★★
MeshPhongMaterial shininess, specular 漆面、塑料 ★★
MeshNormalMaterial 无需参数 法线调试
MeshLambertMaterial color 布料、纸张
MeshMatcapMaterial matcap 纹理 ZBrush 风格快速预览
MeshDistanceMaterial 特殊 near, far 阴影、深度编码

四、实践(示例代码)

ini 复制代码
import * as THREE from 'three';

// 初始化场景
const scene = new THREE.Scene();

// 相机
const camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(0, 2, 10);

// 渲染器
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;
document.body.appendChild(renderer.domElement);

// 灯光
const light = new THREE.DirectionalLight(0xffffff, 1);
light.position.set(5, 5, 5);
light.castShadow = true;
scene.add(light);

// 地面接收阴影
const ground = new THREE.Mesh(
  new THREE.PlaneGeometry(20, 20),
  new THREE.ShadowMaterial({ opacity: 0.3 })
);
ground.rotation.x = -Math.PI / 2;
ground.receiveShadow = true;
scene.add(ground);

// 常见几何体
const geometry = new THREE.SphereGeometry(0.7, 32, 32);

// 各种材质
const materials = [
  new THREE.MeshBasicMaterial({ color: 0xff0000 }),
  new THREE.MeshLambertMaterial({ color: 0x00ff00 }),
  new THREE.MeshPhongMaterial({ color: 0x0000ff, shininess: 80 }),
  new THREE.MeshStandardMaterial({ color: 0xffff00, metalness: 0.8, roughness: 0.2 }),
  new THREE.MeshPhysicalMaterial({ color: 0x00ffff, transmission: 0.9, thickness: 0.5 }),
  new THREE.MeshToonMaterial({ color: 0xff00ff }),
  new THREE.MeshNormalMaterial(),
  new THREE.MeshMatcapMaterial({ matcap: new THREE.TextureLoader().load('matcap.png') })
];

// 逐个摆放材质球
materials.forEach((mat, i) => {
  const mesh = new THREE.Mesh(geometry, mat);
  mesh.position.x = (i - 3.5) * 2;
  mesh.castShadow = true;
  scene.add(mesh);
});

// 粒子系统
const particlesGeo = new THREE.BufferGeometry();
const count = 500;
const positions = new Float32Array(count * 3);
for (let i = 0; i < count * 3; i++) positions[i] = (Math.random() - 0.5) * 10;
particlesGeo.setAttribute('position', new THREE.BufferAttribute(positions, 3));
const particles = new THREE.Points(particlesGeo, new THREE.PointsMaterial({ color: 0xffffff, size: 0.05 }));
scene.add(particles);

// 精灵
const spriteTex = new THREE.TextureLoader().load('https://threejs.org/examples/textures/sprite.png');
const sprite = new THREE.Sprite(new THREE.SpriteMaterial({ map: spriteTex }));
sprite.position.set(0, 3, 0);
scene.add(sprite);

// 渲染循环
function animate() {
  requestAnimationFrame(animate);
  renderer.render(scene, camera);
}
animate();

五、拓展

  1. 性能优化 :移动端尽量避免 PhysicalMaterial,优先 LambertMatcap
  2. 项目应用 :建筑可视化用 StandardMaterial,游戏用 ToonMaterial,展示产品用 PhysicalMaterial
  3. 调试工具NormalMaterialDistanceMaterial 特别适合调试几何体和阴影效果。

六、潜在问题

  • 性能瓶颈:PBR 在低端设备上容易掉帧。
  • 阴影丢失 :如果 renderer.shadowMap 没启用,ShadowMaterial 不生效。
  • 光照依赖 :忘记添加光源时,LambertPhongStandard 会显示黑色。
  • 兼容性RawShaderMaterial 在不同浏览器可能表现差异。

AI 生成内容声明

本文由人工智能生成,仅供参考与学习,不构成专业建议。

相关推荐
掘金安东尼2 小时前
抛弃自定义模态框:原生Dialog的实力
前端·javascript·github
hj5914_前端新手6 小时前
javascript基础- 函数中 this 指向、call、apply、bind
前端·javascript
薛定谔的算法6 小时前
低代码编辑器项目设计与实现:以JSON为核心的数据驱动架构
前端·react.js·前端框架
Hilaku6 小时前
都2025年了,我们还有必要为了兼容性,去写那么多polyfill吗?
前端·javascript·css
yangcode6 小时前
iOS 苹果内购 Storekit 2
前端
LuckySusu6 小时前
【js篇】JavaScript 原型修改 vs 重写:深入理解 constructor的指向问题
前端·javascript
LuckySusu6 小时前
【js篇】如何准确获取对象自身的属性?hasOwnProperty深度解析
前端·javascript
LuckySusu6 小时前
【js篇】深入理解 JavaScript 作用域与作用域链
前端·javascript
LuckySusu6 小时前
【js篇】call() 与 apply()深度对比
前端·javascript