three.js MeshPhysicalMaterial 详解:基于物理的着色进阶属性、配置参数与实战指南
three.js MeshPhysicalMaterial 详解:基于物理的着色进阶属性、配置参数与实战指南
【免费下载链接】three.js JavaScript 3D Library. 项目地址: https://gitcode.com/GitHub_Trending/th/three.js
MeshPhysicalMaterial 是 three.js 中对 MeshStandardMaterial 的基于物理渲染(PBR)扩展,它在一个统一材质内提供了各向异性(Anisotropy)、清漆层(Clearcoat)、虹彩(Iridescence)、物理传输(Transmission)、高级反射率、逆反射(Retroreflection)与织物绒毛光泽(Sheen)等七大高级能力。本文以 MeshPhysicalMaterial 官方文档 为骨架,结合本仓库 src/materials/MeshPhysicalMaterial.js 源码、着色器程序管理与真实示例,完整讲解构造方式、每一组属性的取值范围与默认值、贴图通道约定及性能开销,并给出可直接运行的车漆、玻璃等材质配置。
材质层级与核心定位
从源码类声明与文档开头的继承链可以看到:
Inheritance: EventDispatcher → Material → MeshStandardMaterial → MeshPhysicalMaterial
src/materials/MeshPhysicalMaterial.js 中类直接 extends MeshStandardMaterial,这意味着它继承了标准材质全部基础能力(color、metalness、roughness、normalMap、envMap 等),因此可以在场景里无缝替换 MeshStandardMaterial 使用。它在构造函数中还额外写入了两个编译期宏:
this.defines = {
'STANDARD': '',
'PHYSICAL': ''
};
从 src/materials/MeshPhysicalMaterial.js 可以看到 STANDARD 与 PHYSICAL 两个 defines 常驻开启,这正是 shader 中调用扩展版 PBR 光照代码分支的开关。
相比普通材质,MeshPhysicalMaterial 为以下物理现象提供了建模能力:
- Anisotropy(各向异性):表现拉丝金属一类表面沿特定方向反光差异明显的高光。
- Clearcoat(清漆层):车漆、碳纤维、潮湿表面等在粗糙基底层之上还有一层清晰反光薄膜的情形,无需额外创建透明面。
- Iridescence(虹彩):颜色随观察角与光照角变化,如肥皂泡、水面油膜、昆虫翅膀。
- Physically-based transparency(物理透明):弥补 Material.opacity 在高度透明时反射不足的问题,更真实地表现玻璃这类薄而透明的表面。
- Advanced reflectivity(高级反射率):对非金属材质提供更灵活的反射控制。
- Retroreflection(逆反射):将镜面高光折返向光源方向,用于路标、反光带等安全材料(源码注释中列出,文档正文的示例列表中亦出现于类注释)。
- Sheen(织物绒毛层):用于布匹、织物类表面。
性能提示(原文强调):由于上述复杂着色特性,MeshPhysicalMaterial 的逐像素计算开销高于 three.js 其它材质;绝大多数效果默认处于关闭状态,开启一个才增加一份开销。想要得到最佳效果,使用该材质时务必搭配环境贴图(environment map)。
构造器与参数约定
new MeshPhysicalMaterial( parameters : Object )
构造一个新的物理材质实例。与所有 three.js 材质一致,parameters 是包含一个或多个外观属性的对象,继承链上的任何属性(包括继承自 Material、MeshStandardMaterial 的属性)都能在此传入,颜色值可以传任意被 Color#set 接受的类型(如十六进制数、Color 实例、CSS 颜色字符串)。
构造函数末尾统一通过 this.setValues( parameters ) 完成属性赋值(见 src/materials/MeshPhysicalMaterial.js)。
import * as THREE from 'three';
const glass = new THREE.MeshPhysicalMaterial( {
color: 0xffffff,
metalness: 0, // 非金属
roughness: 0.05,
transmission: 1, // 全透射,模拟玻璃
ior: 1.5, // 折射率
thickness: 0.01, // 体积厚度
specularIntensity: 1,
specularColor: 0xffffff,
envMap: hdrEquirect, // 物理材质最佳实践:必配环境贴图
envMapIntensity: 1
} );
分组属性详解:数值、默认值与贴图通道约定
文档反复出现的 "non-color data"(非颜色数据)是一个关键使用规则:凡标注为 non-color data 的贴图(法线、粗糙度、强度、厚度等),其纹理必须保持
texture.colorSpace = NoColorSpace(默认值),否则渲染会错误。而标注为 color data 的贴图(如sheenColorMap、specularColorMap)则应设置texture.colorSpace = SRGBColorSpace。
各向异性组(Anisotropy)—— 拉丝金属
| 属性 | 类型/范围 | 默认值 | 含义 |
|---|---|---|---|
.anisotropy | number,0.0~1.0 | 0 | 各向异性强度 |
.anisotropyMap | Texture | null | R/G 通道存放切向-副切向空间内 [-1,1] 的方向向量,B 通道为 [0,1] 强度(与 anisotropy 相乘);non-color data |
.anisotropyRotation | number | 0 | 在切向-副切向空间中逆时针旋转的弧度;存在贴图时叠加额外旋转 |
方向贴图的 R/G 表达的是切向/副切向空间里的向量,B 通道只承载强度,因此它是典型 non-color data。旋转量以弧度计,逆时针为正。
清漆层组(Clearcoat)—— 车漆 / 碳纤维 / 湿表面
| 属性 | 类型/范围 | 默认值 | 含义 |
|---|---|---|---|
.clearcoat | number,0.0~1.0 | 0 | 清漆层强度,开启多层材质(基底 + 薄透光层)的入口 |
.clearcoatMap | Texture | null | R 通道逐像素缩放 clearcoat;non-color data |
.clearcoatRoughness | number,0.0~1.0 | 0 | 清漆层粗糙度 |
.clearcoatRoughnessMap | Texture | null | G 通道逐像素缩放 clearcoatRoughness;non-color data |
.clearcoatNormalMap | Texture | null | 为清漆层提供独立法线贴图(例如在平整漆面下做划痕细节) |
.clearcoatNormalScale | Vector2,(0,0)~(1,1) | (1,1) | clearcoatNormalMap 对清漆层的影响强度 |
值得注意的通道约定:官方文档与源码均明确 clearcoatRoughness 读的是贴图 G 通道、clearcoat 读的是 R 通道、而 clearcoatNormalMap 是独立的法线贴图。混用通道是这类材质最常见的使用错误。
物理传输与体积组(Transmission / Volume)—— 玻璃与半透明塑料
这是 MeshPhysicalMaterial 区别于标准材质最重量级的一组能力,属于 "Physically-based transparency":
| 属性 | 类型/范围 | 默认值 | 含义 |
|---|---|---|---|
.transmission | number,0.0~1.0 | 0 | 透射(光学透明)程度 |
.transmissionMap | Texture | null | R 通道逐像素缩放 transmission;non-color data |
.thickness | number | 0 | 表面下介质的体积厚度(以网格自身坐标空间为单位);0 表示薄壁材质,非 0 表示体积边界 |
.thicknessMap | Texture | null | G 通道存放厚度值,与 thickness 相乘;non-color data |
.attenuationDistance | number(世界空间单位,须 > 0) | Infinity | 介质密度,即光在介质中与粒子相互作用前传播的平均距离;决定光被吸收的快慢 |
.attenuationColor | Color | (1,1,1) | 白光传播到衰减距离后因吸收而呈现的颜色,用于模拟彩色玻璃 |
.dispersion | number(≥ 0,现实值典型 [0,1]) | 0 | 光线穿过相对清澈体积时的色散/色差强度;仅对透射物体生效 |
文档特别注明:当 transmission 非零时,opacity 应设置为 1——这是物理模型要求,因为全透射的薄塑料/玻璃即便完全透明也保有较强反射,用 opacity 降低透明度反而会削弱反射、破坏物理真实性。
transmission 的实现代价也是最高的:从 WebGLRenderer.js 源码可见,WebGL 渲染器会为每个摄像机维护 transmissionRenderTarget,先把场景背后内容渲染进一个多采样缓冲,再做 mipmap 更新与回采,且视口需与该缓冲严格对齐。该渲染目标的大小受 renderer.transmissionResolutionScale(默认为 1.0,见 WebGLRenderer.js)缩放控制,实际演示 webgl_materials_physical_transmission.html 把它暴露为可调参数:
renderer.transmissionResolutionScale = params.transmissionResolutionScale; // 0.01 ~ 1,可整体缩放透射缓冲分辨率换取性能
虹彩组(Iridescence)—— 肥皂泡 / 油膜 / 昆虫翅膀
| 属性 | 类型/范围 | 默认值 | 含义 |
|---|---|---|---|
.iridescence | number,0.0~1.0 | 0 | 虹彩层强度,模拟随表面-视点夹角变化的 RGB 色相偏移 |
.iridescenceMap | Texture | null | R 通道逐像素缩放 iridescence;non-color data |
.iridescenceIOR | number,1.0~2.333 | 1.3 | 以折射率表征的虹彩 RGB 色移强度 |
.iridescenceThicknessMap | Texture | null | G 通道存放虹彩层厚度;non-color data |
.iridescenceThicknessRange | Array(恰好 2 个元素) | [100,400] | 虹彩层厚度最小/最大值区间 |
.iridescenceThicknessMap 的映射规则(文档明示):
- G 通道
0.0→ 厚度等于数组第一个元素; - G 通道
1.0→ 厚度等于数组第二个元素; - 中间值在两个元素间线性插值。
虹彩层厚度对色的影响,等价于 thickness 之于 ior(光程差改变干涉色相),因此以纳米级数值为合理量级,默认区间 [100,400] 正是针对可见光波段的经验设定。
反射率与镜面反射组(Advanced Reflectivity)—— 非金属的精细反射控制
| 属性 | 类型/范围 | 默认值 | 含义 |
|---|---|---|---|
.ior | number,1.0~2.333 | 1.5 | 非金属材质折射率 |
.reflectivity | number,0.0~1.0 | 0.5(对应 ior = 1.5) | 非金属反射程度;metalness = 1 时无效果 |
.specularIntensity | number,0.0~1.0 | 1 | 缩放非金属镜面反射量;设为 0 时模型近似 Lambertian |
.specularIntensityMap | Texture | null | A 通道逐像素缩放 specularIntensity;non-color data |
.specularColor | Color | (1,1,1) | 为法线入射处的镜面反射着色(仅非金属) |
.specularColorMap | Texture | null | RGB 通道逐像素缩放 specularColor;color data,应设 SRGBColorSpace |
reflectivity 与 ior 是一对自动换算的"镜像"属性。查看 src/materials/MeshPhysicalMaterial.js 源码可见其实质是一个 Object.defineProperty 存取器:
get: function () {
return ( clamp( 2.5 * ( this.ior - 1 ) / ( this.ior + 1 ), 0, 1 ) );
},
set: function ( reflectivity ) {
this.ior = ( 1 + 0.4 * reflectivity ) / ( 1 - 0.4 * reflectivity );
}
即 ior = 1.5 时折算 reflectivity = 0.5,二者指向同一物理量——Fresnel 法线入射反射率——你可以按习惯选用其一,引擎负责换算。
织物绒毛组(Sheen)—— 布料与织物
| 属性 | 类型/范围 | 默认值 | 含义 |
|---|---|---|---|
.sheen | number,0.0~1.0 | 0 | 绒毛层强度 |
.sheenColor | Color | (0,0,0) | 绒毛着色 |
.sheenColorMap | Texture | null | RGB 通道逐像素缩放 sheenColor;color data,应设 SRGBColorSpace |
.sheenRoughness | number,0.0~1.0 | 1 | 绒毛层粗糙度(默认 1,即默认完全不锐利) |
.sheenRoughnessMap | Texture | null | A 通道逐像素缩放 sheenRoughness;non-color data |
织物边缘发白的"绒毛反光"由 sheenColor 控制色相、sheenRoughness 控制扩散程度,是羽绒服、丝绒、地毯等材质建模的基础。注意 sheenRoughnessMap 走 alpha 通道而非 RGB,这是它与 clearcoatRoughnessMap(G 通道)不同的读取约定。
类型判定与其它成员
| 属性 | 默认值 | 含义 |
|---|---|---|
.isMeshPhysicalMaterial | true(只读) | 运行时类型探测标志 |
源码在 src/materials/MeshPhysicalMaterial.js 设置 this.isMeshPhysicalMaterial = true,配合单元测试 MeshPhysicalMaterial.tests.js 中 "MeshPhysicalMaterial.isMeshPhysicalMaterial should be true" 的断言,说明它可用于在材质数组中安全过滤物理材质。
源码级实现洞察:version 自增与着色器分支开关
MeshPhysicalMaterial 中几个"能力开关"型属性(anisotropy、clearcoat、iridescence、dispersion、retroreflectivity、sheen、transmission)在源码里都实现为带副作用的存取器。以 anisotropy 为例(src/materials/MeshPhysicalMaterial.js):
get anisotropy() { return this._anisotropy; }
set anisotropy( value ) {
if ( this._anisotropy > 0 !== value > 0 ) {
this.version ++; // 从"关闭"跨到"开启"(或反之)时版本号自增
}
this._anisotropy = value;
}
这段实现透露了 three.js 的着色器重编译机制:只有当属性从 0 跨过正数边界(或在同侧连续调整)时才需要让程序缓存失效——从关闭切到开启意味着着色器中新增了对应 feature 的 uniform 与代码路径,必须重新编译着色器程序;而保持开启时仅微调强度不会触发重编译。着色器程序管理端 WebGLPrograms.js 同样以"属性 > 0 + 贴图存在"的组合推导各宏:
const HAS_ANISOTROPY = material.anisotropy > 0;
const HAS_CLEARCOAT = material.clearcoat > 0;
const HAS_DISPERSION = material.dispersion > 0;
const HAS_IRIDESCENCE = material.iridescence > 0;
const HAS_SHEEN = material.sheen > 0;
const HAS_TRANSMISSION = material.transmission > 0;
const HAS_TRANSMISSIONMAP = HAS_TRANSMISSION && !! material.transmissionMap;
可见除开 clearcoatMap 这类贴图宏之外,所有开销型特性都以对应数值属性为总闸——数值为 0 时相关贴图与着色分支都不生效,这正是文档"大多数效果默认关闭、开启才增加成本"的底层来源。此外,材质还实现了完整的 copy() 语义(src/materials/MeshPhysicalMaterial.js),iridescenceThicknessRange 以展开拷贝([ ...source.iridescenceThicknessRange ])避免数组引用共享。
实战:按真实示例配置典型材质
案例一:清漆车漆 + 玻璃(webgl_materials_car.html)
车身漆面是 Clearcoat 的教科书用法——金属漆基底之下叠加一层极光滑清漆:
const bodyMaterial = new THREE.MeshPhysicalMaterial( {
color: 0xff0000,
metalness: 1.0, // 金属基底
roughness: 0.5, // 基底层中等粗糙,提供漫反射着色的金属质感
clearcoat: 1.0, // 开启清漆层
clearcoatRoughness: 0.03 // 清漆层极光滑 → 产生锐利高光与"漆面反光"
} );
const glassMaterial = new THREE.MeshPhysicalMaterial( {
color: 0xffffff,
metalness: 0,
roughness: 0,
transmission: 1, // 玻璃车窗:物理透射
transparent: true
// 可再配合 thickness / ior 控制折射与体积
} );
如果要在清漆层上再加划痕等微细法线扰动而不影响基底层法线,使用 clearcoatNormalMap + clearcoatNormalScale 即可让两层拥有各自独立的法线。
案例二:可交互玻璃参数面板(webgl_materials_physical_transmission.html)
该示例把透射类参数完整暴露到 lil-gui,是调试玻璃材质的最佳起点。核心材质初始化与 GUI 回写逻辑为:
const material = new THREE.MeshPhysicalMaterial( {
color: 0xffffff,
metalness: 0,
roughness: 0,
transmission: params.transmission, // 建议为 1(玻璃)
ior: params.ior, // 1 ~ 2,默认 1.5
thickness: params.thickness, // 0 ~ 5
specularIntensity: params.specularIntensity,
specularColor: params.specularColor,
envMap: hdrEquirect, // 环境贴图驱动透射/反射采样
envMapIntensity: params.envMapIntensity
} );
gui.add( params, 'transmission', 0, 1, 0.01 ).onChange( () => material.transmission = params.transmission );
gui.add( params, 'ior', 1, 2, 0.01 ).onChange( () => material.ior = params.ior );
gui.add( params, 'thickness', 0, 5, 0.01 ).onChange( () => material.thickness = params.thickness );
gui.addColor( params, 'specularColor' ).onChange( () => material.specularColor.set( params.specularColor ) );
gui.add( params, 'transmissionResolutionScale', 0.01, 1, 0.01 )
.name( 'transmission resolution' )
.onChange( () => renderer.transmissionResolutionScale = params.transmissionResolutionScale );
示例同样把 renderer.toneMapping = ACESFilmicToneMapping 与环境贴图作为渲染前提,印证了"物理材质 + 环境贴图 + 色调映射"的组合惯例。同目录下还提供了透明度混合进阶版 webgl_materials_physical_transmission_alpha.html 与 WebGPU 版本 webgpu_materials_transmission.html,可用浏览器直接打开查看不同渲染后端下的效果差异。
案例三:各向异性 / 虹彩 / 逆反射的轻量组合
对开启强度类效果,推荐直接使用材质自带的"数值总闸 + 存取器副作用"机制——在动画循环中连续修改即可,无需触碰 uniform:
// 拉丝金属(Brush metal)基础配方
const brushed = new THREE.MeshPhysicalMaterial( {
metalness: 1.0, roughness: 0.3,
anisotropy: 0.5, // 拉丝高光
anisotropyRotation: Math.PI / 2 // 以弧度逆时针旋转拉丝方向
} );
// 手表表盘一类带虹彩的镜面(示例 webgl_watch.html 中使用 iridescence: 0.3)
const dial = new THREE.MeshPhysicalMaterial( {
metalness: 1.0, roughness: 0.1,
iridescence: 0.3,
iridescenceIOR: 1.3
} );
// 反光带/安全材料(示例 webgpu_materials_retroreflection.html 中 retroreflectivity: 1.0)
const tape = new THREE.MeshPhysicalMaterial( {
color: 0xcccccc, metalness: 0, roughness: 0.5,
retroreflectivity: 1.0
} );
anisotropyRotation 的默认值以本仓库源码 src/materials/MeshPhysicalMaterial.js 的 this.anisotropyRotation = 0 为准(弧度制,逆时针为正,从切向量起算)。上述配方对应的完整演示还可在 material-browser 场景面板中对照查看。
使用要点清单
- 环境贴图几乎必需:透射折射、清漆反射与金属反射都依赖环境采样,文档明确建议"always specify an environment map"。
- colorSpace 双轨制:非颜色数据贴图(强度/粗糙/法线/厚度等)保持
NoColorSpace;颜色数据贴图(sheenColorMap、specularColorMap)设为SRGBColorSpace。 - transmission 与 opacity 二选一:
transmission > 0时应让opacity = 1,把透明度交给物理透射路径处理。 - 按需开启特性:每开启一项高级效果(transmission 尤甚)都会引入额外着色分支乃至额外渲染 pass,只开启视觉所需的最小集合。
- 共享 MeshStandardMaterial 生态:其全部基础属性、
defines机制、copy()、类型判定标志以及 MeshPhysicalMaterial.tests.js 覆盖的单元测试均可直接沿用,迁移成本极低。 - ior / reflectivity 可互换:两者由存取器自动换算,同一材质内只需维护其中一个,避免对非金属反射率做双重调参。
相关资源
- 类定义源码:src/materials/MeshPhysicalMaterial.js
- 基类:src/materials/MeshStandardMaterial.js
- 单元测试:test/unit/src/materials/MeshPhysicalMaterial.tests.js
- 着色器特性宏判定:src/renderers/webgl/WebGLPrograms.js
- 透射渲染缓冲实现:src/renderers/WebGLRenderer.js
- 可运行示例:webgl_materials_car.html、webgl_materials_physical_transmission.html、webgl_materials_physical_transmission_alpha.html、webgl_materials_envmaps_fasthdr.html、webgpu_materials_transmission.html
- 交互材质浏览器:docs/scenes/material-browser.html
【免费下载链接】three.js JavaScript 3D Library. 项目地址: https://gitcode.com/GitHub_Trending/th/three.js
更多推荐

所有评论(0)