Skip to content

Custom Effects

Raoul v. R edited this page May 23, 2021 · 19 revisions

Introduction

Effects are lightweight fullscreen passes. They can be combined using the EffectPass.

Just like passes, effects may perform initialization tasks, react to render size changes and execute supporting render operations if needed but they don't have access to an output buffer and are not supposed to render to screen by themselves.

TL;DR

Code Example

shader.frag
uniform vec3 weights;

void mainImage(const in vec4 inputColor, const in vec2 uv, out vec4 outputColor) {

	outputColor = vec4(inputColor.rgb * weights, inputColor.a);

}
CustomEffect.js
import { Uniform, Vector3 } from "three";
import { BlendFunction, Effect } from "postprocessing";

// Using rollup-plugin-glsl to import shader files.
import fragmentShader from "./shader.frag";

export class CustomEffect extends Effect {

	constructor() {

		super("CustomEffect", fragmentShader, {

			blendFunction: BlendFunction.Normal,
			uniforms: new Map([
				["weights", new Uniform(new Vector3())]
			])

		});

	}

}

Shader Function Signatures

Fragment Shader

Every effect must provide a fragment shader that implements at least one of these two functions:

void mainImage(const in vec4 inputColor, const in vec2 uv, out vec4 outputColor);
void mainUv(inout vec2 uv);

If the effect relies on depth, it can choose to implement a slightly different mainImage function:

void mainImage(const in vec4 inputColor, const in vec2 uv,
	const in float depth, out vec4 outputColor);

Vertex Shader

Effects may also provide a vertex shader that implements the following function:

void mainSupport();

If you need access to the screen uv coordinates, use the following function signature:

void mainSupport(const in vec2 uv);

Effect Attributes

Some effects perform special operations inside the fragment shader or require additional resources. There are currently two EffectAttributes available: CONVOLUTION and DEPTH. Most effects do not need to specify any attributes.

Attributes can be concatenated using the bitwise OR operator.

class MyEffect extends Effect {

	constructor() {

		super(name, fragmentShader, {
			attributes: EffectAttribute.CONVOLUTION | EffectAttribute.DEPTH
		});

	}

}

Convolution

Effects that fetch additional samples from the input buffer inside the fragment shader must specify the CONVOLUTION attribute. To prevent bad results, it is not allowed to have more than one effect with this attribute per EffectPass. Convolution effects are also incompatible with effects that implement the mainUv function. The EffectPass will warn you when it encounters incompatible effects.

Depth

The DEPTH attribute must be specified by effects that require a depth texture. When the EffectPass encounters an effect with this attribute, it will request a depth texture from the EffectComposer. If your fragment shader implements the depth variant of the mainImage function, depth will be read automatically and the value will be provided to your mainImage function.
In case you wish to sample depth at another coordinate, use the predefined function float readDepth(const in vec2 uv). To calculate the view Z based on depth, you can use the predefined function float getViewZ(const in float depth).

Uniforms, Macros and Varyings

All shaders have access to the following uniforms:

uniform vec2 resolution;
uniform vec2 texelSize;
uniform float cameraNear;
uniform float cameraFar;
uniform float aspect;
uniform float time;

The fragment shader has access to the following additional uniforms:

uniform sampler2D inputBuffer;
uniform sampler2D depthBuffer;

The following varyings are reserved:

varying vec2 vUv;

Available vertex attributes:

attribute vec3 position;

Available macros:

  • If the camera of the associated EffectPass is a PerspectiveCamera, the macro PERSPECTIVE_CAMERA will be defined.
  • If the composer uses HalfFloatType frame buffers, the macro FRAMEBUFFER_PRECISION_HIGH will be defined.

Effects may define custom uniforms, varyings, functions and preprocessor macros as usual, but should not define global variables or constants.

Furthermore, the shader chunks common and packing are included in the fragment shader by default.

WebGL Extensions

Effects can enable a set of shader extensions via the extensions constructor option. The available WebGLExtensions are DERIVATIVES, FRAG_DEPTH, DRAW_BUFFERS and SHADER_TEXTURE_LOD.

Code Example

class MyEffect extends Effect {

	constructor() {

		super(name, fragmentShader, {
			extensions: new Set([WebGLExtension.DERIVATIVES])
		});

	}

}