three.js PostProcessing 类解析:从 r183 弃用到 RenderPipeline 迁移指南
导读
本文以 three.js 官方文档 PostProcessing.html.md 为骨架,系统讲解 PostProcessing 后处理管理模块的 API 形态、其自 r183 起正式弃用并被 RenderPipeline 取代的完整背景,以及将既有后处理代码平滑迁移到 RenderPipeline 的实操路径。读完本文你将掌握:PostProcessing/RenderPipeline 的构造参数与属性语义、基于 TSL 节点构建后处理链(pass、renderOutput、FXAA 等)的标准写法、outputColorTransform 关闭的适用场景,以及源码级的执行原理。
说明:官方文档给出的 PostProcessing.html.md 实质是一条 API 弃用说明(含构造函数签名),正文将以其为核心骨架,同时依据仓库内 RenderPipeline.html.md、PostProcessing.js 源码 与 RenderPipeline.js 源码 展开纵深剖析。
一、PostProcessing 是什么
1.1 类定位
PostProcessing 是 three.js 中负责渲染管线与后处理效果链管理的模块。其核心 API 文档(PostProcessing.html.md)给出的构造函数为:
new PostProcessing( renderer : Renderer, outputNode : Node.<vec4> )
- renderer:对渲染器(renderer)的引用;
- outputNode:可选输出节点,类型为
Node<vec4>。
它承担三类职责:
- 持有渲染器引用与最终输出节点;
- 以全屏四边形(QuadMesh)方式把一整条节点链渲染到屏幕;
- 统一编排渲染管线在每帧动画循环中的执行入口。
1.2 它是如何工作的
从源码看,PostProcessing 本身几乎不含业务逻辑——它只是 RenderPipeline 的向后兼容包装类。见 PostProcessing.js:
class PostProcessing extends RenderPipeline {
constructor( renderer, outputNode ) {
warnOnce( 'PostProcessing: "PostProcessing" has been renamed to "RenderPipeline". Please update your code to use "THREE.RenderPipeline" instead.' ); // @deprecated, r183
super( renderer, outputNode );
}
}
真实的工作负载全部继承自 RenderPipeline。这也是为什么文档会在类简介里用加粗醒目标注:
Deprecated: since r183. Use RenderPipeline instead. PostProcessing has been renamed to RenderPipeline. This class is a wrapper for backward compatibility and will be removed in a future version.
即:自 r183 起弃用,新代码应使用 RenderPipeline;该类仅作兼容包装,未来版本将被移除。构造函数体中的 warnOnce(...) 调用意味着只要代码里仍 new THREE.PostProcessing(...),控制台就会收到一次明确的迁移提示,便于开发者定位遗留用法。
从源码结构看,包装类同时承接了弃用警告的打印与转发构造参数两项工作,可以在不断言新 API 功能的前提下尽量平滑地运行旧程序。
二、替代者 RenderPipeline 的完整能力
从官方文档 RenderPipeline.html.md 可知,RenderPipeline 本身的能力如下:
This module is responsible to manage the rendering pipeline setups in apps. You usually create a single instance of this class and use it to define the output of your render pipeline and post processing effect chain.
Note: This module can only be used with
WebGPURenderer.
- 它用于管理应用中的渲染管线设置;
- 通常只需创建一个实例,用它定义渲染管线输出与后处理效果链;
- 只适用于
WebGPURenderer(在 TSL 场景下对应three/webgpu构建入口)。传统 WebGLRenderer 下的后处理不在此 API 范畴。
2.1 构造函数
new RenderPipeline( renderer : Renderer, outputNode : Node.<vec4> )
参数与 PostProcessing 完全一致,其中 renderer 为必传,outputNode 可选。源码中 outputNode 的默认值是:
constructor( renderer, outputNode = vec4( 0, 0, 1, 1 ) )
即未显式给出输出节点时,渲染管线会输出一个不透明的白色全屏颜色(RGBA = (0,0,1,1))。这一细节意味着只传 renderer 也可以构造实例,但直到为 outputNode 赋予真正的效果链之前,画面不会反映你的场景。
2.2 属性一览
官方文档列出了四个公开属性,逐一说明其语义、默认值与源码依据(RenderPipeline.js):
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
.context |
Object(只读) |
null(内部 _contextData) |
渲染管线栈的当前上下文对象 |
.needsUpdate |
Node<vec4>(实为布尔) |
true |
输出节点改变后必须置为 true,触发内部上下文重建 |
.outputColorTransform |
boolean |
true |
是否启用默认输出色调映射与色彩空间变换;设为 false 后须自行用 renderOutput() 控制变换顺序 |
.outputNode |
Node<vec4> |
vec4(0,0,1,1) |
定义渲染管线最终输出的节点,通常是整条效果链的最后一环 |
.renderer |
Renderer |
— | 对渲染器的引用(只读语义) |
提示:文档中将
.needsUpdate类型写作Node.<vec4>,实际源码里是布尔标记(初始true)。渲染器每帧在_update()中检查该标记,必要时调用_updateContext()重建材质与上下文后再复位为false。
三、基于 TSL 的标准后处理写法
从源码与示例可以看到,RenderPipeline 与 TSL(Three Shading Language)节点体系深度绑定:outputNode 本身就是一个 Node<vec4>,而非传统“EffectComposer + 多 Pass 对象”的处理器列表。
3.1 最小示例(来自官方文档)
RenderPipeline.html.md 给出的最小用法为:
const renderPipeline = new RenderPipeline( renderer );
const scenePass = pass( scene, camera );
renderPipeline.outputNode = scenePass;
pass( scene, camera )来自three/tsl,它把一个场景渲染通道封装为可连接的 TSL 节点;- 直接赋给
outputNode后,动画循环调用renderPipeline.render()而非renderer.render()。
3.2 多效果链实战:webgpu_postprocessing.html
examples/webgpu_postprocessing.html 展示了把点阵化(dotScreen)与 RGB 错位(rgbShift)串成链的写法:
renderPipeline = new THREE.RenderPipeline( renderer );
const scenePass = pass( scene, camera );
const scenePassColor = scenePass.getTextureNode().toInspector( 'Scene Color' );
const dotScreenPass = dotScreen( scenePassColor );
dotScreenPass.scale.value = 0.3;
const rgbShiftPass = rgbShift( dotScreenPass );
rgbShiftPass.amount.value = 0.001;
renderPipeline.outputNode = rgbShiftPass;
function animate() {
object.rotation.x += 0.005;
object.rotation.y += 0.01;
renderPipeline.render();
}
要点:
- 每个效果函数(
dotScreen、rgbShift)返回节点对象,可直接“喂给”下一个效果; - 效果参数以
.value暴露为可写句柄(如dotScreenPass.scale.value),便于在动画循环或 UI 面板里动态调整; toInspector()与Inspector配合可把中间结果可视化(示例中渲染器renderer.inspector = new Inspector());- 动画循环中调用的是
renderPipeline.render(),不是renderer.render()。
3.3 outputColorTransform=false 与 FXAA:颜色空间时序问题
outputColorTransform 是迁移者最容易忽略、也最容易出错的开关。RenderPipeline.html.md 对此有专门说明:
Whether the default output tone mapping and color space transformation should be enabled or not. This is enabled by default but it must be disabled for effects that expect to be executed after tone mapping and color space conversion. A typical example is FXAA which requires sRGB input. When set to
false, the app must control the output transformation withRenderOutputNode.
含义拆解:
- three.js 默认把**色调映射(tone mapping)与色彩空间转换(如线性的工作色彩空间 → sRGB)**放在管线末端一次性完成,这是
.outputColorTransform = true的行为; - 但 FXAA 类抗锯齿需要 sRGB 输入——它必须运行在色调映射与色彩空间转换之后。若让变换先做、FXAA 后做(顺序颠倒),画面会出现错误;
- 因此需要关闭默认自动变换(
false),再用renderOutput( ... )在节点链中手动安排转换时机。
官方文档给出了对应的 renderOutput 用法:
const outputPass = renderOutput( scenePass );
examples/webgpu_postprocessing_fxaa.html 是上述理论的完整落地,其导入结构为:
import * as THREE from 'three/webgpu';
import { pass, renderOutput } from 'three/tsl';
import { fxaa } from 'three/addons/tsl/display/FXAANode.js';
核心片段(节选):
renderPipeline = new THREE.RenderPipeline( renderer );
// 关闭默认输出变换,改用 renderOutput() 手动控制顺序
renderPipeline.outputColorTransform = false;
const scenePass = pass( scene, camera ).toInspector( 'Color' );
const outputPass = renderOutput( scenePass );
// FXAA 必须在 sRGB 下计算(即在色调映射与色彩空间转换之后)
const fxaaPass = fxaa( outputPass );
renderPipeline.outputNode = fxaaPass;
链式结构:scene → pass → renderOutput → fxaa → outputNode。即:场景先走 pass,紧接着做颜色变换(tone mapping + sRGB 转换),再交给 fxaa 进行 sRGB 输入下的抗锯齿,最后输出。
源码层面,outputColorTransform 的分支逻辑在 RenderPipeline.js 的 _updateContext() 中:
let outputNode = this.outputNode;
if ( this.outputColorTransform === true ) {
outputNode = renderOutput( outputNode, toneMapping, outputColorSpace );
} else {
contextData.toneMapping = toneMapping;
contextData.outputColorSpace = outputColorSpace;
}
可见:
true(默认):管线自动用renderOutput( outputNode, toneMapping, outputColorSpace )包裹输出节点,把渲染器当前的renderer.toneMapping与renderer.outputColorSpace应用到末端;false:不自动包裹,改由用户在链中显式调用renderOutput(),代码负责变换的先后顺序。
同时 render() 内部的执行序也印证了这一设计(相关实现见源码 RenderPipeline.js):
_update():若渲染器的toneMapping/outputColorSpace与内部缓存不一致或needsUpdate === true,重建上下文;- 触发
onBeforePipelineCallbacks; - 临时把渲染器置为
NoToneMapping+workingColorSpace,避免 renderer 层面的默认变换与管线内变换叠加冲突; - 用内部
QuadMesh渲染整条节点链(期间临时关闭xr.enabled,XR 会话不干扰全屏后处理); - 恢复渲染器原色调映射、色彩空间,触发
onAfterPipelineCallbacks。
因此在设置 outputColorTransform = false 且链内显式使用 renderOutput() 时,需要理解:整个“颜色变换”过程已完全由节点链掌控,链路中一旦遗漏转换节点,输出将停留在工作色彩空间(线性)而缺少 sRGB 转换。
四、RenderPipeline 的方法与内部状态机
4.1 方法清单
| 方法 | 签名 | 说明 |
|---|---|---|
.render() |
render() : void |
动画循环唯一渲染入口;必须用它替代 renderer.render() |
.renderAsync() |
renderAsync() : Promise |
异步版本;已弃用,源码中会打印警告 |
.dispose() |
dispose() : void |
释放内部资源(释放 _quadMesh 的材质) |
4.2 render() vs renderAsync()
官方文档 RenderPipeline.html.md 对二者的注释完全一致:
When
RenderPipelineis used to apply rendering pipeline and post processing effects, the application must use this version ofrender()/renderAsync()inside its animation loop (not the one from the renderer).
即:一旦启用 RenderPipeline,动画循环里渲染画面的是它的 render(),renderer 的同名方法不再负责出画。
renderAsync() 已在文档中标为 Deprecated(返回一个在渲染完成后 resolve 的 Promise)。其源码实现(见 RenderPipeline.js)会打印弃用警告并指出替代方案:
async renderAsync() {
warnOnce( 'RenderPipeline: "renderAsync()" has been deprecated. Use "render()" and "await renderer.init();" when creating the renderer.' ); // @deprecated r181
await this.renderer.init();
this.render();
}
因此 r181 之后建议的做法是:创建渲染器时先 await renderer.init(),动画循环内直接调用 renderPipeline.render(),无需再使用异步渲染。
4.3 内部状态机(_update / _updateContext)
从 RenderPipeline.js 源码可还原其更新流程:
- 构造函数初始化
_toneMapping = renderer.toneMapping、_outputColorSpace = renderer.outputColorSpace,并创建一块名为'Render Pipeline'的QuadMesh,材质是NodeMaterial(name = 'RenderPipeline'); _update()监听渲染器当前 tone mapping / outputColorSpace 变化,一旦变化即置needsUpdate = true,随后调用_updateContext()重建上下文并把needsUpdate复位;_updateContext()依据outputColorTransform分支决定是否自动包裹renderOutput(...),随后把结果写入_quadMesh.material.fragmentNode,并以context( contextData )作为材质的contextNode,同时置material.needsUpdate = true强制重编译。
正是这种“渲染管线状态 → 材质片段节点”的映射关系,让 outputNode 里的任意节点链都能在每帧被动态重算与重编译。
五、从 PostProcessing 到 RenderPipeline 的迁移清单
由于 PostProcessing 只是 RenderPipeline 的空壳子类,迁移几乎全是机械替换。依据文档与源码,可按下述步骤操作:
-
类名替换 将
new THREE.PostProcessing( renderer, outputNode )改为new THREE.RenderPipeline( renderer, outputNode )。- 旧实例在 src/Three.WebGPU.js、src/Three.WebGPU.Nodes.js 的导出仍以兼容形式存在;
- 替换后即可消除 PostProcessing.js 里
warnOnce(...)触发的控制台弃用警告。
-
渲染入口替换 把动画循环里的
postProcessing.render()改到renderPipeline.render()(语义未变)。 -
去除 renderAsync 依赖 若旧代码用了
renderAsync(),先检查渲染器创建处是否await renderer.init();是则直接改render(),否则在初始化流程中补上init()。 -
检查 outputNode 链 确认
outputNode依然是“最后一环”节点。新代码建议直接用 TSL 节点函数(pass、renderOutput、fxaa、bloom等)串联,避免把传统 EffectComposer 式多 Pass 对象塞给outputNode。 -
有意为之才关闭 outputColorTransform 只有 FXAA 这类需要“后颜色变换”输入的效果才置
false,并在链内显式调用renderOutput(...);否则保持默认true让系统自动完成色调映射与色彩空间转换。 -
旧代码的保留与清理计划
PostProcessing目前仍能运行,但属于过渡产物,官方文档明示将在未来版本移除。长期维护的项目应尽快迁移,并可在代码中 grepPostProcessing(构建产物为 build/three.webgpu.js、build/three.webgpu.nodes.js)定位残留引用。
六、源码与示例索引
核心实现
- src/renderers/common/PostProcessing.js:兼容包装类本体(extends RenderPipeline + 弃用警告);
- src/renderers/common/RenderPipeline.js:真正承载渲染管线状态的实现,含
_updateContext()状态机与render()执行序; - src/nodes/display/PassNode.js、src/nodes/display/RenderOutputNode.js:
pass()与renderOutput()的实现载体; - src/nodes/TSL.js:TSL 节点函数的统一出口。
官方文档
- docs/pages/PostProcessing.html.md:本文核心关联文档(API 弃用说明);
- docs/pages/RenderPipeline.html.md:替代类完整参考(构造、属性、方法、代码示例);
- docs/pages/PassNode.html.md、docs/pages/RenderOutputNode.html.md:链路中间节点的细节参考。
可直接运行的示例(均使用 WebGPURenderer + RenderPipeline)
- examples/webgpu_postprocessing.html:多效果链基础写法;
- examples/webgpu_postprocessing_fxaa.html:
outputColorTransform = false+renderOutput的 FXAA 时序示例; - examples/webgpu_postprocessing_bloom.html、examples/webgpu_postprocessing_dof.html、examples/webgpu_postprocessing_ssr.html:Bloom、景深、SSR 等高级链式后处理参考。
七、常见问题与注意事项
-
为什么
new PostProcessing时控制台出现警告? 这是 PostProcessing.js 中的warnOnce()有意为之,提醒 r183 起改名,属预期行为。 -
能否把 PostProcessing/RenderPipeline 用在 WebGLRenderer 上? 不能。官方文档明确 “This module can only be used with
WebGPURenderer”。需要 WebGL 后处理时,应使用examples/jsm/postprocessing/下的传统 EffectComposer 体系(与本文 TSL 体系相互独立)。 -
renderPipeline.render() 与 renderer.render() 能否混用? 文档要求使用 RenderPipeline 时在动画循环调用
renderPipeline.render(),否则整条节点链不会输出。RenderPipeline 内部会临时改写 renderer 的 tone mapping 与色彩空间设置并在渲染后恢复,这一行为本身是自洽的,混用反而会打乱管线。 -
outputColorTransform = false 后画面发灰/发暗怎么办? 说明链中缺少
renderOutput(...)。该模式下系统不再自动包裹颜色变换,需要在合适位置(通常在被 FXAA 等后颜色空间效果消费之前)显式插入renderOutput( ... ),形成“先转换、后处理”的时序。
版本与适用前提:本文所有 API 行为、弃用节点(r183 弃用 PostProcessing、r181 弃用 renderAsync)与源码片段,均依据当前仓库 src/renderers/common/RenderPipeline.js、src/renderers/common/PostProcessing.js 及官方文档核实。TSL 后处理仅在 three/webgpu(WebGPURenderer)下可用。
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 StartedRust0629
MiniCPM5-2BMiniCPM5-2B 是一款面向端侧、本地部署和资源受限场景的 2B 稠密 Transformer,能够达到同尺寸开源模型 SOTA 水平。Markdown00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
HivisionIDPhotos⚡️HivisionIDPhotos: a lightweight and efficient AI ID photos tools. 一个轻量级的AI证件照制作算法。Python07
DragonOSDragonOS is an operating system developed from scratch using Rust, with Linux compatibility. It is designed for **Serverless** scenarios. 使用Rust从0自研内核,具有Linux兼容性的操作系统,面向云计算Serverless场景而设计。Rust00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00