three.js RGBShiftNode 详解:基于 TSL 的 RGB 通道分离后处理节点
RGBShiftNode 是 three.js 中基于 TSL(Three Shading Language)实现的后处理节点,用于将画面中的红色、绿色、蓝色通道分离并沿指定方向错位偏移,从而产生经典的"RGB 分裂 / 色差"(Chromatic Aberration / Glitch)视觉效果。该节点常被用于梦境模糊、故障艺术、能量冲击等氛围营造场景,读者阅读本文后将掌握其构造函数与属性语义、内部采样与偏移算法、在 WebGPURenderer 的 RenderPipeline 中的接入方式,以及它与经典 RGBShiftShader 的对应关系。
概览:什么是 RGB Shift 后处理
RGB 分裂是一种常见的图像后处理特效:相机画面中每个像素的颜色由 R、G、B 三个分量构成,如果让红、绿、蓝三个通道的采样来源沿不同方向错开,彩色边缘就会出现残影,形成类似未对齐的印刷品或视觉故障的效果。
在 three.js 中该功能有两套实现路径:
- 经典 WebGL 路径:以 Shader 对象
RGBShiftShader配合EffectComposer使用,实现在 examples/jsm/shaders/RGBShiftShader.js。 - 新 TSL / WebGPU 路径:以节点
RGBShiftNode配合RenderPipeline使用,实现在 examples/jsm/tsl/display/RGBShiftNode.js。
本文以 docs/pages/RGBShiftNode.html.md 文档为核心,结合源码与官方示例展开讲解 TSL 节点版本。
导入方式:作为 Addon 显式引入
RGBShiftNode 属于 addon 模块,不会被自动打包进 three.js 核心,必须显式导入。官方约定通过 three/addons/ 别名引用:
import { rgbShift } from 'three/addons/tsl/display/RGBShiftNode.js';
这里导入的 rgbShift 是一个便捷的 TSL 函数,其内部实现为:
export const rgbShift = ( node, amount, angle ) => new RGBShiftNode( convertToTexture( node ), amount, angle );
可见 rgbShift 函数与类构造器等价:它把任意可输入节点(通常是 pass() 产生场景贴图或其他后处理节点的输出)通过 convertToTexture 转换为纹理节点后,再以传入的 amount 与 angle 构造 RGBShiftNode。
此外该类也支持直接实例化方式:
import { RGBShiftNode } from 'three/addons/tsl/display/RGBShiftNode.js';
import { pass } from 'three/tsl';
const scenePass = pass( scene, camera );
const node = new RGBShiftNode( scenePass, 0.005, 0 );
从源码可见类继承链为 EventDispatcher → Node → TempNode → RGBShiftNode(src/nodes/core/Node.js、src/nodes/core/TempNode.js 是链中关键基类),因此它具备临时节点"计算一次即被引用替换"的惰性求值特征,可安全地嵌入复杂的后处理节点图中。
构造函数
new RGBShiftNode( textureNode : TextureNode, amount : number, angle : number )
| 参数 | 类型 | 说明 | 默认值 |
|---|---|---|---|
textureNode |
TextureNode |
特效输入,表示待处理的贴图/场景画面 | 无(必填) |
amount |
number |
RGB 通道的偏移量,值越大色彩分离越明显 | 0.005 |
angle |
number |
定义通道偏移的朝向(弧度制) | 0 |
源码构造器核心逻辑如下:
constructor( textureNode, amount = 0.005, angle = 0 ) {
super( 'vec4' );
this.textureNode = textureNode;
this.amount = uniform( amount );
this.angle = uniform( angle );
}
需要注意两点实现细节:
- 构造器向父类声明了输出类型
'vec4',即该节点输出 RGBA 颜色。 amount与angle并非普通数字,而是通过uniform( value )包装为UniformNode<float>。这意味着它们可以在运行时被赋值更新(动画化),例如随时间摆动amount实现呼吸式的故障闪烁,或者改变angle让分裂方向旋转。
属性
.textureNode : TextureNode
特效的输入纹理节点。.setup() 中会优先使用 textureNode.uvNode 作为采样 UV,若未自定义则回退到全屏 UV(uv())。
.amount : UniformNode.
RGB 通道的偏移量。默认 0.005,方向沿 angle 所指示的单位方向乘以此值,因此实际像素偏移为 amount(以归一化 UV 为单位)。示例中常见取值在 0.001~0.01 量级。
.angle : UniformNode.
通道偏移方向角(弧度)。源码中通过 vec2( cos( this.angle ), sin( this.angle ) ).mul( this.amount ) 计算方向偏移向量,因此 angle = 0 时通道沿 U 轴水平错开,angle = Math.PI / 2 时沿 V 轴垂直错开。
setup 方法:通道采样与重组算法
setup( builder : NodeBuilder ) : ShaderCallNodeInternal 用于构建该效果的 TSL 代码。其核心算法(即 RGB 分裂的本质)如下:
setup( /* builder */ ) {
const { textureNode } = this;
const uvNode = textureNode.uvNode || uv();
const sampleTexture = ( uv ) => textureNode.sample( uv );
const rgbShift = Fn( () => {
const offset = vec2( cos( this.angle ), sin( this.angle ) ).mul( this.amount );
const cr = sampleTexture( uvNode.add( offset ) );
const cga = sampleTexture( uvNode );
const cb = sampleTexture( uvNode.sub( offset ) );
return vec4( cr.r, cga.g, cb.b, cga.a );
} );
return rgbShift();
}
逐行理解:
- 计算方向向量
offset = (cos(angle), sin(angle)) * amount。 - 红色通道取自 UV 偏移
+offset处的采样cr。 - 绿色与 Alpha 通道取自原始 UV 处的采样
cga(保持画面主体与透明度不变)。 - 蓝色通道取自 UV 偏移
-offset处的采样cb,与红色方向相反。 - 重组为
vec4( cr.r, cga.g, cb.b, cga.a )输出。
即红蓝通道分别向相反方向偏移,绿色通道保持原位——这与经典 WebGL 版本的 RGBShiftShader 中 texture2D(tDiffuse, vUv + offset)、texture2D(tDiffuse, vUv)、texture2D(tDiffuse, vUv - offset) 的三次采样逻辑完全一致,只是前者以 TSL 节点函数表达。三次纹理采样意味着算法会针对每个像素额外读取两次邻域纹理,对屏幕分辨率画面而言开销可控。
需要说明的是,setup() 中声明的 Fn 函数体通过 fn 语义被惰性编译并复用,可与其他后处理节点(如 bloom、dotScreen)共同组合进同一个渲染管线中。
在 WebGPURenderer 后处理管线中使用
新式后处理基于 RenderPipeline(替代旧的 EffectComposer),其官方用法记载于 manual/pages/webgpu-postprocessing.html。基础整合步骤如下:
import * as THREE from 'three/webgpu';
import { pass } from 'three/tsl';
import { rgbShift } from 'three/addons/tsl/display/RGBShiftNode.js';
// 初始化
const renderPipeline = new THREE.RenderPipeline( renderer );
// 场景被渲染为一个“场景通道”贴图
const scenePass = pass( scene, camera );
// 将 RGB 分裂节点挂到场景输出上
const rgbShiftPass = rgbShift( scenePass );
rgbShiftPass.amount.value = 0.005;
rgbShiftPass.angle.value = Math.PI / 4;
renderPipeline.outputNode = rgbShiftPass;
// 动画循环中不再直接 renderer.render,而是:
renderPipeline.render();
链接到后续效果
由于 TSL 节点是"可组合的",rgbShift 的输出可以继续作为下一个效果节点的输入。例如官方 WebGPU 后处理演示 examples/webgpu_postprocessing.html 中,将点阵化效果与 RGB 分裂串联:
import { dotScreen } from 'three/addons/tsl/display/DotScreenNode.js';
import { rgbShift } from 'three/addons/tsl/display/RGBShiftNode.js';
const dotScreenPass = dotScreen( scenePassColor );
dotScreenPass.scale.value = 0.3;
const rgbShiftPass = rgbShift( dotScreenPass );
rgbShiftPass.amount.value = 0.001;
renderPipeline.outputNode = rgbShiftPass;
该示例同时展示了 amount、angle 为 UniformNode 的运行时更新方式:直接通过 .amount.value、.angle.value 赋值即可让效果在动画循环中变化,无需重建节点。
动态驱动效果
利用 .amount 与 .angle 的 uniform 性质,可以驱动出"故障风"动态效果:
// 在 requestAnimationFrame 中
rgbShiftPass.amount.value = 0.002 + Math.abs( Math.sin( clock.getElapsedTime() * 3 ) ) * 0.006;
rgbShiftPass.angle.value = clock.getElapsedTime() * 0.5;
与经典 RGBShiftShader 的关系与选择
对照 docs/pages/module-RGBShiftShader.html.md 可知,RGBShiftShader 是供 WebGL 经典管线使用的 Shader 对象(uniforms 同样包含 tDiffuse、amount = 0.005、angle = 0.0,片元着色器与节点版算法逐字对应)。选择依据:
- WebGLRenderer + EffectComposer:使用
RGBShiftShader(examples/jsm/shaders/RGBShiftShader.js)。 - WebGPURenderer + RenderPipeline:使用
RGBShiftNode/rgbShift()(examples/jsm/tsl/display/RGBShiftNode.js),这也是面向 WebGPU 新式后处理体系推荐的方向。
两个版本的默认值与算法语义完全一致,因此 amount 与 angle 的调参经验可以在两套管线间直接迁移。
参数调优建议
- amount(偏移量):默认
0.005已较为细腻;低于0.001几乎不可见,高于0.02会出现明显的彩色重影。示例演示多采用0.001~0.005。 - angle(角度):
0表示水平方向分裂(左右色边);Math.PI / 4、Math.PI / 2可切换为斜向与垂直分裂;动态旋转angle会产生扫掠般的色差流动。 - 输入:可直接接
pass( scene, camera ),也可像官方示例那样作为 DotScreen、Bloom 等效果之后的串联节点;需要画面中央区域更纯净时,可考虑自行以遮罩节点对amount进行空间调制,例如将amount与基于 UV 距离中心远近的衰减值相乘。
源码与测试速查
- 节点实现:examples/jsm/tsl/display/RGBShiftNode.js(
RGBShiftNode类与rgbShift便捷函数) - 经典 Shader 对照:examples/jsm/shaders/RGBShiftShader.js
- TSL 后处理函数总表:docs/TSL.md 中
rgbShift( node, amount = 0.005, angle = 0 )条目 - 官方接入指南:manual/pages/webgpu-postprocessing.html
- 可运行示例:examples/webgpu_postprocessing.html
- 基类说明:RGBShiftNode 类文档
综上,RGBShiftNode 将经典的 RGB 分裂算法以纯 TSL 节点形式纳入新一代后处理管线:通过三次不同 UV 的采样分别取红/绿/蓝分量重组,配合可动画化的 amount 与 angle uniform,即可在 WebGPURenderer 下以极简的声明式代码实现高品质的色差特效,并灵活地与其他 TSL 后处理节点自由组合。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0627
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00