【three.js教程】Three.js 材质详解:从“不反光“到“物理级真实“怎么选

Three.js 材质详解:从"不反光"到"物理级真实"怎么选

原文出处:Three.js Manual -- Materials

本文基于 three.js 官方手册"材质"章节整理,用通俗的方式带你理清 three.js 里五花八门的材质该怎么选、怎么用。


写在前面

前面我们聊了图元------也就是那些现成的 3D 形状。但你肯定发现了,一个球体光秃秃的,没有颜色、没有质感,看着特别假。让它"有模有样"的东西,就是材质(Material)

材质决定了物体表面怎么和光互动、看起来是什么质感。three.js 里的材质种类很多:Basic、Lambert、Phong、Standard、Physical、Toon......这篇就帮你把这些材质梳理一遍,介绍什么时候该用哪个。


一、怎么设置一个材质

先说最基础的:材质属性有两种设置时机。

第一种,创建时直接传参数(我们前面一直这么用):

js 复制代码
const material = new THREE.MeshPhongMaterial({
  color: 0xff0000,     // 红色,这里也可以传 CSS 颜色字符串
  flatShading: true,
});

第二种,创建之后再改

js 复制代码
const material = new THREE.MeshPhongMaterial();
material.color.setHSL(0, 1, 0.5);  // 红色
material.flatShading = true;

特别是颜色(THREE.Color 类型),它的设值方式特别多,记几个常用的就够:

js 复制代码
material.color.set(0x00ffff);                 // 和 CSS 的 #RRGGBB 一样
material.color.set('purple');                 // 直接传 CSS 颜色名
material.color.set('#F32');                    // CSS 简写
material.color.set('rgb(255, 127, 64)');      // CSS rgb 写法
material.color.set('hsl(180, 50%, 25%)');     // CSS hsl 写法
material.color.setHSL(h, s, l);               // h/s/l 都是 0~1
material.color.setRGB(r, g, b);               // r/g/b 都是 0~1

创建时也可以混用十六进制和字符串:

js 复制代码
const m1 = new THREE.MeshBasicMaterial({ color: 0xff0000 });       // 红色
const m2 = new THREE.MeshBasicMaterial({ color: 'red' });         // 红色
const m3 = new THREE.MeshBasicMaterial({ color: '#F00' });        // 红色
const m4 = new THREE.MeshBasicMaterial({ color: 'rgb(255,0,0)' }); // 红色
const m5 = new THREE.MeshBasicMaterial({ color: 'hsl(0,100%,50%)' }); // 红色

二、最基础的三属性:Basic / Lambert / Phong

这三个是 three.js 里最基础的受光材质,区别在于怎么计算光照

材质 光照计算方式 特点
MeshBasicMaterial 完全不受光 永远一个色,像贴了自发光贴纸
MeshLambertMaterial 只在顶点处算光照 便宜,适合低端设备
MeshPhongMaterial 逐像素算光照,支持高光 有镜面反光点,最"塑料感"

MeshBasicMaterial 最简单------不管有没有光,它都显示你给的颜色,像灯泡自己会发光。

MeshPhongMaterial 比 Lambert 高级的地方在于它支持高光(specular highlight) ,也就是物体表面那个亮亮的反光点。高光的"锐利程度"由 shininess 参数控制,默认是 30:

  • shininess: 0:高光铺得很开,看起来像磨砂
  • shininess: 30:默认值,适中
  • shininess: 150:高光聚成一个小点,看起来像釉面/瓷器

还有个有意思的技巧:如果你把 MeshLambertMaterialMeshPhongMaterialemissive(自发光色)设成某个颜色,同时把 color 设成黑色(Phong 再把 shininess 设 0),效果就跟 MeshBasicMaterial 一模一样了。换句话说,Basic 其实是 Lambert/Phong 的"阉割版"。

那为什么还要保留三个?答案很简单:越高级的材质越费 GPU 。在手机这种性能弱的设备上,用简单材质能省不少电。所以原则是:不需要的功能就别用 ------不需要光照和高光,就用 MeshBasicMaterial,别无脑上 Phong。


三、Toon 材质:卡通渲染

MeshToonMaterialMeshPhongMaterial 很像,但有个关键区别:它不用平滑渐变来表现明暗,而是用一张渐变贴图(gradient map)来分级

默认的分级是"前 70% 亮度统一 70%,后 30% 统一 100%",所以物体看起来只有两三个色块,像卡通片里的上色。你也可以自己提供 gradient map 来定义分几级、每级什么亮度。

适合做日系卡通风格、二次元渲染那种效果。


四、物理级材质:Standard 和 Physical(PBR)

前面那几个材质用的都是"凑合好看"的简单数学。但真实世界里光的行为复杂得多------金属和非金属反光不一样,粗糙和光滑反光也不一样。为了逼近真实,three.js 提供了 PBR(Physically Based Rendering,基于物理的渲染) 材质。

MeshStandardMaterial

这是目前最常用、也是官方推荐的"标准"材质。它和 Phong 最大的参数区别:

  • Phong 用 shininess(越小越亮)
  • Standard 用两个新参数:roughness (粗糙度)和 metalness(金属度)

