首页
/ three.js TSL 节点体系核心:InputNode 输入节点基类全解析

three.js TSL 节点体系核心:InputNode 输入节点基类全解析

2026-09-07 14:00:10作者:凌朦慧Richard

InputNode 是 three.js 节点材质/着色器系统(TSL,Three Shading Language)中所有"数据输入节点"的抽象基类,位于 src/nodes/core/InputNode.js。无论是把常量数值写进着色器、声明可动态更新的 uniform,还是把几何属性当作节点输入,最终都由 InputNode 及其派生类完成。本文以官方文档 docs/pages/InputNode.html.md 为骨架,结合源码逐项解析 InputNode 的构造、类型推导、精度控制与序列化机制,并向下追踪 ConstNode、UniformNode、BufferAttributeNode 三个核心派生类的真实用法,帮助你在 TSL 编程中准确选型输入节点。

继承关系与定位

EventDispatcher → Node → InputNode

在 TSL 节点体系中,一切节点(Node)都继承自 Node(其本身是事件派发器 EventDispatcher 的子类),负责保存节点类型、参与构建/缓存、生成着色器代码。而 InputNode 在此基础上专门抽象出"数据输入"这一类职责:它持有一个 JS 侧的真实数值(.value),并负责把该值在着色器生成阶段转译成可用的输入。

