首页
/ three.js RGBShiftNode 详解:基于 TSL 的 RGB 通道分离后处理节点

three.js RGBShiftNode 详解:基于 TSL 的 RGB 通道分离后处理节点

2026-09-07 13:51:08作者:滑思眉Philip

RGBShiftNode 是 three.js 中基于 TSL(Three Shading Language)实现的后处理节点,用于将画面中的红色、绿色、蓝色通道分离并沿指定方向错位偏移,从而产生经典的"RGB 分裂 / 色差"(Chromatic Aberration / Glitch)视觉效果。该节点常被用于梦境模糊、故障艺术、能量冲击等氛围营造场景,读者阅读本文后将掌握其构造函数与属性语义、内部采样与偏移算法、在 WebGPURenderer 的 RenderPipeline 中的接入方式,以及它与经典 RGBShiftShader 的对应关系。

概览:什么是 RGB Shift 后处理

RGB 分裂是一种常见的图像后处理特效:相机画面中每个像素的颜色由 R、G、B 三个分量构成,如果让红、绿、蓝三个通道的采样来源沿不同方向错开,彩色边缘就会出现残影,形成类似未对齐的印刷品或视觉故障的效果。

在 three.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 转换为纹理节点后,再以传入的 amountangle 构造 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 → RGBShiftNodesrc/nodes/core/Node.jssrc/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 颜色。
  • amountangle 并非普通数字,而是通过 uniform( value ) 包装为 UniformNode<float>。这意味着它们可以在运行时被赋值更新(动画化),例如随时间摆动 amount 实现呼吸式的故障闪烁,或者改变 angle 让分裂方向旋转。

属性

.textureNode : TextureNode

特效的输入纹理节点。.setup() 中会优先使用 textureNode.uvNode 作为采样 UV,若未自定义则回退到全屏 UV(uv())。

.amount : UniformNode.

RGB 通道的偏移量。默认 0.005,方向沿 angle 所指示的单位方向乘以此值,因此实际像素偏移为 amount(以归一化 UV 为单位)。示例中常见取值在 0.0010.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();

}

逐行理解:

  1. 计算方向向量 offset = (cos(angle), sin(angle)) * amount
  2. 红色通道取自 UV 偏移 +offset 处的采样 cr
  3. 绿色与 Alpha 通道取自原始 UV 处的采样 cga(保持画面主体与透明度不变)。
  4. 蓝色通道取自 UV 偏移 -offset 处的采样 cb,与红色方向相反。
  5. 重组为 vec4( cr.r, cga.g, cb.b, cga.a ) 输出。

即红蓝通道分别向相反方向偏移,绿色通道保持原位——这与经典 WebGL 版本的 RGBShiftShadertexture2D(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;

该示例同时展示了 amountangleUniformNode 的运行时更新方式:直接通过 .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 同样包含 tDiffuseamount = 0.005angle = 0.0,片元着色器与节点版算法逐字对应)。选择依据:

两个版本的默认值与算法语义完全一致,因此 amountangle 的调参经验可以在两套管线间直接迁移。

参数调优建议

  • amount(偏移量):默认 0.005 已较为细腻;低于 0.001 几乎不可见,高于 0.02 会出现明显的彩色重影。示例演示多采用 0.0010.005
  • angle(角度)0 表示水平方向分裂(左右色边);Math.PI / 4Math.PI / 2 可切换为斜向与垂直分裂;动态旋转 angle 会产生扫掠般的色差流动。
  • 输入:可直接接 pass( scene, camera ),也可像官方示例那样作为 DotScreen、Bloom 等效果之后的串联节点;需要画面中央区域更纯净时,可考虑自行以遮罩节点对 amount 进行空间调制,例如将 amount 与基于 UV 距离中心远近的衰减值相乘。

源码与测试速查

综上,RGBShiftNode 将经典的 RGB 分裂算法以纯 TSL 节点形式纳入新一代后处理管线:通过三次不同 UV 的采样分别取红/绿/蓝分量重组,配合可动画化的 amountangle uniform,即可在 WebGPURenderer 下以极简的声明式代码实现高品质的色差特效,并灵活地与其他 TSL 后处理节点自由组合。

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