首页
/ three.js MeshStandardNodeMaterial 深度指南:用 TSL 节点接管标准 PBR 材质的金属度、粗糙度与自发光

three.js MeshStandardNodeMaterial 深度指南:用 TSL 节点接管标准 PBR 材质的金属度、粗糙度与自发光

2026-09-07 16:52:27作者:翟萌耘Ralph

MeshStandardNodeMaterial 是 three.js 中经典 MeshStandardMaterial 的“节点材质(Node Material)”版本,专为 TSL(Three Shading Language)与 WebGPU 渲染管线设计。本文以其官方 API 文档 docs/pages/MeshStandardNodeMaterial.html.md 为骨架,结合仓库内源码与真实示例,系统讲解它的构造参数、emissiveNode / metalnessNode / roughnessNode 等节点化属性,以及 setupEnvironmentsetupLightingModelsetupSpecularsetupVariants 等内部管线方法。读完你将掌握如何在节点化材质中“用节点覆盖(overwrite)”或“用访问器修改(modify)”标准 PBR 通道,并理解其底层实现原理。

一、MeshStandardNodeMaterial 是什么:经典标准材质的节点化实现

在 three.js 中,每种经典材质几乎都有对应的节点化版本,例如 MeshBasicNodeMaterialMeshLambertNodeMaterialMeshPhongNodeMaterial,而 MeshStandardNodeMaterial 对应的是物理正确的标准材质 MeshStandardMaterial。这一点在原文档中有明确表述:

Node material version of MeshStandardMaterial.

它与经典版本的核心区别在于:经典材质只能通过固定属性(colormetalnessroughnessemissive 等)配置,而节点化材质允许你在着色器层用 TSL 节点自由接管任意通道,例如让金属度、粗糙度甚至自发光颜色随 UV、噪声、纹理或时间动态变化。

继承链(原文档开头给出):

EventDispatcher → Material → NodeMaterial → MeshStandardNodeMaterial

二、构造器与参数:new MeshStandardNodeMaterial( parameters )

2.1 签名与语义

new MeshStandardNodeMaterial( parameters : Object )

parameters 是可选配置对象,语义与经典 MeshStandardMaterial 保持一致。之所以能直接传经典属性,原因在构造函数实现(src/materials/nodes/MeshStandardNodeMaterial.js L32-L96)中清晰可见:

  1. 设置只读标志 isMeshStandardNodeMaterial = truelights = true
  2. 将节点通道 emissiveNodemetalnessNoderoughnessNode 初始化为 null
  3. 调用 this.setDefaultValues( _defaultValues ),其中 _defaultValues = new MeshStandardMaterial() 是模块级缓存的一个经典材质实例;
  4. 最后 this.setValues( parameters ) 合入用户配置。

setDefaultValues(基类实现见 src/materials/nodes/NodeMaterial.js L1207-L1238)会把经典 MeshStandardMaterial 的全部属性及其访问器(getter/setter)复制到节点材质上。这意味着:任何你在经典材质上见过的属性——colormapmetalnessmetalnessMaproughnessroughnessMapemissiveemissiveIntensityemissiveMapnormalMapaoMapenvMap 等——在 MeshStandardNodeMaterial 上仍然全部可用,且默认值一致。

2.2 最小示例

在真实示例中,直接传入颜色对象即可使用,例如 examples/webgpu_lights_selective.html 中的写法:

import * as THREE from 'three/webgpu';

const leftObject = new THREE.Mesh(
  geometryTeapot,
  new THREE.MeshStandardNodeMaterial( { color: 0x555555 } )
);

// 后续仍可按经典方式赋值,也可再挂节点
const material = new THREE.MeshStandardNodeMaterial( {
  color: 0xffffff,
  metalness: 0.9,      // 金属质感
  roughness: 0.25,     // 表面粗糙度
  emissive: 0xff4400,
  emissiveIntensity: 0.5
} );

2.3 运行时类型

material.isMeshStandardNodeMaterial === true   // 只读,默认 true
material.isNodeMaterial === true               // 继承自 NodeMaterial
material.type === 'MeshStandardNodeMaterial'   // 静态 type getter 返回

isMeshStandardNodeMaterial 用于类型测试(type testing),是只读标志,默认值为 true

三、核心节点属性:emissiveNode / metalnessNode / roughnessNode

这三个节点属性是该材质区别于经典版本的关键。它们遵循同一设计哲学:默认从经典属性推导,赋节点后则“整体覆盖(overwrite)”默认推导逻辑

3.1 .emissiveNode : Node.<vec3>

类型为 vec3 节点,默认 null

原文档说明:标准材质的自发光默认由 emissiveemissiveIntensityemissiveMap 三个属性共同推导。而 emissiveNode 允许你用一个自定义节点完全覆盖这一默认推导,直接定义发光颜色。

