Skip to content

Three.js helper to easily create multi passes post processing effects.

License

Notifications You must be signed in to change notification settings

martinlaxenaire/three-multipass-post-processing

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

11 Commits
 
 
 
 
 
 
 
 

Repository files navigation

Three.js helper to create multi passes post processing effects.

Based on this excellent article by luruke, this allows to easily add multiple passes post processing to your three.js scenes.

Installation

In a browser:

<script src="three.min.js"></script>
<script src="three.multipass.post.processing.min.js"></script>

Using npm:

npm install martinlaxenaire/three-multipass-post-processing

Load ES module:

import MultiPostFX from 'three-multipass-post-processing';

Initializing

// assuming 'renderer' is a THREE.WebGLRenderer class object
const multiPostFX = new MultiPostFX({
    renderer: renderer,
    passes: {
        invertColors: {
            fragmentShader: `
                precision highp float;
                uniform sampler2D uScene;
                uniform vec2 uResolution;
                
                void main() {
                    vec2 uv = gl_FragCoord.xy / uResolution.xy;
                    vec4 color = texture2D(uScene, uv);
                    // invert colors                 
                    color = mix(color, vec4((1.0 - color.rgb) * color.a, color.a), step(uv.x, 0.5));
                    color = mix(color, vec4((1.0 - color.rgb) * color.a, color.a), step(uv.y, 0.5));
                    gl_FragColor = color;
                }
            `,
        },
    }
});

// ...

// in your rendering loop, instead of using 'renderer.render(scene, camera)'
multiPostFX.render(scene, camera);

Parameters

renderer: your THREE.WebGLRenderer object used to render your scene initially

passes: an object where each pass is an object with these parameters:

Parameter Type Default Description
vertexShader String see below The vertex shader used by your pass
fragmentShader String see below The fragment shader used by your pass (this is where you'll do most of your postprocessing stuff)
uniforms object null Additional uniforms to use in your shaders. Will be merged with the built-in uniforms. Should respect three.js Uniform object structure
format THREE texture constants format THREE.RGBAFormat The texture format property (whether to use alpha for example)

Default built-in shaders and uniforms

Default uniforms

There are two default uniforms that your passes will always use:

uScene (three.js Texture): a texture containing the scene to which post processing will be applied.

uResolution (three.js Vector2): your renderer parameter sizes, used to calculate the UV in your fragment shader.

Built-in shaders

Vertex shader

If you don't want to pass any varyings from your vertex shader to your fragment shader, don't specify any vertexShader property and your post processing pass will use the default one:

precision highp float;
attribute vec2 position;
void main() {
    gl_Position = vec4(position, 1.0, 1.0);
}

Fragment shader

If you don't specify any fragment shader, then your scene will be rendered as is by using this fragment shader (note how the UV are calculated thanks to the uResolution uniform):

precision highp float;
uniform sampler2D uScene;
uniform vec2 uResolution;
void main() {
    vec2 uv = gl_FragCoord.xy / uResolution.xy;
    gl_FragColor = texture2D(uScene, uv);
}

Methods

Method parameters Description
render scene, camera Renders your scene with all the post processing passes applied
resize - Resize the passes (use it after having resized your scene and updated your camera aspect and matrix)

Example

let ww = window.innerWidth;
let wh = window.innerHeight;

let scene = new THREE.Scene();
let camera = new THREE.PerspectiveCamera(45, ww / wh, 0.1, 2000);
camera.position.set(0, 0, 3);
camera.lookAt(new THREE.Vector3(0, 0, 0));

scene.add(camera);

let renderer = new THREE.WebGLRenderer({
    alpha: true,
    antialias: false // post processing will disable default antialiasing anyway
});
renderer.setSize(ww, wh);
document.body.appendChild(renderer.domElement);

// add a simple point light and a cube
let light = new THREE.PointLight(0xffffff, 1, 0);
light.position.set(10, 10, 10);
scene.add(light);

let box = new THREE.Mesh(
    new THREE.BoxGeometry(1, 1),
    new THREE.MeshPhongMaterial({
        color: 0x156289,
        emissive: 0x072534,
    })
);

scene.add(box);


let passes = {
    distortion: {
        uniforms: {
            uTime: {
                value: 0
            }
        },
        fragmentShader: `
            precision highp float;
            uniform sampler2D uScene;
            uniform vec2 uResolution;
            uniform float uTime;
            void main() {
                vec2 uv = gl_FragCoord.xy / uResolution.xy;
                // displace UV based on time uniform
                uv += vec2(
                    sin(uv.y * 25.0) * cos(uv.x * 25.0) * (cos(uTime / 60.0)) / 50.0,
                    cos(uv.y * 25.0) * sin(uv.x * 25.0) * (sin(uTime / 60.0)) / 50.0
                );
                gl_FragColor = texture2D(uScene, uv);
            }
        `
    },
    invertColors: {
        fragmentShader: `
            precision highp float;
            uniform sampler2D uScene;
            uniform vec2 uResolution;
            
            void main() {
                vec2 uv = gl_FragCoord.xy / uResolution.xy;
                vec4 color = texture2D(uScene, uv);
                // invert colors                 
                color = mix(color, vec4((1.0 - color.rgb) * color.a, color.a), step(uv.x, 0.5));
                color = mix(color, vec4((1.0 - color.rgb) * color.a, color.a), step(uv.y, 0.5));
                gl_FragColor = color;
            }
        `,
    },
    // you can add another pass like FXAA if you want...
};

let postFX = new MultiPostFX({
    renderer: renderer,
    passes: passes
});

// handle resize
window.addEventListener("resize", () => this.onResize())

// render everything
animate();

function animate() {
    // continuously rotate our cube
    box.rotation.x += 0.005;
    box.rotation.y += 0.005;

    // increase our distortion pass' time uniform
    postFX.passes.distortion.material.uniforms.uTime.value++;
    // render everything
    postFX.render(scene, camera);

    requestAnimationFrame(() => this.animate());
}

function onResize() {
    ww = window.innerWidth;
    wh = window.innerHeight;

    camera.aspect = ww / wh;
    camera.updateProjectionMatrix();

    renderer.setSize(ww, wh);

    postFX.resize();
}

Useful post processing shaders

All credits go to their respective authors.

About

Three.js helper to easily create multi passes post processing effects.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published