资讯动态

three.js ColorSpaceNode:TSL 节点系统中的 GPU 色彩空间转换机制

发布时间:2026/9/7 18:01:50 来源:尧图企业网站定制
three.js ColorSpaceNodeTSL 节点系统中的 GPU 色彩空间转换机制【免费下载链接】three.jsJavaScript 3D Library.项目地址: https://gitcode.com/GitHub_Trending/th/three.jsColorSpaceNode 是 three.js 节点着色语言TSL中负责色彩空间转换的核心节点它将一个颜色值从源色彩空间如 sRGB转换到目标色彩空间如 linear-sRGB。本文基于官方文档 ColorSpaceNode 的 API 定义结合 src/nodes/display/ColorSpaceNode.js 的完整源码实现、库内调用点与 GPU 单元测试讲清楚这个节点的构造方式、setup()中的三步转换管线、配套的 TSL 工具函数workingToColorSpace/colorSpaceToWorking/convertColorSpace以及它如何在渲染输出的最后一级被自动用于输出色彩空间转换。1. 定位与继承体系官方文档给出 ColorSpaceNode 的继承链为Inheritance: EventDispatcher → Node → TempNode → ColorSpaceNode对应源码中可以看到类定义与type标识// src/nodes/display/ColorSpaceNode.js class ColorSpaceNode extends TempNode { static get type() { return ColorSpaceNode; } // ... }它位于 src/nodes/display/ 目录下与ToneMappingNode、RenderOutputNode等显示类节点同级。官方文档对其功能的一句话定义是This node represents a color space conversion. Meaning it converts a color value from a source to a target color space.继承自TempNode有一个实际意义TempNode见 src/nodes/core/TempNode.js通过缓存机制在节点被多处引用usageCount 1时生成临时变量避免重复计算。因此ColorSpaceNode在一次着色器编译中被多次引用时其转换结果只会被求值一次并缓存到临时变量中。构造时节点的类型固定为vec4super( vec4 )即颜色空间转换只作用于 RGB 分量alpha 分量原样透传——这一点在setup()源码中可以直接验证见下节。2. API 参考构造函数与属性2.1 new ColorSpaceNode( colorNode, source, target )官方文档定义的构造函数签名为new ColorSpaceNode( colorNode : Node, source : string, target : string )三个参数含义参数类型说明colorNodeNode待转换的颜色节点sourcestring源色彩空间targetstring目标色彩空间源码中的构造函数src/nodes/display/ColorSpaceNode.js#L33-L58仅做三件事将节点类型设为vec4并保存三个属性constructor( colorNode, source, target ) { super( vec4 ); this.colorNode colorNode; // 待转换的颜色 this.source source; // 源色彩空间 this.target target; // 目标色彩空间 }2.2 属性.colorNode : Node— 待转换的颜色.source : string— 源色彩空间.target : string— 目标色彩空间。这三个属性都是普通公开属性节点构建完成后仍可读写由于setup()在每次构建时都会重新解析并读取this.source/this.target因此动态修改这两个字符串属性会影响下一次着色器生成。3. resolveColorSpace()色彩空间常量的解析官方文档对该方法的定义是This method resolves the constantsWORKING_COLOR_SPACEandOUTPUT_COLOR_SPACEbased on the current configuration of the color management and renderer.builder— 当前的节点构建器NodeBuildercolorSpace— 要解析的色彩空间Returns— 解析后的色彩空间字符串。源码实现src/nodes/display/ColorSpaceNode.js#L69-L83揭示了这两个内部常量的具体值与解析规则const WORKING_COLOR_SPACE WorkingColorSpace; const OUTPUT_COLOR_SPACE OutputColorSpace; resolveColorSpace( builder, colorSpace ) { if ( colorSpace WORKING_COLOR_SPACE ) { return ColorManagement.workingColorSpace; } else if ( colorSpace OUTPUT_COLOR_SPACE ) { return builder.context.outputColorSpace || builder.renderer.outputColorSpace; } return colorSpace; }可以归纳为三条解析规则传入字面量WorkingColorSpace时返回全局的ColorManagement.workingColorSpacethree.js 的默认工作色彩空间是LinearSRGBColorSpace由ColorManagement管理传入字面量OutputColorSpace时优先取builder.context.outputColorSpace例如后处理 Pass 在渲染目标上指定的输出色彩空间否则回退到builder.renderer.outputColorSpace渲染器全局输出色彩空间其他情况原样返回传入的字符串例如SRGBColorSpacesrgb、LinearSRGBColorSpacesrgb-linear等three导出的色彩空间常量。这套解析机制正是 TSL 层三个便捷函数的基础开发者不需要自己查询当前工作/输出色彩空间只需要传入WorkingColorSpace/OutputColorSpace占位符节点会在构建时动态解析为渲染器当前配置的实际值。4. setup() 的三步转换管线核心原理ColorSpaceNode真正干活的地方是setup( builder )src/nodes/display/ColorSpaceNode.js#L85-L123。它不直接输出 GLSL而是把自身“展开”为若干基础 TSL 节点组成的子图setup( builder ) { const { colorNode } this; const source this.resolveColorSpace( builder, this.source ); const target this.resolveColorSpace( builder, this.target ); let outputNode colorNode; if ( ColorManagement.enabled false || source target || ! source || ! target ) { return outputNode; // ① 恒等直接透传 } if ( ColorManagement.getTransfer( source ) SRGBTransfer ) { outputNode vec4( sRGBTransferEOTF( outputNode.rgb ), outputNode.a ); // ② EOTF } if ( ColorManagement.getPrimaries( source ) ! ColorManagement.getPrimaries( target ) ) { outputNode vec4( mat3( ColorManagement._getMatrix( new Matrix3(), source, target ) ).mul( outputNode.rgb ), outputNode.a ); // ③ 色域矩阵转换 } if ( ColorManagement.getTransfer( target ) SRGBTransfer ) { outputNode vec4( sRGBTransferOETF( outputNode.rgb ), outputNode.a ); // ④ OETF } return outputNode; }按执行顺序管线包含四类步骤① 恒等快捷路径。当ColorManagement.enabled false关闭了全局色彩管理、source target或任一侧未指定时直接把colorNode原样返回不生成任何转换代码。单元测试converting between the same color space is the identity专门验证了这一点。② 源转移曲线解码EOTF。若源色彩空间的转移函数是 sRGBSRGBTransfer常量来自 src/constants.js先对 RGB 分量应用sRGBTransferEOTF把 sRGB 编码值解码为线性光。该函数定义在 src/nodes/display/ColorSpaceFunctions.jsexport const sRGBTransferEOTF Fn( ( [ color ] ) { const a color.mul( 0.9478672986 ).add( 0.0521327014 ).pow( 2.4 ); const b color.mul( 0.0773993808 ); const factor color.lessThanEqual( 0.04045 ); const rgbResult mix( a, b, factor ); return rgbResult; } );即标准 sRGB EOTF分段函数在0.04045阈值处切换线性段12.92x与幂律段(x0.055)/1.055的 2.4 次方这里以等价的乘法/加法常量形式表达。③ 色域Primaries矩阵转换。仅当源与目标的色彩基色primaries不同时才执行通过ColorManagement._getMatrix()求出 3×3 转换矩阵如 Rec.709 → Rec.2020再以mat3(...).mul( outputNode.rgb )应用到 RGB 分量。④ 目标转移曲线编码OETF。若目标色彩空间使用 sRGB 转移函数最后应用sRGBTransferOETF把线性光编码回 sRGB 值const a color.pow( 0.41666 ).mul( 1.055 ).sub( 0.055 ); // (x)^(1/2.4)*1.055 - 0.055 const b color.mul( 12.92 ); // 12.92x const factor color.lessThanEqual( 0.0031308 );两个步骤的共同点始终用vec4( fn( outputNode.rgb ), outputNode.a )重新打包alpha 不参与转换。值得注意的是对于最常见的srgb ↔ srgb-linear转换对两者色域相同都是 Rec.709因此矩阵步骤被跳过整个转换退化为纯 EOTF/OETF——单元测试的注释中也明确指出了这一简化。5. TSL 工具函数与方法链ColorSpaceNode在文件末尾src/nodes/display/ColorSpaceNode.js#L129-L164配套导出了三个 TSL 便捷函数均会先把入参经nodeObject()包装为节点// 当前工作色彩空间 → 指定色彩空间 export const workingToColorSpace ( node, targetColorSpace ) new ColorSpaceNode( nodeObject( node ), WORKING_COLOR_SPACE, targetColorSpace ); // 指定色彩空间 → 当前工作色彩空间 export const colorSpaceToWorking ( node, sourceColorSpace ) new ColorSpaceNode( nodeObject( node ), sourceColorSpace, WORKING_COLOR_SPACE ); // 任意色彩空间 → 任意色彩空间 export const convertColorSpace ( node, sourceColorSpace, targetColorSpace ) new ColorSpaceNode( nodeObject( node ), sourceColorSpace, targetColorSpace ); addMethodChaining( workingToColorSpace, workingToColorSpace ); addMethodChaining( colorSpaceToWorking, colorSpaceToWorking );使用示例结合three导出的色彩空间常量SRGBColorSpace/LinearSRGBColorSpaceimport { vec4, workingToColorSpace, colorSpaceToWorking, convertColorSpace } from three/tsl; import { SRGBColorSpace, LinearSRGBColorSpace } from three; const srgbColor vec4( 0.5, 0.25, 0.75, 1 ); // 解码sRGB → 工作色彩空间默认 linear-sRGB const linear colorSpaceToWorking( srgbColor, SRGBColorSpace ); // 编码工作色彩空间 → sRGB const backToSrgb workingToColorSpace( linear, SRGBColorSpace ); // 任意 → 任意 const converted convertColorSpace( linear, LinearSRGBColorSpace, SRGBColorSpace );由于addMethodChaining注册了方法链任何 TSL 节点都可以直接以方法形式调用node.workingToColorSpace( srgb )、node.colorSpaceToWorking( srgb )。这些函数同时通过 src/nodes/TSL.jsexport * from ./display/ColorSpaceNode.js与 src/Three.TSL.js 导出可从three/tsl入口获取。6. 库内实际调用点谁在自动使用它从源码结构看ColorSpaceNode不只是给开发者手动调用的工具也是 three.js 节点管线内部自动完成色彩管理的关键组件渲染输出的最终编码。src/nodes/display/RenderOutputNode.js 在节点材质输出阶段执行outputNode.workingToColorSpace( outputColorSpace )第 132 行将工作色彩空间的结果转换为渲染目标/屏幕所需的目标色彩空间。这是节点管线中“工作空间 → 输出空间”的最后一环。纹理采样时的自动解码。src/nodes/accessors/TextureNode.js 在生成采样表达式后对非线性色彩的纹理调用colorSpaceToWorking( expression( snippet, nodeType ), texture.colorSpace ).setup( builder ).build( builder, nodeType )。也就是说当你在 TSL 里采样一张colorSpace: SRGBColorSpace的纹理时采样值会被自动解码回工作色彩空间与 CPU 端Color类的行为保持一致src/math/Color.js 内部同样调用ColorManagement.colorSpaceToWorking/workingToColorSpace。法线贴图解包。src/materials/nodes/MeshNormalNodeMaterial.js 中法线被编码进 sRGB 纹理通道后再通过colorSpaceToWorking( vec4( packNormalToRGB( normalView ), opacityNode ), SRGBColorSpace )解码回线性空间以避免 sRGB 编码/解码带来的精度损失。这三处调用共同说明在 TSL/节点管线中色彩空间转换被下沉到了 GPU 上按节点图精确控制的位置而ColorSpaceNode正是承担该职责的唯一节点类型。7. 单元测试验证GPU 层面的行为由 test/unit/addons/tsl/TSLColorSpaceConversion.tests.js 覆盖测试通过gpuTest将 TSL 表达式编译到 GPU 上采样并比对像素值覆盖了文档语义的关键分支colorSpaceToWorking(c, SRGBColorSpace)的结果与vec4(sRGBTransferEOTF(c.rgb), c.a)在1e-5容差内一致workingToColorSpace(c, SRGBColorSpace)的结果与vec4(sRGBTransferOETF(c.rgb), c.a)一致convertColorSpace(c, SRGBColorSpace, LinearSRGBColorSpace)与colorSpaceToWorking(c, SRGBColorSpace)等价因为默认工作色彩空间就是LinearSRGBColorSpace;workingToColorSpace( colorSpaceToWorking(c, SRGBColorSpace ), SRGBColorSpace )往返转换可恢复原始颜色1e-4容差;源与目标相同时convertColorSpace(c, X, X)为恒等对应setup()的提前返回分支。测试文件头部的注释还解释了测试策略srgb与srgb-linear共享 Rec.709 色域setup()会跳过色域矩阵路径因此该转换对退化为纯 EOTF/OETF预期值可直接由这两个已独立覆盖的转移函数推导。8. 小结与使用要点API 面new ColorSpaceNode( colorNode, source, target )属性.colorNode/.source/.target方法.resolveColorSpace( builder, colorSpace )继承链EventDispatcher → Node → TempNode → ColorSpaceNode节点类型为vec4。转换语义setup()按 “源 sRGB EOTF 解码 → 色域矩阵仅色域不同时→ 目标 sRGB OETF 编码” 的固定顺序展开子图ColorManagement.enabled false或源目标相同时直接透传。占位符色彩空间传入WorkingColorSpace/OutputColorSpace会在构建时解析为ColorManagement.workingColorSpace与builder.context.outputColorSpace || builder.renderer.outputColorSpace的实际值。推荐入口日常使用优先workingToColorSpace/colorSpaceToWorking/convertColorSpace三个 TSL 函数或节点上的同名链式方法它们由 src/nodes/display/ColorSpaceNode.js 定义并经three/tsl导出。适用前提以上行为基于当前仓库版本的 TSL 节点管线WebGL/WebGPU 节点渲染路径依赖ColorManagement的启用状态若关闭全局色彩管理该节点会退化为恒等透传。【免费下载链接】three.jsJavaScript 3D Library.项目地址: https://gitcode.com/GitHub_Trending/th/three.js创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价