-
-
Notifications
You must be signed in to change notification settings - Fork 216
Custom Passes
At a closer look, passes can be divided into four groups. The first group consists of passes that render normal scenes like the RenderPass and MaskPass. The second type doesn't render anything, but performs supporting operations. For example, the ClearMaskPass belongs to that group. Passes that render special textures make up the third group. GPGPU passes are a good example for this group. 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 instead.
There are two options for creating custom passes. You can either rely on the general-purpose ShaderPass or extend Pass.
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 the inputBuffer
to this uniform.
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.
ShaderPass Code Example
import { ShaderMaterial, Uniform } from "three";
import { ShaderPass } from "postprocessing";
const myShaderMaterial = new ShaderMaterial({
defines: { LABEL: "value" },
uniforms: { tDiffuse: new Uniform(null) },
vertexShader: "...",
fragmentShader: "..."
});
const myShaderPass = new ShaderPass(myShaderMaterial, "tDiffuse");
An example that uses the Kaleidoscope Shader from the three.js examples together with the ShaderPass
can be found here.
More complex passes sometimes require additional programming. By extending the Pass
class you can decide what happens with your pass during resizing, initialization and rendering.
The minimum requirement to create a custom pass is to override the render
method. You may also need to set the needsSwap
flag to false
if your pass never renders to the outputBuffer
. If you're creating a fullscreen effect, you'll need to assign a fullscreen Material
by using the setFullscreenMaterial(Material)
method. You may also create custom render targets in your pass.
Inside the render method you have access to an inputBuffer
as well as an outputBuffer
. Reading from and writing to the same render target should be avoided. Therefore, two seperate yet identical buffers are used. The EffectComposer
expects your pass to render its result to the outputBuffer
. After your pass has finished rendering, the outputBuffer
will be swapped with the inputBuffer
so that the next pass can find the result in the inputBuffer
. If your pass doesn't write to the outputBuffer
, you need to set needsSwap
to false
. Otherwise, the image in the input buffer will be lost.
Note that Passes don't have to use the buffers that are provided in the render method. Writing self-contained render-to-texture passes is also a feasible option.
Custom Pass Code Example
varying vec2 vUv;
void main() {
vUv = position.xy * 0.5 + 0.5;
gl_Position = vec4(position.xy, 1.0, 1.0);
}
uniform sampler2D inputBuffer;
uniform vec3 weights;
varying vec2 vUv;
void main() {
vec4 texel = texture2D(inputBuffer, vUv);
gl_FragColor = vec4(texel.rgb * weights, texel.a);
// Support automatic output encoding.
#include <encodings_fragment>
}
import { ShaderMaterial, Uniform, Vector3 } from "three";
// Using rollup-plugin-glsl to import shader files.
import fragmentShader from "./shader.frag";
import vertexShader from "./shader.vert";
export class CustomMaterial extends ShaderMaterial {
constructor() {
super({
type: "CustomMaterial",
uniforms: {
inputBuffer: new Uniform(null),
weights: new Uniform(new Vector3())
},
fragmentShader,
vertexShader,
toneMapped: false,
depthWrite: false,
depthTest: false
});
}
}
import { Pass } from "postprocessing";
import { CustomMaterial } from "./CustomMaterial.js";
export class CustomPass extends Pass {
constructor() {
super("CustomPass");
this.setFullscreenMaterial(new CustomMaterial());
}
render(renderer, inputBuffer, outputBuffer, deltaTime, stencilTest) {
const material = this.getFullscreenMaterial();
material.uniforms.inputBuffer.value = inputBuffer.texture;
renderer.setRenderTarget(this.renderToScreen ? null : outputBuffer);
renderer.render(this.scene, this.camera);
}
}