Skip to content

Commit 3bc1f95

Browse files
committed
Update content
1 parent cd1072e commit 3bc1f95

File tree

10 files changed

+283
-26
lines changed

10 files changed

+283
-26
lines changed

manual/config/_default/menu.en.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,27 +4,27 @@
44
"identifier": "docs",
55
"name": "API Reference",
66
"url": "../docs/",
7-
"weight": 100
7+
"weight": 999
88
}
99
],
1010
"community": [
1111
{
1212
"identifier": "bugs",
1313
"name": "Issue Tracker",
1414
"url": "https://github.com/pmndrs/postprocessing/issues",
15-
"weight": 70
15+
"weight": 997
1616
},
1717
{
1818
"identifier": "discord",
1919
"name": "Discord",
2020
"url": "https://discord.com/invite/poimandres",
21-
"weight": 80
21+
"weight": 998
2222
},
2323
{
2424
"identifier": "forum",
2525
"name": "Forum",
2626
"url": "https://github.com/pmndrs/postprocessing/discussions",
27-
"weight": 90
27+
"weight": 999
2828
}
2929
]
3030
}

manual/content/custom-effects.en.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ collection: sections
44
title: Custom Effects
55
draft: false
66
menu: main
7-
weight: 40
7+
weight: 50
88
---
99

1010
# Custom Effects

