
Three.js ComputeNode 全解析TSL 计算着色器的节点封装、调度参数与底层执行机制【免费下载链接】three.jsJavaScript 3D Library.项目地址: https://gitcode.com/GitHub_Trending/th/three.jsComputeNode 是 three.js 节点系统TSL中专用于 WebGPU 计算着色器的核心封装类继承链为EventDispatcher → Node → ComputeNode位于 src/nodes/gpgpu/ComputeNode.js。掌握它意味着你可以用 TSL 声明式地编写 GPU 并行计算逻辑粒子模拟、粒子排序、流体等并通过count/dispatchSize/workgroupSize精确控制线程调度。本文基于官方 API 文档 docs/pages/ComputeNode.html.md 与仓库源码完整覆盖其构造参数、属性、方法并深入解析其在 Renderer 中的实际执行链路。构造器与继承体系ComputeNode 继承自Node进而继承EventDispatcher代表一个计算着色器节点Represents a compute shader node。new ComputeNode( computeNode, workgroupSize )参数类型说明computeNodeNode定义计算着色器逻辑的节点workgroupSizeArray.number定义计算着色器执行时 workgroup 的 X、Y、Z 维度从 src/nodes/gpgpu/ComputeNode.js 的源码可以看到构造函数内部首先调用super( void )将节点输出类型固定为void——计算着色器不产出渲染结果只产生副作用写入存储缓冲、纹理等随后初始化isComputeNode true类型标志并保存workgroupSize。所有属性初始值均已在构造函数中显式声明count、dispatchSize、countNode初始为nullversion初始为1name初始为onInitFunction初始为null。属性详解.computeNode : Node定义计算着色器逻辑的节点。这是被包装的实际计算体。.count : number | Array.要执行的线程invocation总数。文档指出当其为 number 类型时会自动生成针对instanceIndex的边界检查bounds checking。这一行为在源码setup()与generate()中得到印证见后文底层执行机制一节。.countNode : UniformNode一个持有 dispatch count 的 uniform 节点用于边界检查。当count为 number 时由setup( builder )自动创建// src/nodes/gpgpu/ComputeNode.js if ( this.count ! null this.countNode null ) { this.countNode uniform( this.count, uint ).onObjectUpdate( () this.count ); }可以看到countNode绑定为uint类型且通过onObjectUpdate在每次对象更新时同步count的最新值——这意味着运行期修改computeNode.count会动态改变着色器内的线程上限。.dispatchSize : number | Array.workgroup 在 X、Y、Z 轴上的 dispatch 尺寸。当count未提供时直接使用。.isComputeNode : boolean (readonly)用于类型测试的标志默认为true。这个标志是 Renderer 校验入参的依据src/renderers/common/Renderer.js 的compute()方法中明确检查computeList[ 0 ].isComputeNode ! true时抛出THREE.Renderer: .compute() expects a ComputeNode.异步预编译接口compileComputeAsync()也有同样的isComputeNode ! true校验——因此该标志并非装饰性的而是调度层识别计算节点的唯一凭据。.name : string节点的名称或标签默认为。覆写自Node#name。.onInitFunction : function计算节点完成初始化时执行的回调。Renderer 在首次编译该节点的 pipeline 时即pipelines.has( computeNode ) false分支调用它并以{ renderer: this }作为参数上下文执行onInitFn.call( computeNode, { renderer: this } )——典型用途是在初始化回调中获取 renderer 后配置存储缓冲、纹理等资源。.updateBeforeType : string覆写自Node#updateBeforeType默认值为object即NodeUpdateType.OBJECT。文档说明因为updateBefore默认每个对象执行一次所以类型设为 OBJECT。这决定了 ComputeNode 的每帧更新时机绑定到场景对象的生命周期上。.version : number节点版本号覆写自Node#version。.workgroupSize : Array.定义 workgroup 的 X、Y、Z 维度默认值为[ 64 ]。方法详解.dispose()触发本节点的dispose事件this.dispatchEvent( { type: dispose } )。Renderer 在首次编译 pipeline 时会通过computeNode.addEventListener( dispose, dispose )监听该事件用于在节点销毁时清理 pipelines、bindings 与 nodes 缓存因此dispose()是资源回收的入口。.setName( name : string ) : ComputeNode设置name属性返回自身引用以支持链式调用。.label( name : string ) : ComputeNode已废弃功能等同于setName但源码中已标记废弃label( name ) { warn( TSL: label() has been deprecated. Use setName() instead., new StackTrace() ); return this.setName( name ); }新代码应直接使用setName()。.onInit( callback : function ) : ComputeNode设置初始化期间运行的回调函数等价于赋值this.onInitFunction callback返回自身。.updateBefore( frame : NodeFrame )执行本节点的计算方法覆写自Node#updateBefore。实现极为简洁updateBefore( { renderer } ) { renderer.compute( this ); }即把自身作为ComputeNode交给 Renderer 的compute()方法调度这是它接入渲染循环的唯一钩子。TSL 工厂函数compute 与 computeKernel除了直接使用构造器TSL 更推荐使用两个工厂函数均定义在 src/nodes/gpgpu/ComputeNode.js 文件底部computeKernel( node, workgroupSize [ 64 ] )创建计算内核节点的 TSL 函数源码中包含两层严格校验workgroupSize长度必须为 1、2 或 3 个元素否则报错TSL: compute() workgroupSize must have 1, 2, or 3 elements每个元素必须是正整数否则报错TSL: compute() workgroupSize element at index [ n ] must be a positive integer校验通过后不足 3 维的部分自动填充1——源码注释说明这与 WGSL 对workgroup_size的处理方式一致。compute( node, count, workgroupSize )在computeKernel基础上增加调度参数关键分支逻辑为if ( typeof count number ) { computeNode.count count; } else { computeNode.dispatchSize count; }即传入数字走count自动获得 instanceIndex 边界检查传入数组则视为 workgroup 的 dispatch 尺寸写入dispatchSize。两个函数均通过addMethodChaining注册因此也可作为节点方法链式调用。底层执行机制setup 与 generate 的双阶段行为理解 ComputeNode 的关键在于它区分了两种 shader stage。源码generate( builder, output )中compute stagebuilder.shaderStage compute调用this.computeNode.build( builder, void )生成计算逻辑代码片段若非空则通过builder.addLineFlowCode( snippet, this )注入。随后若count ! null且builder.allowEarlyReturns true会额外注入一段边界检查代码builder.flow.code ${ builder.tab }if ( ${ indexSnippet } ${ countSnippet } ) { return; }\n\n${ builder.flow.code };这正是文档中自动生成针对instanceIndex的边界检查的实现——把instanceIndex count的线程提前return保证 dispatch 尺寸非 workgroup 整数倍时不会有线程越界写入。非 compute stage读取setup()阶段保存的properties.outputComputeNode并重建输出实现计算节点向渲染阶段的结果透传。Renderer 中的实际调度链路从 src/renderers/common/Renderer.js 的compute()方法可以确认完整的调用链updateBefore( frame )由节点更新系统按updateBeforeType object每对象触发一次内部调用renderer.compute( this )compute()递增info.compute.calls与info.compute.frameCalls统计计数可通过 renderer.info 观测校验isComputeNode标志不通过则抛错对每个尚未编译的节点pipelines.has( computeNode ) false注册dispose监听以自动清理资源调用onInitFunction即.onInit()设置的回调完成初始化再编译 pipeline已通过backend.beginCompute( computeNodes )将计算分发到底层。此外Renderer 还提供异步预编译接口compileComputeAsync( computeNodes, onProgress )源码注释说明其用途是规避首次渲染时的shader compilation stutter同样要求入参通过isComputeNode校验。适用前提与限制ComputeNode 属于 WebGPU 计算管线能力依赖 src/renderers/common/Backend.js 一类的后端抽象Backend中通过abstractRenderContext.isComputeNode分支处理计算上下文实际运行需要支持 WebGPU 的环境workgroupSize各维度必须为正整数长度 1–3默认[ 64 ]边界检查仅在count为 number 且构建上下文允许 early returnallowEarlyReturns true时生效label()已废弃请使用setName()本文所有 API 描述以当前仓库 docs/pages/ComputeNode.html.md 文档与 src/nodes/gpgpu/ComputeNode.js 源码为准。【免费下载链接】three.jsJavaScript 3D Library.项目地址: https://gitcode.com/GitHub_Trending/th/three.js创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考