three.js MeshPhysicalMaterial 详解:基于物理的着色进阶属性、配置参数与实战指南

【免费下载链接】three.js JavaScript 3D Library. 【免费下载链接】three.js 项目地址: 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)—— 拉丝金属

属性类型/范围默认值含义
.anisotropynumber,0.0~1.00各向异性强度
.anisotropyMapTexturenullR/G 通道存放切向-副切向空间内 [-1,1] 的方向向量,B 通道为 [0,1] 强度(与 anisotropy 相乘);non-color data
.anisotropyRotationnumber0在切向-副切向空间中逆时针旋转的弧度;存在贴图时叠加额外旋转

方向贴图的 R/G 表达的是切向/副切向空间里的向量,B 通道只承载强度,因此它是典型 non-color data。旋转量以弧度计,逆时针为正。

清漆层组(Clearcoat)—— 车漆 / 碳纤维 / 湿表面

属性类型/范围默认值含义
.clearcoatnumber,0.0~1.00清漆层强度,开启多层材质(基底 + 薄透光层)的入口
.clearcoatMapTexturenullR 通道逐像素缩放 clearcoat;non-color data
.clearcoatRoughnessnumber,0.0~1.00清漆层粗糙度
.clearcoatRoughnessMapTexturenullG 通道逐像素缩放 clearcoatRoughness;non-color data
.clearcoatNormalMapTexturenull为清漆层提供独立法线贴图(例如在平整漆面下做划痕细节)
.clearcoatNormalScaleVector2,(0,0)~(1,1)(1,1)clearcoatNormalMap 对清漆层的影响强度

值得注意的通道约定:官方文档与源码均明确 clearcoatRoughness 读的是贴图 G 通道、clearcoat 读的是 R 通道、而 clearcoatNormalMap 是独立的法线贴图。混用通道是这类材质最常见的使用错误。

物理传输与体积组(Transmission / Volume)—— 玻璃与半透明塑料

这是 MeshPhysicalMaterial 区别于标准材质最重量级的一组能力,属于 "Physically-based transparency":

属性类型/范围默认值含义
.transmissionnumber,0.0~1.00透射(光学透明)程度
.transmissionMapTexturenullR 通道逐像素缩放 transmission;non-color data
.thicknessnumber0表面下介质的体积厚度(以网格自身坐标空间为单位);0 表示薄壁材质,非 0 表示体积边界
.thicknessMapTexturenullG 通道存放厚度值,与 thickness 相乘;non-color data
.attenuationDistancenumber(世界空间单位,须 > 0)Infinity介质密度,即光在介质中与粒子相互作用前传播的平均距离;决定光被吸收的快慢
.attenuationColorColor(1,1,1)白光传播到衰减距离后因吸收而呈现的颜色,用于模拟彩色玻璃
.dispersionnumber(≥ 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)—— 肥皂泡 / 油膜 / 昆虫翅膀

属性类型/范围默认值含义
.iridescencenumber,0.0~1.00虹彩层强度,模拟随表面-视点夹角变化的 RGB 色相偏移
.iridescenceMapTexturenullR 通道逐像素缩放 iridescence;non-color data
.iridescenceIORnumber,1.0~2.3331.3以折射率表征的虹彩 RGB 色移强度
.iridescenceThicknessMapTexturenullG 通道存放虹彩层厚度;non-color data
.iridescenceThicknessRangeArray(恰好 2 个元素)[100,400]虹彩层厚度最小/最大值区间

.iridescenceThicknessMap 的映射规则(文档明示):

  • G 通道 0.0 → 厚度等于数组第一个元素;
  • G 通道 1.0 → 厚度等于数组第二个元素;
  • 中间值在两个元素间线性插值。

虹彩层厚度对色的影响,等价于 thickness 之于 ior(光程差改变干涉色相),因此以纳米级数值为合理量级,默认区间 [100,400] 正是针对可见光波段的经验设定。

反射率与镜面反射组(Advanced Reflectivity)—— 非金属的精细反射控制

属性类型/范围默认值含义
.iornumber,1.0~2.3331.5非金属材质折射率
.reflectivitynumber,0.0~1.00.5(对应 ior = 1.5)非金属反射程度;metalness = 1 时无效果
.specularIntensitynumber,0.0~1.01缩放非金属镜面反射量;设为 0 时模型近似 Lambertian
.specularIntensityMapTexturenullA 通道逐像素缩放 specularIntensity;non-color data
.specularColorColor(1,1,1)为法线入射处的镜面反射着色(仅非金属)
.specularColorMapTexturenullRGB 通道逐像素缩放 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)—— 布料与织物

属性类型/范围默认值含义
.sheennumber,0.0~1.00绒毛层强度
.sheenColorColor(0,0,0)绒毛着色
.sheenColorMapTexturenullRGB 通道逐像素缩放 sheenColor;color data,应设 SRGBColorSpace
.sheenRoughnessnumber,0.0~1.01绒毛层粗糙度(默认 1,即默认完全不锐利)
.sheenRoughnessMapTexturenullA 通道逐像素缩放 sheenRoughness;non-color data

织物边缘发白的"绒毛反光"由 sheenColor 控制色相、sheenRoughness 控制扩散程度,是羽绒服、丝绒、地毯等材质建模的基础。注意 sheenRoughnessMap 走 alpha 通道而非 RGB,这是它与 clearcoatRoughnessMap(G 通道)不同的读取约定。

类型判定与其它成员

属性默认值含义
.isMeshPhysicalMaterialtrue(只读)运行时类型探测标志

源码在 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 场景面板中对照查看。

使用要点清单

  1. 环境贴图几乎必需:透射折射、清漆反射与金属反射都依赖环境采样,文档明确建议"always specify an environment map"。
  2. colorSpace 双轨制:非颜色数据贴图(强度/粗糙/法线/厚度等)保持 NoColorSpace;颜色数据贴图(sheenColorMap、specularColorMap)设为 SRGBColorSpace。
  3. transmission 与 opacity 二选一:transmission > 0 时应让 opacity = 1,把透明度交给物理透射路径处理。
  4. 按需开启特性:每开启一项高级效果(transmission 尤甚)都会引入额外着色分支乃至额外渲染 pass,只开启视觉所需的最小集合。
  5. 共享 MeshStandardMaterial 生态:其全部基础属性、defines 机制、copy()、类型判定标志以及 MeshPhysicalMaterial.tests.js 覆盖的单元测试均可直接沿用,迁移成本极低。
  6. ior / reflectivity 可互换:两者由存取器自动换算,同一材质内只需维护其中一个,避免对非金属反射率做双重调参。

相关资源

【免费下载链接】three.js JavaScript 3D Library. 【免费下载链接】three.js 项目地址: https://gitcode.com/GitHub_Trending/th/three.js

更多推荐