从源码注释可知它的核心定位是 "Base class for representing data input nodes",即所有数据输入节点的基类。它的两个固有特征可以被类型测试直接使用:

  • this.isInputNode = true(只读标志,见 src/nodes/core/InputNode.js#L35);
  • this.value 持有 JS 侧值;
  • this.precision 控制该值在着色器中的精度。

InputNode 本身是抽象基类,其 generate() 方法直接调用 warn( 'Abstract function.' )(见 src/nodes/core/InputNode.js#L128-L132),提示子类必须实现实际的代码生成逻辑。

构造函数与自动类型推导

new InputNode( value : any, nodeType : string )
参数 说明 默认值
value 节点持有的值。可以是任意 JS 原始值(number、boolean、string)、函数、ArrayBuffer,也可以是 three.js 对象(Vector2/3/4、Matrix2/3/4、Color 等)
nodeType 节点类型。若显式给出(如 'vec3''float'),则直接采用;若省略,节点会尝试从 value 中推导类型 null

在源码中构造器只是简单地把两个参数上交 super( nodeType ),然后初始化 .value.precision(见 src/nodes/core/InputNode.js#L24-L52),真正的类型逻辑集中在 generateNodeType()

generateNodeType() {
    if ( this.nodeType === null ) {
        return getValueType( this.value );
    }
    return this.nodeType;
}

也就是说,显式指定 nodeType 时它优先于自动推导;当不指定时,由 NodeUtils.js 中的 getValueType() 依据 value 的实际形态推断着色器类型。查阅 src/nodes/core/NodeUtils.js#L229-L291 可得到完整的推导映射表:

value 形态 推导出的类型
undefined / null null
number float
boolean bool
string string
function shader
isVector2 === true 的对象 vec2
isVector3 === true 的对象 vec3
isVector4 === true 的对象 vec4
isMatrix2 === true 的对象 mat2
isMatrix3 === true 的对象 mat3
isMatrix4 === true 的对象 mat4
isColor === true 的对象 color
ArrayBuffer 实例 ArrayBuffer
其他对象 null

这套映射在 getValueFromType()src/nodes/core/NodeUtils.js#L370-L430)中配套存在——它负责把类型名和数值重新构造为对应的 Vector/Matrix/Color 实例,是反序列化路径上的关键辅助函数。

属性详解

.isInputNode : boolean(只读)

类型测试标志,恒为 true。在 TSL 中许多工具函数需要区分节点种类,InputNode 及所有派生类都能通过该标志被快速识别。

.value : any

节点在 JS 侧持有的数据。可以是 JS 原始值、函数、ArrayBuffer 或 three.js 对象。着色器生成阶段会把它按 nodeType 对应的类型写入代码。

.precision : 'low' | 'medium' | 'high'

该值在着色器中使用的精度等级,默认 null。精度控制对 WebGPU 后端(如 f32i32 等数据类型选择)有意义;设为 null 表示不特别指定,由构建器按默认规则处理。它通常通过 .setPrecision() 方法(链式 API 中对应 precision())设置。

核心方法

.getInputType( builder : NodeBuilder ) : string

返回节点的"输入类型",默认直接返回节点类型(this.getNodeType( builder ))。派生类可能覆盖此方法以返回固定类型或做分析式计算。

文档中给出了最有代表性的例子——纹理:一张普通 RGBA 纹理,其"输入类型"是 texture,而"节点类型"是 vec4。二者的语义差异在于:在着色器代码生成阶段,声明的是采样器(texture),而表达式取出的最终值类型是 vec4。类似的覆盖在源码中也可直接看到,例如 UniformNode 的 getInputType()src/nodes/core/UniformNode.js#L169-L181)会把 bool 提升为 uint,以满足 WebGPU 对 uniform 存储类型的约束:

getInputType( builder ) {
    let type = super.getInputType( builder );
    if ( type === 'bool' ) type = 'uint';
    return type;
}

.setPrecision( precision : 'low' | 'medium' | 'high' ) : InputNode

设置精度并返回 this,以支持链式调用。若最终精度需要分析式计算,派生类可覆盖此方法。实现位于 src/nodes/core/InputNode.js#L90-L96

序列化与反序列化机制(源码补充)

官方文档未展开但源码中完整实现的 serialize() / deserialize() 方法(见 src/nodes/core/InputNode.js#L98-L126),保证了 InputNode 能随 TSL 节点树被 JSON 持久化或在 Web Worker 中传递:

  • serialize() 保存 valuevalueTypenodeTypeprecision;若 value 具有 toArray() 方法(Vector/Matrix/Color 皆有)则序列化为数组;若 valueType 为 ArrayBuffer,则经 arrayBufferToBase64() 转成 Base64 字符串后再保存;
  • deserialize() 反向还原:数组按 valueType 交给 getValueFromType() 重建;具有 fromArray() 的对象则调用对应方法填充;ArrayBufferbase64ToArrayBuffer() 还原。

这也解释了为何 getValueFromType() 要维护一张与类型映射表反向对应的"构造表"。

派生类:InputNode 如何落地为真实功能

src/nodes 目录的结构看,直接继承 InputNode 的核心实现有三处:ConstNodeUniformNodeBufferAttributeNode。它们各自解决了"输入数据"在不同作用域下的着色器接入问题。

ConstNode —— 编译期常量

src/nodes/core/ConstNode.js 把值作为着色器内的编译期常量输出。它调用 builder.generateConst() 把 JS 值展开成字面量,并对数值类型做了特殊优化:当节点类型与输出目标同为 float|u?int 时直接生成目标类型的常量,避免多余转换(见 src/nodes/core/ConstNode.js#L51-L63)。适合数学常量、配置值等静态输入。

UniformNode —— 运行时可更新 uniform

src/nodes/core/UniformNode.js 把值提升为着色器 uniform,运行时可被 CPU 侧更新,对应 TSL 中最常用的 uniform() 工厂函数。它引入了完整的作用域与共享机制:

  • .name.setName() 控制 uniform 在着色器中的命名;
  • .groupNode / .setGroup() 决定 uniform 归属于哪个 UniformGroupNode(objectGroup、frameGroup、renderGroup 等),默认按对象管理(objectGroup);
  • getSharedNode() 依据 hash 复用 uniform:同一 hash 的 uniform 在构建器中只注册一次,同名节点共享同一份声明(见 src/nodes/core/UniformNode.js#L120-L149);
  • onUpdate() 支持注册逐帧回调动态改写 .value

典型用法(配合 NodeMaterial 的节点属性):

const timeUniform = uniform( 0 );
material.colorNode = timeUniform.mul( vec3( 1, 0.5, 0.25 ) );
// 每帧更新
timeUniform.value = performance.now() / 1000;

当把一个节点传入 uniform() 时,它会沿节点树寻找其中的常量值并提取为 uniform 的初始值(见 src/nodes/core/UniformNode.js#L253-L269)。

BufferAttributeNode —— 节点级几何属性

src/nodes/accessors/BufferAttributeNode.js 允许在节点层级直接提供属性数据(而不必挂到 geometry 上),特别适合由 compute shader 生成的几何。构造时接受 BufferAttributeInterleavedBuffer 或裸 TypedArray,并额外带 bufferTypebufferStridebufferOffset 三个参数描述内存布局。官方注释中给出的两个典型场景:

// 在节点层直接为材质提供顶点颜色
const colors = [];
for ( let i = 0; i < position.count; i ++ ) colors.push( 1, 0, 0 );
material.colorNode = bufferAttribute( new THREE.Float32BufferAttribute( colors, 3 ) );

// 把计算着色器产出的存储缓冲直接转为属性
material.positionNode = positionBuffer.toAttribute();

其构造器签名见 src/nodes/accessors/BufferAttributeNode.js#L79-L120,若打算逐帧更新数据应使用 .setUsage( THREE.DynamicDrawUsage )

使用建议与选型小结

  • 需要编译期固定的常量(如 PI、通道系数)→ 选 ConstNode(TSL 中常用 float(0.5) 等快捷写法);
  • 需要CPU 逐帧更新 / 跨渲染对象共享的值 → 选 UniformNode(uniform(...));
  • 需要在节点层绑定顶点缓冲数据 → 选 BufferAttributeNode(bufferAttribute(...));
  • 自己实现新节点时,只要本质是"输入某个外部数据",都应继承 InputNode,从而免费获得 .precision、类型自动推导以及 serialize/deserialize 序列化能力。

当 nodeType 不确定时,记得利用 InputNode 的推导规则(参照上文映射表):传入 Vector3 自动得到 vec3、传入 Color 得到 color;需要强约束时则显式传入第二个参数,例如 new InputNode( someVector, 'vec3' ),此时显式类型优先生效。

补充说明:以上行为均以当前仓库 src/nodes/core/InputNode.js 及其派生实现为准,NodeBuilder 完成的实际类型格式化、uniform 分配等后续流程可继续阅读 src/nodes/core/NodeBuilder.js 与 TSL 入口文档 docs/TSL.md

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

项目优选

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