后处理效果
PostProcess API 在每个相机渲染完成后应用全屏着色器效果。效果通过 PostProcessStack 对象管理,可以绑定到不同的相机,让每个相机拥有不同的效果。
快速开始
import { PostProcess } from 'esengine';
// 创建效果栈const fx = PostProcess.createStack();fx.addPass('vignette', PostProcess.createVignette());fx.setUniform('vignette', 'u_intensity', 0.6);fx.setUniform('vignette', 'u_softness', 0.5);
// 绑定到相机实体PostProcess.bind(cameraEntity, fx);PostProcessStack
PostProcessStack 是可复用的效果链。创建一个 Stack,添加 pass,然后绑定到一个或多个相机:
const fx = PostProcess.createStack();
// 添加效果(按顺序执行)fx.addPass('blur', PostProcess.createBlur());fx.addPass('vignette', PostProcess.createVignette());
// 配置 uniformfx.setUniform('blur', 'u_intensity', 3.0);fx.setUniform('vignette', 'u_intensity', 0.8);fx.setUniform('vignette', 'u_softness', 0.3);
// 绑定到相机 — 效果立即生效PostProcess.bind(cameraEntity, fx);Stack 方法
| 方法 | 说明 |
|---|---|
addPass(name, shader) | 追加一个命名 pass |
removePass(name) | 按名称移除 pass |
clearPasses() | 移除所有 pass |
setEnabled(name, enabled) | 启用或禁用 pass |
setUniform(pass, name, value) | 设置 float uniform |
setUniformVec4(pass, name, value) | 设置 vec4 uniform |
setAllPassesEnabled(enabled) | 一次性启用或禁用所有 pass |
passCount | pass 总数 |
enabledPassCount | 已启用的 pass 数 |
destroy() | 销毁 Stack(自动解绑所有相机) |
相机绑定
// 绑定 Stack 到相机PostProcess.bind(cameraEntity, fx);
// 查询当前 Stackconst stack = PostProcess.getStack(cameraEntity); // 无绑定时返回 null
// 解绑(相机不再应用效果)PostProcess.unbind(cameraEntity);内置效果
| 方法 | 效果 | Uniform |
|---|---|---|
createBloomExtract() | 辉光:提取亮像素 | u_threshold (0–1) — 亮度截断 |
createBloomKawase(iteration) | 辉光:Kawase 模糊 pass | u_radius — 模糊扩散, u_resolution(自动) |
createBloomComposite() | 辉光:合成回原图 | u_intensity — 辉光强度, u_sceneTexture(自动) |
createBlur() | 高斯模糊 | u_intensity — 模糊扩散 |
createVignette() | 暗角 | u_intensity (0–1) — 效果强度, u_softness (0–1) — 衰减 |
createGrayscale() | 去饱和 | u_intensity (0–1) — 混合比例 |
createChromaticAberration() | RGB 通道偏移 | u_intensity — 偏移量 |
辉光 (Bloom)
Bloom 使用多 pass 管线:提取亮像素、多次 Kawase 模糊迭代、最后合成回原图。比单 pass 方案质量更高:
const fx = PostProcess.createStack();
// 1. 提取亮像素fx.addPass('bloom_extract', PostProcess.createBloomExtract());fx.setUniform('bloom_extract', 'u_threshold', 0.4);
// 2. Kawase 模糊(多次迭代产生更宽、更平滑的模糊)for (let i = 0; i < 5; i++) { fx.addPass(`bloom_kawase_${i}`, PostProcess.createBloomKawase(i)); fx.setUniform(`bloom_kawase_${i}`, 'u_radius', 1.0);}
// 3. 将辉光合成回原始场景fx.addPass('bloom_composite', PostProcess.createBloomComposite());fx.setUniform('bloom_composite', 'u_intensity', 1.5);
PostProcess.bind(cameraEntity, fx);模糊 (Blur)
9 次采样高斯模糊。u_intensity 越大模糊越强:
const fx = PostProcess.createStack();fx.addPass('blur', PostProcess.createBlur());fx.setUniform('blur', 'u_intensity', 3.0);PostProcess.bind(cameraEntity, fx);暗角 (Vignette)
使屏幕边缘变暗:
const fx = PostProcess.createStack();fx.addPass('vignette', PostProcess.createVignette());fx.setUniform('vignette', 'u_intensity', 0.6);fx.setUniform('vignette', 'u_softness', 0.5);PostProcess.bind(cameraEntity, fx);灰度 (Grayscale)
在原色和灰度之间混合:
const fx = PostProcess.createStack();fx.addPass('grayscale', PostProcess.createGrayscale());fx.setUniform('grayscale', 'u_intensity', 1.0); // 完全灰度PostProcess.bind(cameraEntity, fx);色差 (Chromatic Aberration)
偏移 R 和 B 通道以产生镜头畸变效果:
const fx = PostProcess.createStack();fx.addPass('chromatic', PostProcess.createChromaticAberration());fx.setUniform('chromatic', 'u_intensity', 2.0);PostProcess.bind(cameraEntity, fx);屏幕级后处理
屏幕级效果在所有相机渲染完成后应用于最终合成画面,适用于全局渐变、全屏暗角、电影黑边等效果。
import { PostProcess } from 'esengine';
const screenFx = PostProcess.createStack();screenFx.addPass('vignette', PostProcess.createVignette());screenFx.setUniform('vignette', 'u_intensity', 0.8);
// 对最终合成画面应用效果(在所有相机之后)PostProcess.setScreenStack(screenFx);
// 移除屏幕效果PostProcess.setScreenStack(null);后处理 Volume
Volume 允许空间后处理 — 当相机进入指定区域时激活效果。多个 Volume 按优先级混合,类似 Unity 的 Post Process Volume。
Volume 属性
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
isGlobal | boolean | true | 为 true 时忽略形状,全局生效 |
shape | 'box' | 'sphere' | 'box' | 触发区域形状 |
size | { x, y } | { x: 5, y: 5 } | Box 半尺寸 / Sphere 半径 |
priority | number | 0 | 高优先级覆盖低优先级 |
weight | number | 1 | 混合权重(0–1) |
blendDistance | number | 0 | 边缘渐入距离(世界单位) |
Volume 混合
当多个 Volume 重叠时,系统使用 SDF 距离函数评估每个 Volume 的影响范围,按优先级混合效果参数:
// 全局 Volume — 始终生效,低优先级(基础层)// 实体 A: PostProcessVolume,isGlobal=true,priority=0// effects: [{ type: 'vignette', enabled: true, uniforms: { u_intensity: 0.3 } }]
// 局部 Volume — 相机进入区域时激活// 实体 B: PostProcessVolume,isGlobal=false,shape='sphere',size={x:10,y:10}// priority=1,blendDistance=3// effects: [{ type: 'blur', enabled: true, uniforms: { u_intensity: 5.0 } }]编辑器工作流
在编辑器中为 Camera 实体添加 PostProcessVolume 组件即可可视化配置效果:
- 在 Hierarchy 中选中 Camera 实体
- 添加组件 → PostProcessVolume
- 配置 Volume 属性:
- Is Global — 勾选为全局效果,取消勾选为空间触发
- Priority / Weight — 控制混合顺序和强度
- Shape / Size / Blend Distance — 定义空间触发区域(非全局时可见)
- 点击 添加效果 并从内置效果列表中选择
- 使用滑块调整参数
效果在场景视图和游戏视图中都可见。组件数据随场景文件保存。
自定义 Pass
编写自定义片段着色器。顶点着色器使用固定的全屏三角形 — 你只需编写片段着色器:
import { Material, PostProcess } from 'esengine';
const invertShader = Material.createShader( `#version 300 es precision highp float; layout(location = 0) in vec2 a_position; layout(location = 1) in vec2 a_texCoord; out vec2 v_texCoord; void main() { v_texCoord = a_texCoord; gl_Position = vec4(a_position, 0.0, 1.0); }`, `#version 300 es precision highp float; in vec2 v_texCoord; uniform sampler2D u_texture; uniform float u_intensity; out vec4 fragColor; void main() { vec4 color = texture(u_texture, v_texCoord); vec3 inverted = mix(color.rgb, 1.0 - color.rgb, u_intensity); fragColor = vec4(inverted, color.a); }`);
const fx = PostProcess.createStack();fx.addPass('invert', invertShader);fx.setUniform('invert', 'u_intensity', 1.0);PostProcess.bind(cameraEntity, fx);参见材质与着色器了解更多着色器编写方法。
示例:暂停菜单模糊
游戏暂停时启用模糊和灰度效果:
import { PostProcess } from 'esengine';
const pauseFx = PostProcess.createStack();pauseFx.addPass('blur', PostProcess.createBlur());pauseFx.addPass('gray', PostProcess.createGrayscale());pauseFx.setAllPassesEnabled(false);
PostProcess.bind(cameraEntity, pauseFx);
function setPaused(paused: boolean) { pauseFx.setAllPassesEnabled(paused); pauseFx.setUniform('blur', 'u_intensity', 4.0); pauseFx.setUniform('gray', 'u_intensity', 0.6);}示例:不同相机使用不同效果
// 主相机 — 辉光 + 暗角const mainFx = PostProcess.createStack();mainFx.addPass('bloom_extract', PostProcess.createBloomExtract());mainFx.setUniform('bloom_extract', 'u_threshold', 0.4);for (let i = 0; i < 5; i++) { mainFx.addPass(`bloom_kawase_${i}`, PostProcess.createBloomKawase(i));}mainFx.addPass('bloom_composite', PostProcess.createBloomComposite());mainFx.setUniform('bloom_composite', 'u_intensity', 1.5);mainFx.addPass('vignette', PostProcess.createVignette());mainFx.setUniform('vignette', 'u_intensity', 0.6);PostProcess.bind(mainCamera, mainFx);
// 小地图相机 — 灰度const minimapFx = PostProcess.createStack();minimapFx.addPass('gray', PostProcess.createGrayscale());minimapFx.setUniform('gray', 'u_intensity', 0.5);PostProcess.bind(minimapCamera, minimapFx);生命周期
| 方法 | 说明 |
|---|---|
PostProcess.createStack() | 创建新的效果栈 |
PostProcess.bind(camera, stack) | 将 Stack 绑定到相机 |
PostProcess.unbind(camera) | 解绑相机的 Stack |
PostProcess.getStack(camera) | 获取已绑定的 Stack(无则返回 null) |
PostProcess.setScreenStack(stack) | 设置屏幕级效果栈(传 null 清除) |
PostProcess.screenStack | 获取当前屏幕级效果栈 |
PostProcess.init(width, height) | 初始化管线(自动调用) |
PostProcess.shutdown() | 关闭管线 |
PostProcess.resize(width, height) | 更新帧缓冲尺寸(自动调用) |
PostProcess.begin() | 开始捕获场景用于后处理 |
PostProcess.end() | 结束捕获并执行所有效果 pass |
PostProcess.createBloomExtract() | 创建辉光亮度提取着色器 |
PostProcess.createBloomKawase(iteration) | 创建辉光 Kawase 模糊着色器 |
PostProcess.createBloomComposite() | 创建辉光合成着色器 |
PostProcess.createBlur() | 创建模糊效果着色器 |
PostProcess.createVignette() | 创建暗角效果着色器 |
PostProcess.createGrayscale() | 创建灰度效果着色器 |
PostProcess.createChromaticAberration() | 创建色差效果着色器 |