Three.js 节点材质完全解析:MeshLambertNodeMaterial 的 Lambert 光照实现与 WebGPU 使用指南
本文围绕 Three.js 的 MeshLambertNodeMaterial 展开,它是经典 MeshLambertMaterial 在**节点材质体系(Node Material / TSL)**下的对等实现,用于在无高光的非光泽表面(如未上漆的木材、石材)上提供性能友好的 Lambert 漫反射光照。读完本文,你将掌握它的继承结构、构造函数参数、两个只读/行为属性的含义、setupEnvironment 与 setupLightingModel 的底层原理,以及它如何被 WebGPURenderer 的 StandardNodeLibrary 自动接管渲染。
概览:什么是 MeshLambertNodeMaterial
MeshLambertNodeMaterial 是文档页 MeshLambertNodeMaterial.html.md 所描述的对象,官方用一句话定位它:
Node material version of
MeshLambertMaterial.
它位于节点材质文件族 NodeMaterials.js 中,与 MeshBasicNodeMaterial、MeshPhongNodeMaterial 等并列导出。其核心实现只有约 80 行,位于 MeshLambertNodeMaterial.js,整套逻辑建立在基类 NodeMaterial(约 1368 行)的通用节点材质框架之上。
继承链
按文档定义,其继承关系为:
EventDispatcher → Material → NodeMaterial → MeshLambertNodeMaterial
关键点在于中间层 NodeMaterial。基类把材质从"一组固定 uniform 的着色器模板"升级为"可编程的节点图":漫反射、自发光、法线、环境等都可以用 TSL 节点接管。MeshLambertNodeMaterial 只负责两件事——覆盖光照模型和覆盖环境映射策略,其余外观属性全部复用自 MeshLambertMaterial 的默认值。
为何 Lambert:无高光非光泽材质的取舍
要理解这个节点材质,先要理解它的"经典版"兄弟 MeshLambertMaterial.js。源码头部注释清晰说明了设计意图:
- 使用非物理的 Lambertian(朗伯)反射模型计算反射率;
- 适合模拟未上漆木材、石材等表面,无法模拟带镜面高光的光泽表面(如清漆木材);
- 采用逐片元(per-fragment)着色;
- 由于反射与光照模型简单,性能通常优于
MeshPhongMaterial、MeshStandardMaterial、MeshPhysicalMaterial,代价是图形精度略低。
因此 MeshLambertNodeMaterial.lights 必须为 true——它依赖场景光源做漫反射计算,这与 NodeMaterial 基类默认的 lights = false 恰好相反(见 NodeMaterial.js 附近注释),这一点在下文属性一节会再次印证。
构造与实例化
构造函数签名
new MeshLambertNodeMaterial( parameters : Object )
parameters 为可选配置对象,其中可包含该材质(含继承来的)任意属性。颜色类属性接受 Color.set 支持的所有写法(十六进制、CSS 字符串、THREE.Color 实例等)。
使用方式
由于源码 MeshLambertNodeMaterial.js 的构造函数逻辑是 super() → 设置自有标记 → setDefaultValues( _defaultValues ) → setValues( parameters ),所以传参方式与经典材质完全一致:
import { WebGPURenderer, Mesh, BoxGeometry, MeshLambertNodeMaterial } from 'three/webgpu';
const material = new MeshLambertNodeMaterial( {
color: 0xddbb99, // 漫反射颜色,默认 0xffffff
emissive: 0x000000, // 自发光颜色,默认黑色
flatShading: true, // 平直着色
} );
const mesh = new Mesh( new BoxGeometry( 1, 1, 1 ), material );
值得注意的一行关键源码在模块顶部:
const _defaultValues = /*@__PURE__*/ new MeshLambertMaterial();
这意味着默认值并非逐一手写,而是直接实例化一个经典 MeshLambertMaterial,再将其全部自有属性拷入节点材质实例。基类 NodeMaterial#setDefaultValues(见 NodeMaterial.js)遍历传入材质实例的属性并赋值。这一"复用刷新逻辑"的设计,使得经典材质的全部外观参数在节点材质上开箱即用。
自动转换:WebGPU 下的经典材质无缝升级
当你不显式使用 MeshLambertNodeMaterial,而只是把一个普通 new MeshLambertMaterial(...) 交给 WebGPURenderer 渲染时,渲染器会通过材质映射库 StandardNodeLibrary.js 自动把 MeshLambertMaterial 适配为 MeshLambertNodeMaterial:
this.addMaterial( MeshBasicNodeMaterial, 'MeshBasicMaterial' );
this.addMaterial( MeshLambertNodeMaterial, 'MeshLambertMaterial' );
因此熟悉经典 API 的用户在迁移到 WebGPU 后端时几乎不需要改业务代码——升级在渲染管线内部透明完成。
属性详解
.isMeshLambertNodeMaterial : boolean(只读)
类型测试标志,用于在代码中判断某个材质是否为 Lambert 节点材质:
if ( material.isMeshLambertNodeMaterial ) { /* ... */ }
默认恒为 true,在构造函数中写入(见 MeshLambertNodeMaterial.js)。注意它与经典版的 isMeshLambertMaterial 是不同的标志,两者分别标记两条材质家族。
.lights : boolean
Lambert 材质依赖光照,故该属性为 true。这是对基类 NodeMaterial#lights(基类默认 false)的显式覆盖(Overrides)。在基类中,节点材质通过 lights = true 表明"受场景全部灯光影响";如需"选择性光照",可在基类提供的 lightsNode 属性上挂一个由 lights( [ light1, light2 ] ) 构造的自定义灯光节点(见 NodeMaterial.js 附近说明),该能力对 Lambert 节点材质同样生效。
外观属性全集:继承自 MeshLambertMaterial 的默认值
由于 setDefaultValues( new MeshLambertMaterial() ) 的存在,以下出自 MeshLambertMaterial.js 的属性在 MeshLambertNodeMaterial 上全部可用。整理如下(未注明时括号内为默认值):
| 类别 | 属性(默认值) | 说明 |
|---|---|---|
| 漫反射 | color (0xffffff) |
漫反射基色,与 map 相乘 |
| 贴图 | map (null) |
颜色贴图,需设 SRGBColorSpace |
| 烘焙光照 | lightMap (null) / lightMapIntensity (1) |
预烘焙光照贴图,需第二套 UV,多为 LinearSRGBColorSpace 的 .exr/.hdr |
| 环境光遮蔽 | aoMap (null) / aoMapIntensity (1) |
使用红色通道,强度范围 [0,1],需第二套 UV,无颜色空间 |
| 自发光 | emissive (0x000000) / emissiveIntensity (1) / emissiveMap (null) |
不受其他光源影响的自发光;有 emissiveMap 时请把 emissive 设成非黑色 |
| 凹凸 | bumpMap (null) / bumpScale (1) |
黑白深度感知,仅影响光照;定义了法线贴图时被忽略 |
| 法线 | normalMap (null) / normalMapType (TangentSpaceNormalMap) / normalScale ((1,1)) |
左手系法线贴图需将 y 取负以修正 |
| 位移 | displacementMap (null) / displacementScale (1) / displacementBias (0) |
真正移动顶点,可投射阴影;需配套法线贴图 |
| 高光贴图 | specularMap (null) |
Lambert 模型本身无高光,但保留该贴图槽位与经典版对齐 |
| Alpha | alphaMap (null) |
灰度透明度(黑透明白不透明),仅用绿色通道采样 |
| 环境 | envMap (null) / envMapRotation (Euler) / combine (MultiplyOperation) / reflectivity (1) / envMapIntensity (1) / refractionRatio (0.98) |
环境映射;MixOperation 时用 reflectivity 混合 |
| 线框 | wireframe (false) / wireframeLinewidth (1) / wireframeLinecap ('round') / wireframeLinejoin ('round') |
线框渲染(仅 SVGRenderer 支持线宽/端点样式) |
| 着色/雾 | flatShading (false) / fog (true) |
平直着色与雾效开关 |
映射到节点材质后,颜色与贴图仍受同一套 ColorSpace 约定约束;alpha 通道配合 Material#transparent 或 alphaTest 生效。这些属性都会在实例化后通过 setValues( parameters ) 被覆盖为传入值,行为与经典 MeshLambertMaterial 完全一致。
方法深度剖析
.setupEnvironment( builder : NodeBuilder ) : BasicEnvironmentNode.<vec3>
Lambert 材质使用 BasicEnvironmentNode(而非 PBR 的 PMREM/辐照度环境模型)实现默认环境映射。源码实现非常简短(MeshLambertNodeMaterial.js):
setupEnvironment( builder ) {
const envNode = super.setupEnvironment( builder );
return envNode ? new BasicEnvironmentNode( envNode ) : null;
}
含义是:先让基类 NodeMaterial#setupEnvironment 解析环境来源(例如 envNode 或材质自身的 envMap),若解析到环境节点,就把它包装成 BasicEnvironmentNode;否则返回 null。包装后的节点在 BasicEnvironmentNode.js 的 setup() 中执行关键动作:
builder.context.environment = cubeMapNode( this.envNode );
即把环境写入 builder 上下文,供 BasicLightingModel.finish() 消费。注释明确指出:BasicEnvironmentNode 面向非 PBR 材质(如 MeshBasicNodeMaterial 或 MeshPhongNodeMaterial),立方体/经纬环境贴图在这里走 cubeMapNode 采样路径。
值得区分:基类若没有可用环境节点会返回 null,此时 Lambert 节点材质的环境通道整体关闭,环境贴图不会产生任何效果。
.setupLightingModel() : PhongLightingModel
这是"Lambert"语义真正落地的地方(MeshLambertNodeMaterial.js):
setupLightingModel( /*builder*/ ) {
return new PhongLightingModel( false ); // ( specular ) -> force lambert
}
MeshLambertNodeMaterial 复用了 Phong 光照模型类 PhongLightingModel.js,但传入构造参数 specular = false,用源码注释的话说就是 "force lambert"——强制关闭镜面分量,只保留漫反射。
PhongLightingModel 内部结构印证了这一机制:
- 构造函数默认
specular = true(PhongLightingModel.js),注释指明:要得到"无高光、非光泽表面"的 Lambert 式材质,应置为false。 direct()直接光部分(第 67-80 行)先计算dotNL(法线与光线方向的点积,经clamp())得到辐照度irradiance,再乘以BRDF_Lambert( diffuseColor.rgb )累加进reflectedLight.directDiffuse;只有this.specular === true时才追加BRDF_BlinnPhong高光计算与materialSpecularStrength。indirect()间接光部分(第 87-95 行)把辐照度(IBL)同样经BRDF_Lambert累加,再乘环境光遮蔽。
结论:specular = false 意味着即使 specularMap 有值,Blinn-Phong 高光分支也完全不会进入着色器图。这正是 Lambert 材质"没有镜面高光"的本质来源,也是它与 MeshPhongNodeMaterial(使用 new PhongLightingModel( true ))在代码层面的唯一分水岭。如果读者想要高光,应改用 MeshPhongNodeMaterial;想要基于物理的高光/粗糙度,则应选用 PBR 的 MeshStandardNodeMaterial。
从底层到实战:一个完整的渲染链路
综合上述源码,一次渲染经历的数据流可以概括为:
- 场景灯光 → 由
NodeMaterial组装为LightsNode(或经lightsNode选择性裁剪); MeshLambertNodeMaterial.setupLightingModel()注入PhongLightingModel(false),剔除高光分支;setupEnvironment()用BasicEnvironmentNode包装环境贴图,走cubeMapNode快速采样;- 节点图编译后生成 WebGPU/WebGL 着色器,逐片元计算
BRDF_Lambert漫反射并叠加 AO 与雾效。
如果只想快速验证其行为,可参照本文档阅读 源文件 及其被引用处:模块出口在 NodeMaterials.js,WebGPU 材质映射在 StandardNodeLibrary.js,光照模型细节见 PhongLightingModel.js,环境节点见 BasicEnvironmentNode.js。
常见疑问速查
- 问:为什么它在三套(Basic/Lambert/Phong)节点材质里"复用 Phong 模型"?
答:因为 Lambert 本质是 Phong 模型去掉镜面分量的退化形态。通过
PhongLightingModel(false)统一实现,代码复用度最高,代价仅是一个布尔分支。 - 问:
lights明明默认被覆盖为true,基类却默认false? 答:这是刻意为之——大部分纯 TSL 节点材质(如 Basic)不需要动态光源;Lambert 需要。覆盖行为正是文档标注 "Overrides: NodeMaterial#lights" 的原因。 - 问:要不要手动给每个网格 new 一个 Lambert 节点材质?
答:不需要。WebGPU 后端已通过
StandardNodeLibrary将经典MeshLambertMaterial自动替换为节点版本;手动实例化仅在需要精确控制节点级属性(如自定义envNode、lightsNode)时才有意义。 - 问:想在 WebGL 后端用 TSL 场景怎么办?
答:仓库 package.json 的
exports提供了./webgpu(WebGPU 全量)与./tsl(TSL 管线)两个入口;本材质位于 WebGPU 全量构建所导出的 NodeMaterials.js,应结合你实际使用的渲染后端选择对应入口。
需要再次强调的是:选用 Lambert 系材质,意味着你已接受"无高光、非物理、追求性能"的定位。若场景以 PBR 质感的金属、玻璃、清漆表面为主,Lambert 节点材质并非合适的载体——这正是本项目同时维护 Phong、Standard、Physical 等多套节点材质的原因。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0630
MiniCPM5-2BMiniCPM5-2B 是一款面向端侧、本地部署和资源受限场景的 2B 稠密 Transformer,能够达到同尺寸开源模型 SOTA 水平。Markdown00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00