roughness 基本就是 shininess 的反面:

  • 高 roughness(接近 1):像棒球,表面粗糙,没有硬反光
  • 低 roughness(接近 0):像台球,非常光滑,反光锐利
  • 取值范围 0~1

metalness 表示"这东西有多像金属":

  • 0:非金属(塑料、木头、皮肤)
  • 1:纯金属(铁、铜、金)

这两个参数可以组合出无数种质感,比如"粗糙的金属""光滑的塑料""磨砂玻璃"。

MeshPhysicalMaterial

MeshPhysicalMaterialMeshStandardMaterial 基础上多加了一层清漆效果,新增两个参数:

  • clearcoat:0~1,表示表面镀了一层多厚的透明清漆(比如车漆、钢琴漆)
  • clearCoatRoughness:这层清漆本身有多粗糙

它适合做车漆、带保护层的家具、手机背板这种"底层是某种材质、表面又镀了一层"的效果。

性能排序

从快到慢排个队:

复制代码
MeshBasicMaterial → MeshLambertMaterial → MeshPhongMaterial
→ MeshStandardMaterial → MeshPhysicalMaterial

越靠后的越真实,但也越费性能。低端机/移动端尽量往前选。


五、三个特殊用途材质

这三个不常用,但知道有它们会省事:

材质 干嘛用的
ShadowMaterial 只显示阴影。做阴影相关效果时用来显示阴影数据
MeshDepthMaterial 把每个像素的深度渲染出来(近的偏黑,远的偏白),某些后期特效会用到
MeshNormalMaterial 把几何体的法线方向用颜色画出来。x=红、y=绿、z=蓝,调试模型朝向时很有用

六、自定义着色器:ShaderMaterial / RawShaderMaterial

如果你想做完全自定义的效果(比如水波、溶解、全息),可以用 ShaderMaterial 写 GLSL 着色器。它还能借助 three.js 帮你处理一部分底层逻辑。

RawShaderMaterial 更彻底------完全不依赖 three.js 的任何帮助,你自己掌控所有。这两个话题比较大,会在后面专门讲。


七、两个最常用的通用属性

所有材质都共享一堆通用设置(定义在 Material 基类里),挑两个最实用的说:

flatShading(平面着色)

默认 false。设为 true 后,物体表面会按每个三角面平整上色,而不是平滑过渡------也就是传说中的 low-poly 风格。配合低面数球体特别好看。

side(显示哪一面)

默认 THREE.FrontSide(只显示正面)。其他可选:

  • THREE.BackSide:只显示背面
  • THREE.DoubleSide:两面都显示

大部分 3D 物体是实心不透明的,背面朝内,不用画。但PlaneGeometry 这种没有厚度的平面 ,从背面看会直接消失------所以这种场景必须把 side 设成 THREE.DoubleSide


八、一个容易踩的坑:needsUpdate

大多数情况下你改材质属性,three.js 会自己重新应用。但有几类设置改了之后不会自动生效,得手动告诉它:

js 复制代码
material.needsUpdate = true;

需要在改完后设 needsUpdate = true 的情况主要是:

  1. 改了 flatShading
  2. 增删了纹理(注意:只是换纹理图片不用,但从"有纹理"变"没纹理"或反过来,就得设)

从纹理切回无纹理时,更省事的做法是直接换一张 1×1 的白色像素纹理,而不是真的去掉纹理。

大部分 App 这辈子都不会碰到这个问题------你很少会在运行时切换 flat shading 或有没有纹理。知道有这么回事就行。


回顾一下

材质这东西,核心记住三句话:

  1. 不需要光照就用 Basic,要真实就用 Standard,中间按性能和效果权衡选 Lambert / Phong / Toon。
  2. PBR 时代首选 MeshStandardMaterial ,用 roughness + metalness 两个参数就能调出绝大多数日常质感。
  3. 平面类物体记得设 THREE.DoubleSide,否则背面看不见。

材质还有一大块内容没展开------纹理(Texture)。那是另一个大话题,会单独讲。在那之前,先消化这几个材质就够用了。


延伸阅读

相关推荐
远离UE422 分钟前
UE5 母材质 编写笔记
笔记·ue5·材质
Highcharts.js33 分钟前
Highcharts for Python 一套 Python 可视化图表生成库
开发语言·python·highcharts·可视化图表
这是程序猿37 分钟前
深入剖析Java HashMap:底层原理、数据结构与核心机制全解析
java·开发语言·数据结构
nike0good1 小时前
CF 126B(Password-z algorithm/exkmp)
开发语言·c++·算法
huaweichenai1 小时前
PHP实现对接MQTT协议
开发语言·php
牧羊人.3331 小时前
动手学深度学习 01:核心组件与完整训练流程
开发语言·人工智能·深度学习
晓晓_za8986681 小时前
GEO 搜索源码白帽合规改造:适配各大 AI 信源收录规则
java·开发语言·人工智能·性能优化·开源
一条破秋裤1 小时前
C-数据类型
c语言·开发语言·chrome
waillyer1 小时前
企业知识库智能问答 Agent(React + Nest)
javascript·redis·agent