manual/content/custom-passes.en.md

Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
---
2+
layout: single
3+
collection: sections
4+
title: Custom Passes
5+
draft: false
6+
menu: main
7+
weight: 40
8+
---
9+
10+
# Custom Passes
11+
12+
## Introduction
13+
14+
At a closer look, passes can be divided into four groups. The first group consists of passes that render normal scenes like the `GeometryPass`. The second type doesn't render anything but performs supporting operations like the `ClearPass` or `LambdaPass`. Passes that render textures for further use make up the third group. One example would be the `LuminancePass`. The fourth and most prominent group contains the fullscreen effect passes. If you want to make a pass that belongs to the last group, you should consider [creating an Effect]({{< relref "custom-effects" >}}) instead.
15+
16+
There are two options for creating custom passes. You can either rely on the general-purpose `ShaderPass` or extend `Pass`.
17+
18+
## ShaderPass
19+
20+
<details><summary>TL;DR</summary>
21+
<p>
22+
23+
```js
24+
import { ShaderMaterial, Uniform } from "three";
25+
import { ShaderPass } from "postprocessing";
26+
27+
const myShaderMaterial = new ShaderMaterial({
28+
29+
defines: { SOMETHING: "value" },
30+
uniforms: { tDiffuse: new Uniform(null) },
31+
vertexShader: "...",
32+
fragmentShader: "..."
33+
34+
});
35+
36+
const myShaderPass = new ShaderPass(myShaderMaterial, "tDiffuse");
37+
```
38+
39+
</p>
40+
</details>
41+
42+
The `ShaderPass` expects an instance of `ShaderMaterial` as its first argument. The second argument specifies the name of the texture sampler uniform of the shader you provide. This name defaults to `"inputBuffer"` and the `ShaderPass` binds its `input.defaultBuffer` to this uniform.
43+
44+
In order to render a simple `ShaderMaterial`, you have to pass your shader object (uniforms, defines, fragment and vertex shader code) to `ShaderMaterial` and then pass that material instance to `ShaderPass`. Depending on the material you use, you may have to adjust the name of the input texture.
45+
46+
## Extending Pass
47+
48+
<details><summary>TL;DR</summary>
49+
<p>
50+
51+
##### shader.frag
52+
53+
```glsl
54+
#include <pp_default_output_pars_fragment>
55+
#include <pp_input_buffer_pars_fragment>
56+
57+
uniform vec3 weights;
58+
59+
in vec2 vUv;
60+
61+
void main() {
62+
63+
vec4 texel = texture(inputBuffer, vUv);
64+
out_Color = vec4(texel.rgb * weights, texel.a);
65+
66+
}
67+
```
68+
69+
##### CustomMaterial.ts
70+
71+
```ts
72+
import { ShaderMaterial, Uniform, Vector3 } from "three";
73+
import { FullscreenMaterial, Uniform, Vector3 } from "postprocessing";
74+
75+
// Tip: Use a bundler plugin like esbuild-plugin-glsl to import shaders as text.
76+
import fragmentShader from "./shader.frag";
77+
78+
export class CustomMaterial extends FullscreenMaterial {
79+
80+
constructor() {
81+
82+
super({
83+
name: "LuminanceMaterial",
84+
fragmentShader,
85+
uniforms: {
86+
weights: new Uniform(new Vector3())
87+
}
88+
});
89+
90+
}
91+
92+
}
93+
```
94+
95+
##### CustomPass.js
96+
97+
```ts
98+
import { Pass } from "postprocessing";
99+
import { CustomMaterial } from "./CustomMaterial.js";
100+
101+
export class CustomPass extends Pass<CustomMaterial> {
102+
103+
constructor() {
104+
105+
super("CustomPass");
106+
this.fullscreenMaterial = new CustomMaterial();
107+
108+
}
109+
110+
override render(): void {
111+
112+
this.setRenderTarget(this.output.defaultBuffer?.value);
113+
this.renderFullscreen();
114+
115+
}
116+
117+
}
118+
```
119+
120+
</p>
121+
</details>
122+
123+
By extending `Pass`, you can decide what happens during resizing, initialization and rendering. There are also several lifecycle hooks that you can take advantage of. Passes in postprocessing receive various input data from the main `GeometryPass` and the preceding pass in a render pipeline.
124+
125+
The minimum requirement to create a custom pass is to override the `render` method. If you're creating a fullscreen effect, you'll need to assign a `fullscreenMaterial`:
126+
127+
```ts
128+
this.fullscreenMaterial = new MyMaterial();
129+
```
130+
131+
> [!TIP]
132+
> If your pass uses multiple materials, add them to the `materials` set so that they can be precompiled. The `fullscreenMaterial` is added automatically.
133+
134+
### Resources
135+
136+
Framebuffers can be created manually or via the `createFrambuffer` method. All framebuffers should be added to the `output` buffer resources so that the pipeline can optimize them:
137+
138+
```ts
139+
this.output.setBuffer(MyPass.BUFFER_ID, this.createFramebuffer());
140+
```
141+
142+
A convenience getter can be defined to retrieve the buffer as needed:
143+
144+
```ts
145+
private get renderTarget(): WebGLRenderTarget {
146+
147+
return this.output.getBuffer(MyPass.BUFFER_ID)!;
148+
149+
}
150+
```
151+
152+
> [!TIP]
153+
> If your pass uses disposable resources that don't fit into the existing `input` and `output` resources, add them to the `disposables` set instead.
154+
155+
### G-Buffer
156+
157+
Passes can request [GBuffer]() components via `input.gBuffer`. The actual textures will be supplied via `input.buffers` and can be retrieved by using the `GBuffer` value as the key. Passes should override the `onInputChange` hook to fetch and utilize the requested textures.
158+
159+
#### G-Buffer Packing
160+
161+
WebGL 2 guarantees that a compatible device supports at least 4 texture attachments per render target. For a broad device support, postprocessing stays within this limitation and packs certain combinations of G-Buffer components into a single texture attachment. To be able to unpack this data, special shader macros that control predefined unpacking functions are provided to the requesting passes via input `defines`. If a pass uses a fullscreen material that extends `FullscreenMaterial`, these `defines` will automatically be integrated into the shaders. To finally read the data, the following shader chunk must be included in the fragment shader:
162+
163+
```glsl
164+
#include <pp_gbuffer_packing>
165+
```
166+
167+
This include adds the following utility functions that should be used to read the respective G-Buffer data:
168+
169+
```glsl
170+
float readDepth(sampler2D depthBuffer, vec2 uv);
171+
vec3 readNormal(sampler2D normalBuffer, vec2 uv);
172+
vec2 readVelocity(sampler2D velocityBuffer, vec2 uv);
173+
```
174+
175+
176+
### Lifecycle Hooks
177+
178+
The `Pass` base class defines lifecycle methods that can be overridden to react to various events:
179+
* `checkRequirements(): void;`
180+
* `onInputChange(): void;`
181+
* `onOutputChange(): void;`
182+
* `onResolutionChange(): void;`
183+
* `onViewportChange(): void;`
184+
* `onScissorChange(): void;`
185+
* `onSceneChildAdded(): void;`
186+
* `onSceneChildRemoved(): void;`
187+
188+
It depends on the use case which of these hooks will actually be needed.
189+
190+
### Fullscreen Passes
191+
192+
It's recommended to use materials that extend [FullscreenMaterial]() when writing passes that perform fullscreen render operations. Materials of this type will benefit from the following automatic features:
193+
* The `inputBuffer` will be set based on `input.defaultBuffer`.
194+
* Input `defines` and `uniforms` will be assigned.
195+
* If the geometry pass uses a float type color buffer, the macro `FRAMEBUFFER_PRECISION_HIGH` will be defined.
196+
197+
To render a fullscreen material, set the render target and use the `renderFullscreen` method:
198+
199+
```ts
200+
override render(): void {
201+
202+
this.setRenderTarget(this.output.defaultBuffer?.value);
203+
this.renderFullscreen();
204+
205+
}
206+
```

