mirror of
https://github.com/galacean/engine.git
synced 2026-05-07 23:37:11 +08:00
178 lines
6.7 KiB
Plaintext
178 lines
6.7 KiB
Plaintext
---
|
|
order: 2
|
|
title: Custom Post-Processing
|
|
---
|
|
|
|
In the post-processing system, the effect ([Effect](/apis/core/#PostProcessEffect)) is responsible for maintaining the data layer, and the pipeline ([Pass](/apis/core/#PostProcessPass)) is responsible for writing the rendering logic. In the pipeline, calling the [getBlendEffect](/apis/core/#PostProcessManager-getBlendEffect) method can get the final post-processing data after **global/local** mixing.
|
|
|
|
```mermaid
|
|
graph TD;
|
|
subgraph Pass
|
|
A1[Custom Pass ...]
|
|
A2[Uber Pass]
|
|
A3[Custom Pass ...]
|
|
end
|
|
|
|
subgraph Effect
|
|
B1[Bloom Effect]
|
|
B2[Tonemapping Effect]
|
|
B3[Custom Effect]
|
|
end
|
|
|
|
A1 --> A2 --> A3
|
|
B1 --> B2 --> B3
|
|
|
|
A1 ---|Blend| B1
|
|
```
|
|
|
|
The engine has built-in [PostProcessUberPass](/apis/core/#PostProcessUberPass), which is used with [BloomEffect](/apis/core/#BloomEffect) and [TonemappingEffect](/apis/core/#TonemappingEffect) data. If you want to customize post-processing effects, we need to create a new Pass and then create an Effect based on whether the data needs to be fused.
|
|
|
|
## A Demo
|
|
|
|
Here we will simply implement a grayscale image post-processing effect~
|
|
|
|
<Comparison
|
|
leftSrc="https://gw.alipayobjects.com/zos/OasisHub/f5d3ea3d-47a4-4618-b3d6-0ea46321e786/image-20250115162952605.png"
|
|
leftText="Uber Pass"
|
|
rightSrc="https://gw.alipayobjects.com/zos/OasisHub/75ccba72-b70b-49f6-812a-e00305e89201/image-20250115163026345.png"
|
|
rightText="Uber Pass + Custom Pass"
|
|
/>
|
|
|
|
### 1. Add script
|
|
|
|
Let's create a script first. Next, we will write our custom post-processing Shader and Pass in this script file.
|
|
|
|
<Image src="https://gw.alipayobjects.com/zos/OasisHub/6df5fd1c-a24c-4e32-a62e-841b61fe76c7/image-20250124111456360.png" />
|
|
|
|
We refer to [Adding post-processing methods](/en/docs/graphics/postProcess/postProcess/#1-adding-a-post-processing-component) to add global or local post-processing components, and hang the script on this entity:
|
|
|
|
<Image src="https://gw.alipayobjects.com/zos/OasisHub/62a36df2-fdc5-4c13-9a32-c41f963d18b5/image-20250124112043221.png" />
|
|
|
|
### 2. Shader Writing
|
|
|
|
The algorithm is not special, but you need to pay attention to `renderer_BlitTexture` This built-in variable is the post-processing rendering result of the previous Pass. Here it is the result after Bloom and Tonemapping. We display this result in grayscale.
|
|
|
|
```ts showLineNumbers {14} filename="CustomPostProcessPass.ts"
|
|
const customShader = Shader.create(
|
|
"Gray Scale Shader",
|
|
`
|
|
attribute vec4 POSITION_UV;
|
|
varying vec2 v_uv;
|
|
|
|
void main() {
|
|
gl_Position = vec4(POSITION_UV.xy, 0.0, 1.0);
|
|
v_uv = POSITION_UV.zw;
|
|
}
|
|
`,
|
|
`
|
|
varying vec2 v_uv;
|
|
uniform sampler2D renderer_BlitTexture;
|
|
|
|
void main(){
|
|
vec4 color = texture2D(renderer_BlitTexture, v_uv);
|
|
float grayScale = 0.299 * color.r + 0.587 * color.g + 0.114 * color.b;
|
|
gl_FragColor = vec4(vec3(grayScale), 1.0);
|
|
}
|
|
`
|
|
);
|
|
```
|
|
|
|
### 3. Create a new Pass
|
|
|
|
We create a new Pass, directly [Blitter](/apis/core/#Blitter) to the screen in the [onRender hook](/apis/core/#PostProcessUberPass-onRender), and then add this Pass to the engine.
|
|
|
|
```ts showLineNumbers {10,15} filename="CustomPostProcessPass.ts"
|
|
class CustomPass extends PostProcessPass {
|
|
private _blitMaterial: Material;
|
|
|
|
constructor(engine: Engine) {
|
|
super(engine);
|
|
this._blitMaterial = new Material(this.engine, customShader);
|
|
}
|
|
|
|
onRender(_, srcTexture: Texture2D, dst: RenderTarget): void {
|
|
Blitter.blitTexture(this.engine, srcTexture, dst, undefined, undefined, this._blitMaterial, 0);
|
|
}
|
|
}
|
|
|
|
const customPass = new CustomPass(engine);
|
|
engine.addPostProcessPass(customPass);
|
|
```
|
|
|
|
The execution order of Pass is executed after Uber Pass by default, that is [`PostProcessPassEvent.AfterUber`](/apis/core/#PostProcessPassEvent-AfterUber), we can also manually modify the execution order of the pipeline:
|
|
|
|
```ts filename="CustomPostProcessPass.ts"
|
|
customPass.event = PostProcessPassEvent.BeforeUber;
|
|
```
|
|
|
|
Whether the Pass is effective is determined by the Pass's [isActive](/apis/core/#PostProcessUberPass-isActive) by default. We can also modify the effectiveness logic, such as whether the intensity is greater than 0:
|
|
|
|
```ts showLineNumbers {2-9} filename="CustomPostProcessPass.ts"
|
|
class CustomPass extends PostProcessPass {
|
|
override isValid(postProcessManager: PostProcessManager): boolean {
|
|
if (!this.isActive) {
|
|
return false;
|
|
}
|
|
|
|
const customEffectBlend = postProcessManager.getBlendEffect(CustomEffect);
|
|
return customEffectBlend?.intensity > 0;
|
|
}
|
|
}
|
|
```
|
|
|
|
### 4. Blend data
|
|
|
|
The above steps 2 and 3 can already customize the post-processing effect. Here is an advanced version of Blend data.
|
|
|
|
Take `intensity` as an example. We define a `CustomEffect`, which is specifically used to fuse intensity. The data to be fused is also very simple. The engine has encapsulated a series of post-processing parameters, such as [floating point type parameters](/apis/core/#PostProcessEffectFloatParameter).
|
|
|
|
```ts showLineNumbers {2,7} filename="CustomPostProcessPass.ts"
|
|
class CustomEffect extends PostProcessEffect {
|
|
intensity = new PostProcessEffectFloatParameter(0.8);
|
|
}
|
|
|
|
// Add this effect to the post-processing component. postProcess can be created separately,
|
|
// or it can be the same as Bloom and other effects. It depends on the Blend requirements~
|
|
postProcess.addEffect(CustomEffect);
|
|
```
|
|
|
|
After defining the data, you need to change the `onRender` hook of the custom Pass to get the Blend data:
|
|
|
|
```ts showLineNumbers {3-6} filename="CustomPostProcessPass.ts"
|
|
class CustomPass extends PostProcessPass {
|
|
onRender(camera: Camera, srcTexture: Texture2D, dst: RenderTarget): void {
|
|
const postProcessManager = camera.scene.postProcessManager;
|
|
const customEffectBlend = postProcessManager.getBlendEffect(CustomEffect);
|
|
if (customEffectBlend) {
|
|
this._blitMaterial.shaderData.setFloat("u_intensity", customEffectBlend.intensity.value);
|
|
}
|
|
|
|
Blitter.blitTexture(this.engine, srcTexture, dst, undefined, undefined, this._blitMaterial, 0);
|
|
}
|
|
}
|
|
```
|
|
|
|
As you can see, we continuously set the fused intensity in the `onRender` hook of the custom pipeline, and then consume this data in the shader:
|
|
|
|
```ts showLineNumbers {8,12} filename="CustomPostProcessPass.ts"
|
|
const customShader = Shader.create(
|
|
"Gray Scale Shader",
|
|
`
|
|
......
|
|
`,
|
|
`
|
|
......
|
|
uniform float u_intensity;
|
|
|
|
void main(){
|
|
......
|
|
gl_FragColor = vec4(mix(color.rgb, vec3(grayScale), u_intensity), 1.0);
|
|
}
|
|
`
|
|
);
|
|
```
|
|
|
|
If your post-processing component is in local mode, we can also use [Blend Distance](/apis/core/#PostProcess-blendDistance) to set the distance at which the camera is close to the collision body to start blending:
|
|
|
|
<Image src="https://gw.alipayobjects.com/zos/OasisHub/76b084c1-f517-47d7-8067-7f838db002f8/2025-01-15%25252017.38.30.gif" />
|