// 覆盖默认:恒定红色自发光
material.emissiveNode = color( 0xff0000 );

// 覆盖默认:把随时间变化的辉光接到自发光
material.emissiveNode = color( 0xff6600 ).mul( sin( time ).add( 1 ) );

如果你不想覆盖、而想在既有自发光(含贴图)基础上做修改,文档明确建议改用 TSL 中导出的 materialEmissive 访问器(MaterialNode 中的材质引用节点):

// 修改而不是覆盖:给既有自发光叠加热烈的红色色调
material.emissiveNode = materialEmissive.add( color( 0xff0000 ) );

在片元流程中,NodeMaterial.setupLighting 会把最终 emissive 贡献累加到出射光上(见 src/materials/nodes/NodeMaterial.js L1101-L1109):存在 emissiveNode 节点时使用节点,否则回落到经典的 materialEmissive(即材质实例上的 emissive 颜色)。

3.2 .metalnessNode : Node.<float>

类型为 float 节点,默认 null

默认金属度由 metalnessmetalnessMap 推导;metalnessNode 覆盖之。例如给一个模型整体指定金属度,或接入某张 mask 贴图做逐像素金属分布:

material.metalnessNode = float( 0.8 );               // 整体 0.8
material.metalnessNode = texture( maskTexture );     // 按纹理逐像素金属度

只想在既有金属度基础上调整,用 materialMetalness 访问器:

// 保留 metalnessMap 贡献的同时做范围压缩/增强
material.metalnessNode = materialMetalness.mul( 0.5 );

3.3 .roughnessNode : Node.<float>

类型为 float 节点,默认 null

默认粗糙度由 roughnessroughnessMap 推导;roughnessNode 覆盖之。仓库真实案例 examples/jsm/generators/TerrainGenerator.js L474 展示了用 mix 在两个粗糙度极值间按雪面 mask 插值的典型写法:

material.roughnessNode = mix( float( 0.95 ), float( 0.72 ), snowMask );

即:无雪区域粗糙(0.95),积雪区域更光滑(0.72),两者按 snowMask 平滑过渡。若要保留原有粗糙度贴图只做修正,则用 materialRoughness

material.roughnessNode = materialRoughness.mul( float( 0.9 ) );

3.4 “覆盖 vs 修改”的统一原则

emissiveNode / metalnessNode / roughnessNode 与访问器 materialEmissive / materialMetalness / materialRoughness 的配合关系,可以归纳为一个通用规则:赋值 xxxNode = 覆盖默认推导(贴图贡献将失效);基于 materialXxx 组合新节点 = 在默认值上做数学修改(贴图贡献被保留在输入中)。这是三个属性共用的模式,也是 NodeMaterial 体系下 colorNode/normalNode/aoNode 等其它通道的通用做法。

3.5 继承自 NodeMaterial 的其它节点通道

由于继承自 NodeMaterialMeshStandardNodeMaterial 实例还拥有基类定义的一整组节点属性(src/materials/nodes/NodeMaterial.js L58-L392),包括但不限于:

属性 节点类型 作用
colorNode Node.<vec3> 覆盖漫反射基色(默认来自 color/map
opacityNode Node.<float> 覆盖透明度(默认来自 opacity/alphaMap
normalNode Node.<vec3> 覆盖法线(默认来自 normalMap/bumpMap
aoNode Node.<float> 覆盖环境光遮蔽
envNode Node.<vec3> 覆盖环境反射来源
alphaTestNode Node.<float> 覆盖 alpha 测试阈值
positionNode Node.<vec3> 覆盖顶点位置(位移/形变)
fragmentNode / vertexNode Node.<vec4> 完全接管片元/顶点着色器逻辑
outputNode Node.<vec4> 替换最终输出颜色(内置光照仍执行)

四、片元通道的底层实现:setupVariants 与 setupSpecular

原文档列出 setupSpecular()setupVariants(builder) 两个方法,它们正是三个节点属性在片元管线里“落地”的地方。完整实现如下(src/materials/nodes/MeshStandardNodeMaterial.js L134-L172):

setupSpecular() {

	const specularColorNode = mix( vec3( 0.04 ), diffuseColor.rgb, metalness );

	specularColor.assign( vec3( 0.04 ) );
	specularColorBlended.assign( specularColorNode );
	specularF90.assign( 1.0 );

}

setupVariants() {

	// METALNESS

	const metalnessNode = this.metalnessNode ? float( this.metalnessNode ) : materialMetalness;

	metalness.assign( metalnessNode );

	// ROUGHNESS

	let roughnessNode = this.roughnessNode ? float( this.roughnessNode ) : materialRoughness;
	roughnessNode = getRoughness( { roughness: roughnessNode } );

	roughness.assign( roughnessNode );

	// SPECULAR COLOR

	this.setupSpecular();

	// DIFFUSE COLOR

	diffuseContribution.assign( diffuseColor.rgb.mul( metalnessNode.oneMinus() ) );

}

逐行解读这段代码,能精确理解三节点属性的语义:

1) 金属度赋值。 若设了 metalnessNode 则包装为 float 后直接赋值给全局 PBR 变量 metalness,否则使用 materialMetalness(即材质实例的 metalness 属性)。这些全局变量(metalnessroughnessdiffuseColorspecularColor 等)定义于 src/nodes/core/PropertyNode.js,供后续光照模型统一读取。

2) 粗糙度赋值。 与金属度类似,默认走 materialRoughness;随后无论来自节点还是属性,都会再经过 getRoughness() 处理(src/nodes/functions/material/getRoughness.js),其作用是合入 roughnessMap/粗糙度贴图语义,保证节点化路径与经典材质的行为对齐。

3) 镜面反射颜色(setupSpecular)。 这是标准 PBR 的 F0(法线入射反射率)计算:

  • specularColor.assign( vec3( 0.04 ) ):电介质(非金属)表面在法线入射时的基准反射率约为 4%;
  • specularColorBlended.assign( mix( vec3( 0.04 ), diffuseColor.rgb, metalness ) ):随金属度升高,反射颜色从恒定 0.04 向基色 diffuseColor.rgb 过渡——金属表面会用自身基色着色反射光
  • specularF90.assign( 1.0 ):掠射角(grazing angle)处的反射率取 1.0。

4) 漫反射贡献剔除。 diffuseContribution = diffuseColor.rgb * ( 1 - metalness ):纯金属(metalness = 1)不再产生漫反射,全部能量归入镜面反射;metalness = 0 时漫反射保持完整。这也解释了为何高金属度物体“看起来像抛光的镜子”。

setupVariants(builder) 在基类 NodeMaterial.setup L470-L608 的片元阶段被调用(先 setupDiffuseColor 得到基础漫反射,再 setupVariants 建立材质专属变量,随后 setupLighting 计算光照),返回的 outgoingLightNodediffuseColor.a 合成最终片元输出。

五、环境映射与光照模型:setupEnvironment 与 setupLightingModel

5.1 .setupEnvironment( builder : NodeBuilder ) : EnvironmentNode.<vec3>

原文档说明:该方法被覆盖是因为标准材质需要用 EnvironmentNode 实现 PBR(基于 PMREM)的环境映射,同时要尊重 Scene.environment。源码实现(src/materials/nodes/MeshStandardNodeMaterial.js L106-L118):

setupEnvironment( builder ) {

	let envNode = super.setupEnvironment( builder );   // 优先 envNode / envMap

	if ( envNode === null && builder.environmentNode ) {   // 否则读取场景环境

		envNode = builder.environmentNode;

	}

	return envNode ? new EnvironmentNode( envNode ) : null;

}
  • 基类 NodeMaterial.setupEnvironmentsrc/materials/nodes/NodeMaterial.js L948-L964)负责解析材质自身来源:this.envNode 自定义节点优先,其次是 envMap(立方体贴图或普通贴图);
  • 若材质未配置任何环境,则回退读取 builder.environmentNode,即来自 Scene.environment 的环境;
  • 最终无论哪种来源都会包进 EnvironmentNodesrc/nodes/lighting/EnvironmentNode.js),交给光照阶段做基于 PMREM 的 IBL(基于图像的光照)采样。

这是标准材质能“自动反射场景环境贴图”的机制来源:经典示例中只需设置 scene.environment = pmremGenerator.fromScene( ... ).texture,标准节点材质即可产生环境反射,无需额外代码。

5.2 .setupLightingModel() : PhysicalLightingModel

标准 PBR 材质使用物理光照模型 PhysicalLightingModel。该方法是抽象接口 NodeMaterial.setupLightingModelsrc/materials/nodes/NodeMaterial.js L1062-L1066)的覆盖实现:

setupLightingModel( /*builder*/ ) {

	return new PhysicalLightingModel();

}

它定义于 src/nodes/functions/PhysicalLightingModel.js,承载标准材质整套光照计算。不同的节点材质选用不同光照模型:如 Lambert、Phong、Matcap 各有专属模型,而 MeshStandardNodeMaterial 用物理模型以支持金属/粗糙度工作流。该模型也常作为扩展基点——例如 src/materials/nodes/MeshSSSNodeMaterial.jsSSSLightingModel extends PhysicalLightingModel,在物理模型之上叠加次表面散射。