manual/content/demos/light-shadow/bloom.en.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,16 @@ script: bloom
1111

1212
# Bloom
1313

14+
The `BloomEffect` uses a fast `MipmapBlurPass` to blur bright scene highlights. By default, any colors that exceed the LDR range `[0.0, 1.0]` will be affected by bloom. The luminance filter can be adjusting via the `luminanceThreshold` and `luminanceSmoothing` settings. It can also be disabled entirely by setting `luminancePass.enabled` to `false`.
15+
16+
The mipmap blur algorithm downsamples the input buffer multiple times based on the number of `levels`. At each level, the target buffer size is halved, creating a manual mipmap chain. After reaching the highest mipmap level, i.e. the smallest buffer size, the image is upsampled again using an additional support buffer at each level. The sampling patterns have been chosen carefully to create stable blurs with minimal aliasing, even at the screen's edges. The following images roughly visualize the blurring process and the resulting bloom texture:
17+
18+
<img src="img/bloom.gif" width="256" height="128">
19+
<img src="img/bloom.png" width="256" height="128">
20+
21+
> [!TIP]
22+
> The number of levels does not have a big impact on performance because the working buffer size quickly shrinks to negligible sizes. However, there is an upper limit to the level count which can be calculated based on the resolution: `maxLevels = log2(min(width, height))`
23+
1424
### External Resources
1525

