three.js TSL BypassNode:在节点图中执行无返回值的副作用并保留原输出

发布时间:2026/9/7 4:33:28
three.js TSL BypassNode:在节点图中执行无返回值的副作用并保留原输出 three.js TSL BypassNode在节点图中执行无返回值的副作用并保留原输出【免费下载链接】three.jsJavaScript 3D Library.项目地址: https://gitcode.com/GitHub_Trending/th/three.js本篇围绕 three.js 的 TSLThree Shading Language节点系统中的BypassNode展开。它解决一个看似简单却高频出现的节点图问题当某个输入位需要“有返回值”的节点而你想在那里调用一个不返回值的函数或节点即void类型时如何让副作用代码被执行、同时输出值保持来自另一个节点。读完后你将理解BypassNode的构造参数与属性、其代码生成逻辑以及在 three.js 源码中真实的使用方式。核心定位为“void 调用”开辟旁路BypassNode的官方定义是见 BypassNode 文档该类会生成给定节点callNode的代码但在输出上返回另一个节点outputNode。这可用于在需要返回值的输入处调用一个不返回值void类型的方法或节点。换句话说在 TSL 节点图中几乎每个节点表达式都必须“产出值”。但很多操作是纯副作用的——例如向某个 buffer 写入数据、更新 uniform、执行stack的清理、调用只负责“干活”的函数——它们没有有意义的返回值。BypassNode就是把这种调用“旁路”接进需要返回值的位置callNode真正被执行、产生副作用的节点其代码会被生成到 shader 中outputNode该表达式最终对外呈现的值与 callNode 的返回值无关。文档给出的原始示例material.colorNode myColor.bypass( runVoidFn() )意思是material.colorNode最终取myColor的值但在求值过程中会先执行runVoidFn()的副作用代码。从源码结构看这正是 BypassNode.js 中generate方法的行为先构建callNode按void类型把生成的代码片段注入到 shader 的行内代码流中再返回outputNode的构建结果generate( builder ) { const snippet this.callNode.build( builder, void ); if ( snippet ! ) { builder.addLineFlowCode( snippet, this ); } return this.outputNode.build( builder ); }注意两个细节this.callNode.build( builder, void )显式按void类型构建调用节点表明它预期该节点不返回值只有当副作用代码片段非空snippet ! 时才通过 NodeBuilder 的addLineFlowCode将其加入行内代码流line flow避免向 shader 注入无意义的空语句。此外generateNodeType也直接委托给 outputNode源码第 58-62 行generateNodeType( builder ) { return this.outputNode.getNodeType( builder ); }这保证了BypassNode在类型推导上“透明”——它对节点图其余部分的类型签名完全等价于outputNode本身调用方感知不到旁路的存在。API 参考构造函数new BypassNode( outputNode : Node, callNode : Node )创建一个旁路节点。参数说明outputNode输出节点。决定该表达式对外返回的值与类型callNode调用节点。决定被执行产生副作用的代码。属性.outputNode : Node输出节点。.callNode : Node调用节点。.isBypassNode : booleanreadonly类型测试标志默认为true可用于instanceof之外的鸭子类型判断。对应的 TSL 函数除类本身外BypassNode.js 文件末尾 还导出并提供两种调用方式export const bypass /*__PURE__*/ nodeProxy( BypassNode ).setParameterLength( 2 ); addMethodChaining( bypass, bypass );bypass( outputNode, callNode )TSL 全局函数形式通过nodeProxy生成BypassNode实例参数长度固定为 2.bypass( callNode )通过addMethodChaining挂载的方法链形式任意节点上都可以直接调用这也是文档示例myColor.bypass( runVoidFn() )能成立的原因。该模块通过 Nodes.js 与 TSL.js 统一导出因此既可以import { BypassNode } from three也可以在 TSL 上下文中直接使用bypass函数或.bypass()方法。实战用法基本示例执行副作用同时保留原颜色import { material, color, uniform } from three/tsl; // 假设 setEnv() 是一个不返回值的 TSL 函数void material.colorNode myColor.bypass( setEnv() ); // 上式等价于bypass( myColor, setEnv() )生成 shader 时setEnv()的函数调用语句会先出现在行内代码流中而colorNode的最终赋值仍然使用myColor的值。一个真实的源码用例LightsNode 的 stack 清理在 three.js 光照节点的实现中BypassNode被用来处理“执行清理、保留输出”的场景。LightsNode.js 第 424 行outgoingLightNode outgoingLightNode.bypass( builder.removeStack() );这里outgoingLightNode是光照计算完成后对外输出的节点累加了总漫反射、总镜面反射光等见 第 417-420 行builder.removeStack()是 NodeBuilder 上用于弹出栈内所有节点引用、结束一段作用域的方法它的返回值被移除的StackNode在这里并不关心用.bypass()把“弹出栈”这个副作用绑定在输出节点上保证光照输出值不被破坏同时清理代码仍然被生成并执行。这与 TSL 栈机制成对出现addStack/removeStackNodeBuilder.js 第 1871-1906 行用于在 shader 构建中管理临时变量的作用域而 TSLCore.js 的setupOutput展示了典型的“入栈—求值—出栈”结构setupOutput( builder ) { builder.addStack(); builder.stack.outputNode this.call( builder ); return builder.removeStack(); }可以看出BypassNode是 TSL 构建器体系中处理“执行与取值分离”这一需求的基础构件。工作原理小结BypassNode在代码生成阶段的完整流程节点图求值到BypassNode时generateNodeType将类型声明完全转交给outputNode使外部看到的就是outputNode的类型generate先以void类型构建callNode得到副作用代码片段若片段非空调用builder.addLineFlowCode( snippet, this )将副作用语句写入当前 shader 的行内代码流最后返回outputNode.build( builder )的结果即该表达式对外暴露的值。由此BypassNode以极小的实现成本整个类不到 100 行提供了 TSL 节点图中“命令式副作用”与“声明式求值”之间的桥接副作用代码确定会被生成而输出值完全由你指定的outputNode决定。相关文档BypassNode 文档页BypassNode 源码NodeBuilder 栈管理实现TSL 函数与节点代理机制TSL 总览文档【免费下载链接】three.jsJavaScript 3D Library.项目地址: https://gitcode.com/GitHub_Trending/th/three.js创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考