NodeMaterial.setupLighting L1074-L1112 中,基类会把 setupLightingModel() 返回的模型与场景 LightsNode、材质环境节点、AO 节点、背景(backdrop)节点组合进 lightingContext,统一求值出射光。

5.3 关于 .lights = true

lights 属性默认 true(覆盖自基类 NodeMaterial 的默认值 false,见 src/materials/nodes/NodeMaterial.js L79-L85)。语义为:标准材质会响应场景光照(方向光、点光、聚光灯、IBL 环境等)。若你基于 NodeMaterial 自定义派生材质却忘开此开关,材质将不会受光——这是自建节点材质时最常见的“模型全黑/不受光”问题根源。

六、在真实场景中组合使用

6.1 一个可运行的整体示例

import * as THREE from 'three/webgpu';
import { WebGPURenderer } from 'three/webgpu';
import { color, float, materialRoughness, mix, normalLocal, positionLocal, time } from 'three/tsl';

const material = new THREE.MeshStandardNodeMaterial( {
	color: 0xb0a090,          // 经典属性仍然有效
	metalness: 0.1,
	roughness: 0.6,
	envMapIntensity: 1.0
} );

// —— 用节点通道做程序化增强 ——
// 按模型位置 Y 方向做轻微粗糙度渐变
material.roughnessNode = mix( materialRoughness, float( 0.2 ), normalLocal.y.abs() );
// 随时间轻微波动的自发光(可改成对外部传入 uniform 的引用)
material.emissiveNode = color( 0xff4400 ).mul( time.sin().mul( 0.5 ).add( 0.5 ) );

scene.add( new THREE.Mesh( geometry, material ) );

要点:不设任何节点通道时它就是一个“能响应光照、能读 metalnessMap/roughnessMap/envMap”的标准材质;设了节点通道,对应 PBR 变量就被节点接管,即第四、五节所述流程。

6.2 仓库中的真实使用参考

  • examples/webgpu_reflection.html L307:反射(SSR)演示中创建 new THREE.MeshStandardNodeMaterial() 作为被反射物体的材质;
  • examples/webgpu_lights_selective.html:多个茶壶分别使用 new THREE.MeshStandardNodeMaterial( { color: 0x555555 } ),配合 lights() 构建选择性光照;
  • examples/jsm/generators/TerrainGenerator.js L474:地形生成器中 material.roughnessNode = mix(...),用噪声 mask 让雪地/裸地呈现不同粗糙度;
  • 光照、环境、材质相关成体系的 WebGPU/TSL 示例(如 examples/webgpu_materials_envmaps_*.htmlexamples/webgpu_materials_transmission.html)也大量以标准/物理节点材质为底座。

七、扩展与注意事项

7.1 派生与家族关系

src/materials/nodes 目录的类声明可确认如下层次:

NodeMaterial
├── MeshStandardNodeMaterial          ← 本文主题(PBR:金属度/粗糙度工作流)
│   └── MeshPhysicalNodeMaterial      (清漆、透射、光泽等扩展)
│       └── MeshSSSNodeMaterial       (次表面散射扩展)
├── MeshLambertNodeMaterial / MeshPhongNodeMaterial / MeshMatcapNodeMaterial ...
└── MeshBasicNodeMaterial / ...

因此 MeshPhysicalNodeMaterial 自动继承 emissiveNode/metalnessNode/roughnessNodesetupEnvironment/setupLightingModel 等标准通道,再在其上扩展 clearcoatNodetransmissionNode 等物理属性。引用 MeshPhysicalNodeMaterialPhysicalLightingModel 文档可继续深入。

7.2 使用注意点

  • 覆盖 vs 修改:直接赋 metalnessNode = float(1) 会旁路 metalnessMap;如需同时保留贴图请基于 materialMetalness 组合节点。
  • 受光开关lights 默认 true;若派生新材质继承自 NodeMaterial 而非本类,须手动 lights = true 才能受光。
  • 环境来源优先级envNode(材质自定义)> envMap(材质贴图)> Scene.environment,最终统一经 EnvironmentNode 走 PMREM 采样。
  • 运行入口:节点材质由 src/materials/nodes/NodeMaterials.js 统一导出,通常在 three/webgpu(或 three/tsl)入口下以 THREE.MeshStandardNodeMaterial 形式消费(仓库示例即如此),并搭配 WebGPURenderer 使用;TSL 相关语法与 materialEmissive/materialMetalness/materialRoughness 访问器的完整说明见 docs/pages/TSL.html.md

参考资料(仓库内)

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.14 K
2.75 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
857
1.35 K
docsdocs
暂无描述
Markdown
898
5.82 K
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
921
1.84 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.8 K
1.02 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
531
596
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.02 K
519
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.36 K
1.46 K
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
548
391