1626
* [UE4 Custom Bloom](https://www.froyok.fr/blog/2021-12-ue4-custom-bloom/)

manual/content/demos/utility/gbuffer.en.md

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ script: gbuffer
1616
The `GeometryPass` is responsible for rendering a scene and its output serves as the starting point in a render pipeline. This output is generally referred to as a "G-Buffer" and in its simplest form, it only consists of a single color texture attachment. With [MRT](https://registry.khronos.org/webgl/specs/latest/2.0/#3.7.11) it's possible to render to multiple buffers at once which enables more advanced effects. Without MRT, the entire scene would have to be rendered multiple times to obtain various types of geometry data.
1717

1818
> [!NOTE]
19-
> The GeometryPass does not clear its output buffer automatically. Instead, an explicit ClearPass must be used before it:
19+
> The `GeometryPass` does not clear its output buffer automatically. Instead, an explicit `ClearPass` must be used before it.
2020
2121
```ts
2222
const pipeline = new RenderPipeline(renderer);
@@ -29,21 +29,16 @@ pipeline.add(
2929

3030
Depending on the requirements of other passes in the pipeline, the `GeometryPass` will configure a G-Buffer that contains the needed texture attachments. The individual render textures will then be provided to the passes that requested them.
3131

32-
## G-Buffer Packing
32+
## Alpha
3333

34-
WebGL 2 guarantees that a compatible device supports at least 4 texture attachments per render target. For a broad device support, postprocessing stays within this limitation and packs certain combinations of G-Buffer components into a single texture attachment. To be able to unpack this data, special shader macros that control predefined unpacking functions are provided to the requesting passes via input `defines`. If a pass uses a fullscreen material that extends `FullscreenMaterial`, these `defines` will automatically be integrated into the shaders. To finally read the data, the following shader chunk must be included in the fragment shader:
34+
Postprocessing uses the compact `R11F_G11F_B10F` framebuffer format by default which requires alpha to be disabled. This reduces memory usage for most internal color buffers by 50% while still yielding sufficient precision to combat banding. However, if you need to use transparent framebuffers, you can enable alpha like this:
3535

36-
```glsl
37-
#include <pp_gbuffer_packing>
36+
```ts
37+
const geoPass = new GeometryPass(scene, camera, { alpha: true });
3838
```
3939

40-
This include adds the following utility functions that should be used to read the respective G-Buffer data:
41-
42-
```glsl
43-
float readDepth(sampler2D depthBuffer, vec2 uv);
44-
vec3 readNormal(sampler2D normalBuffer, vec2 uv);
45-
vec2 readVelocity(sampler2D velocityBuffer, vec2 uv);
46-
```
40+
> [!NOTE]
41+
> Enabling alpha changes the internal framebuffer format to `RGBA16F`.
4742
4843
### External Resources
4944

manual/content/demos/utility/ui.en.md

Lines changed: 44 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,26 +15,65 @@ UI rendering requires special treatment when combined with postprocessing. There
1515

1616
## Depth-Aware UI
1717

18-
Excluding UI elements, or any other 3D object, from being affected by image effects can be done by using a second `GeometryPass` with a separate UI scene. By default, the pass will copy the output of the preceding pass to its own output buffer before rendering the UI scene. If the UI pass was configured to use a `depthBuffer`, depth will either be reused, if possible, or copied. This allows UI elements to be occluded by other objects. However, there are three problems with this approach:
18+
Excluding UI elements, or any other 3D object, from being affected by image effects can be done by using a second `GeometryPass` with a separate UI scene. By default, the pass will copy the output of the preceding pass to its own output buffer before rendering the UI scene. If the UI pass was configured to use a `depthBuffer` (`true` by default), depth will either be reused, if possible, or copied. This allows UI elements to be occluded by other objects. However, there are three problems with this approach:
1919

2020
1. Overlay effects such as bloom don't use depth, so they will always be overwritten by the UI elements.
2121
2. MSAA is not supported because the image has already been resolved at this point.
2222
3. The additional copy operation is fast but not free.
2323

24+
```ts
25+
const clearPass = new ClearPass();
26+
const geoPass = new GeometryPass(scene, camera);
27+
const effectPass = new EffectPass(..., toneMappingEffect);
28+
const uiPass = new GeometryPass(uiScene, camera);
29+
const aaPass = new EffectPass(aaEffect);
30+
31+
const pipeline = new RenderPipeline(renderer);
32+
pipeline.add(clearPass, geoPass, effectPass, uiPass, aaPass);
33+
```
34+
2435
> [!NOTE]
25-
> Bloom and similar overlay effects may be applied after the UI elements, but tone mapping must then be applied afterwards to avoid clipping.
36+
> Bloom and similar overlay effects may be applied after the UI elements, but tone mapping must then be applied afterwards to avoid color clipping.
2637
2738
## HUD Overlay
2839

2940
### Single Render Pipeline
3041

3142
Instead of using a depth-aware `GeometryPass`, the UI elements can be rendered directly on top of the output buffer of a preceding fullscreen pass. To do this, the `GeometryPass` must be instantiated without a `depthBuffer` and its `output.defaultBuffer` must be set to the `output.defaultBuffer` of the preceding pass. The result then needs to be rendered to screen with a final `CopyPass` or `EffectPass`.
3243

33-
> [!TIP]
34-
> It's recommended to use an `EffectPass` with an antialiasing effect as the final pass.
44+
```ts
45+
const clearPass = new ClearPass();
46+
const geoPass = new GeometryPass(scene, camera);
47+
const effectPass = new EffectPass(..., toneMappingEffect);
48+
const uiPass = new GeometryPass(uiScene, camera, { depthBuffer: false });
49+
uiPass.output.defaultBuffer = effectPass.output.defaultBuffer;
50+
const aaPass = new EffectPass(aaEffect);
51+
52+
const pipeline = new RenderPipeline(renderer);
53+
pipeline.add(clearPass, geoPass, effectPass, uiPass, aaPass);
54+
```
3555

3656
Due to the lack of depth information, MSAA is not supported.
3757

3858
### Multiple Render Pipelines
3959

40-
UI elements can also be rendered in a separate pipeline with a `ClearPass` and a `GeometryPass`. The `GeometryPass` can make use of MSAA if it has a `depthBuffer` and the UI scene can be rendered only when needed. The resulting UI output texture can then be integrated into the main pipeline by using a `TextureEffect`. This approach is not depth-aware.
60+
UI elements can also be rendered in a separate pipeline with a `ClearPass` and a `GeometryPass`. The `GeometryPass` can make use of MSAA if it has a `depthBuffer` and the UI scene can be rendered only when needed. The resulting UI output texture can then be integrated into the main pipeline by using a `TextureEffect`. This approach is not depth-aware between pipelines.
61+
62+
```ts
63+
const uiClearPass = new ClearPass();
64+
const uiPass = new GeometryPass(uiScene, camera, { alpha: true, samples: 4 });
65+
66+
const uiPipeline = new RenderPipeline(renderer);
67+
uiPipeline.add(uiClearPass, uiPass);
68+
69+
const uiOverlayTexture = uiPass.output.defaultBuffer?.texture?.value ?? null;
70+
const uiOverlayEffect = new TextureEffect(uiOverlayTexture);
71+
72+
const clearPass = new ClearPass();
73+
const geoPass = new GeometryPass(scene, camera);
74+
const effectPass = new EffectPass(..., uiOverlayEffect, toneMappingEffect);
75+
const aaPass = new EffectPass(aaEffect);
76+
77+
const pipeline = new RenderPipeline(renderer);
78+
pipeline.add(clearPass, geoPass, effectPass, aaPass);
79+
```

0 commit comments

Comments
 (0)