three.js TSL 中 BasicLightMapNode 解析:无光照材质如何正确处理 Light Map
three.js TSL 中 BasicLightMapNode 解析无光照材质如何正确处理 Light Map【免费下载链接】three.jsJavaScript 3D Library.项目地址: https://gitcode.com/GitHub_Trending/th/three.jsBasicLightMapNode 是 three.js 节点材质体系TSL中一个职责非常聚焦的类它是 IrradianceNode 的特殊版本只为无光照unlit的 MeshBasicNodeMaterial 服务负责把烘焙光照贴图Light Map以正确的缩放系数送入渲染上下文。读完本文你将理解无光照材质为什么不能直接复用通用的辐照度节点、1 / PI缩放因子的物理来源以及 Light Map 从材质属性到最终片元颜色的完整数据链路。一、类定位与 API 概览BasicLightMapNode 位于src/nodes/lighting/目录继承链为EventDispatcher → Node → LightingNode → BasicLightMapNode并通过 src/nodes/Nodes.js 统一导出。官方文档对其定义是A specific version of IrradianceNode that is only relevant for MeshBasicNodeMaterial. Since the material is unlit, it requires a special scaling factor for the light map. IrradianceNode 的一个特定版本仅对 MeshBasicNodeMaterial 有意义。由于该材质是无光照的光照贴图需要一个特殊的缩放因子。完整的 API 面很小包含一个构造函数和一个属性构造函数new BasicLightMapNode( lightMapNode null )参数类型说明lightMapNodeNodevec3默认null提供光照贴图像素值的节点通常为vec3颜色属性.lightMapNode : Nodevec3—— 保存构造时传入的光照贴图节点供setup()阶段求值使用。二、核心实现setup() 中的缩放因子整个类中最关键的逻辑集中在setup()方法中见 BasicLightMapNode.js#L37-L45setup( builder ) { // irradianceLightMap property is used in the indirectDiffuse() method of BasicLightingModel const RECIPROCAL_PI float( 1 / Math.PI ); builder.context.irradianceLightMap this.lightMapNode.mul( RECIPROCAL_PI ); }这里有两点值得注意写入的是专用上下文槽位它没有像通用辐照度节点那样累加到builder.context.irradiance而是把结果写入builder.context.irradianceLightMap。注释明确说明该属性会被BasicLightingModel的间接光照计算消费。1 / PI的缩放因子这正是文档所强调的 special scaling factor。对于有光照的 Lambert/Phong/Toon 模型辐照度在求值时会乘上BRDF_Lambert其中天然包含1 / PI项而无光照的 Basic 模型不执行 BRDF 求值必须在这里显式补上1 / PI才能与旧版MeshBasicMaterial的渲染结果保持一致后文会与 GLSL 旧管线交叉验证。三、与 IrradianceNode 的对比为什么需要特殊版本对照 IrradianceNode 的实现// src/nodes/lighting/IrradianceNode.js setup( builder ) { builder.context.irradiance.addAssign( this.node ); }两者同为LightingNode的子类但去向完全不同对比项IrradianceNodeBasicLightMapNode上下文写入目标builder.context.irradiance累加builder.context.irradianceLightMap赋值缩放处理无原样累加乘1 / PI消费方有光照模型如 PhongLightingModel 中reflectedLight.indirectDiffuse.addAssign( irradiance.mul( BRDF_Lambert( { diffuseColor } ) ) )BasicLightingModel 的间接光照阶段服务对象通用 Node 材质Phong/Toon/Physical 等仅MeshBasicNodeMaterial从源码结构看这一分叉的根源在于有光照模型对辐照度做辐照度 × 朗伯 BRDF的组合而朗伯 BRDF 自带1 / PIBasic 模型跳过 BRDF光贴图值必须预先缩放否则无光照网格的光照贴图会比有光照网格亮 PI 倍。四、数据链路从材质属性到片元输出BasicLightMapNode并不是用户手工挂到场景上的而是由材质构建管线在编译期自动创建。完整调用链如下1. NodeMaterial 的默认实现有光照路径NodeMaterial.setupLightMap 默认创建的是通用IrradianceNodesetupLightMap( builder ) { let node null; if ( builder.material.lightMap ) { node new IrradianceNode( materialLightMap ); } return node; }随后setupMaterialLightings()同文件 L992-L1020会把isLightingNode为真的节点推入materialLightsNode数组最终参与光照上下文组装。2. MeshBasicNodeMaterial 的重写无光照路径MeshBasicNodeMaterial.setupLightMap 重写了该方法替换为BasicLightMapNodesetupLightMap( builder ) { let node null; if ( builder.material.lightMap ) { node new BasicLightMapNode( materialLightMap ); } return node; }源码注释解释了重写原因This method must be overwritten since light maps are evaluated with a special scaling factor for basic materials. 值得注意的是MeshBasicNodeMaterial虽然定义上是无光照的其.lights属性仍被置为true见 构造函数 注释因为渲染管线需要一个光照模型来计算出射光该材质通过setupLightingModel()返回 BasicLightingModel。3. BasicLightingModel 消费 irradianceLightMapBasicLightMapNode写入的上下文槽位最终在这里被读取见 BasicLightingModel.js#L32-L58indirect( { context } ) { const ambientOcclusion context.ambientOcclusion; const reflectedLight context.reflectedLight; const irradianceLightMap context.irradianceLightMap; reflectedLight.indirectDiffuse.assign( vec4( 0.0 ) ); // accumulation (baked indirect lighting only) if ( irradianceLightMap ) { reflectedLight.indirectDiffuse.addAssign( irradianceLightMap ); } else { reflectedLight.indirectDiffuse.addAssign( vec4( 1.0, 1.0, 1.0, 0.0 ) ); } // modulation reflectedLight.indirectDiffuse.mulAssign( ambientOcclusion ); reflectedLight.indirectDiffuse.mulAssign( diffuseColor.rgb ); }由此可还原无光照材质的最终着色逻辑有光照贴图时输出为lightMap × (1/PI) × lightMapIntensity × AO × 漫反射颜色没有光照贴图时退化为基础色AO × diffuseColor等价于旧的vec3(1.0)兜底项。4. 输入节点 materialLightMap 已包含 lightMapIntensity构造函数传入的materialLightMap是 MaterialNode.js 中的访问器其内部逻辑L389-L399为} else if ( scope MaterialNode.LIGHT_MAP ) { if ( material.lightMap ) { node this.getTexture( scope ).rgb.mul( this.getFloat( lightMapIntensity ) ); } else { node vec3( 0.0 ); } }也就是说lightMapIntensity已经在访问器阶段乘入BasicLightMapNode再补上1 / PI两段缩放合起来构成完整的lightMap × lightMapIntensity × (1/PI)公式。五、与旧版 GLSL 管线的交叉验证节点材质体系与遗留 ShaderLib 行为保持一致可以直接对照 meshbasic.glsl.jsreflectedLight.indirectDiffuse lightMapTexel.rgb * lightMapIntensity * RECIPROCAL_PI; // 有 lightMap reflectedLight.indirectDiffuse vec3( 1.0 ); // 无 lightMap // ... reflectedLight.indirectDiffuse * diffuseColor.rgb; vec3 outgoingLight reflectedLight.indirectDiffuse;BasicLightingModel中的RECIPROCAL_PI缩放、vec4(1,1,1,0)兜底项与这里的 GLSL 语句逐一对应可以确认BasicLightMapNode就是把MeshBasicMaterial的 GLSL 语义原样翻译成了 TSL 图且语义等价性有源码级依据。六、实战用法BasicLightMapNode通常无需手工实例化——给MeshBasicNodeMaterial赋lightMap后构建管线会自动完成上述全流程。典型用法如下import { Mesh, BoxGeometry, MeshBasicNodeMaterial } from three; const material new MeshBasicNodeMaterial( { color: 0xffffff, lightMap: bakedLightMapTexture, // 烘焙光照贴图 lightMapIntensity: 1.0, // 由 materialLightMap 访问器乘入 } ); const mesh new Mesh( new BoxGeometry(), material ); scene.add( mesh );如果需要在 TSL 图中显式引用该类例如自定义材质时想复用同一缩放约定写法为import { BasicLightMapNode, materialLightMap } from three/webgpu; // 经 Nodes 导出 // new BasicLightMapNode( materialLightMap ) 等价于 // lightMapNode.rgb × lightMapIntensity × (1/PI) 写入 irradianceLightMap使用时的注意事项仅对MeshBasicNodeMaterial生效其他 Node 材质的lightMap走IrradianceNode路径BasicLightMapNode写入的irradianceLightMap槽位不会被消费依赖光照贴图纹理builder.material.lightMap为空时setupLightMap返回nullBasicLightingModel自动退化为纯基础色路径AO 仍参与调制即使材质无光照环境遮蔽值builder.context.ambientOcclusion来源之一为aoMap仍会乘入最终颜色见 BasicLightingModel.js#L54。七、小结BasicLightMapNode是 three.js 节点材质体系中小而关键的一类组件它用一个约 50 行的类解决了无光照材质与有光照材质在 Light Map 语义上的不一致问题。理解它的关键在于三条线索——setup()中写入irradianceLightMap并乘1/PI、MeshBasicNodeMaterial.setupLightMap对默认IrradianceNode路径的重写、以及BasicLightingModel.indirect()中的消费逻辑。这条链路与旧版 meshbasic.glsl.js 的 GLSL 实现严格对应也为理解 TSL 中上下文槽位 光照模型的协作模式提供了一个清晰的最小样本。更多相关文档可参考 BasicLightMapNode 官方页面、IrradianceNode 与 MeshBasicNodeMaterial。【免费下载链接】three.jsJavaScript 3D Library.项目地址: https://gitcode.com/GitHub_Trending/th/three.js创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考