mcpField
T
threejs-impl-post-processing

threejs-impl-post-processing

github.com/impertio-studioDevelopment23143 installsUpdated 30w ago
Repository

About threejs-impl-post-processing

A Three.js skill reference for implementing post-processing with built-in, pmndrs, and WebGPU systems. It covers effect pipelines, pass ordering, configuration, and resizing.

Key features

  1. 01Comparison of three post-processing systems
  2. 02EffectComposer pipeline setup
  3. 03Bloom, occlusion, outline, and depth effects
  4. 04Anti-aliasing and custom shader passes
  5. 05Resize handling and system compatibility

Use cases

  1. 01Adding bloom to a WebGL scene
  2. 02Configuring an outline for selected objects
  3. 03Setting up anti-aliasing and color output
  4. 04Choosing a post-processing approach for R3F

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/impertio-studio/three.js-claude-skill-package --skill threejs-impl-post-processing

After installing, type/threejs-impl-post-processingin Claude Code to confirm it loaded.

Bundled files

References and scripts installed along with SKILL.md.

.gitkeep
references/
anti-patterns.md
examples.md
methods.md

Details

From the project README

threejs-impl-post-processing

Quick Reference

Architecture Overview

Three.js offers TWO incompatible post-processing systems. NEVER mix them.

SystemPackageApproachBest For
Built-in EffectComposerthree/addons/postprocessing/One shader pass per effectSimple setups, custom ShaderPass
pmndrs/postprocessingpostprocessing (npm)Merges effects into single passPerformance-critical, R3F apps
WebGPU PostProcessingthree/addons/tsl/display/Node-based TSL graphsWebGPU renderer only
Standard Pipeline (Built-in EffectComposer)
import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js';
import { RenderPass } from 'three/addons/postprocessing/RenderPass.js';
import { OutputPass } from 'three/addons/postprocessing/OutputPass.js';

const composer = new EffectComposer( renderer );
composer.addPass( new RenderPass( scene, camera ) ); // ALWAYS first
// ... effect passes here ...
composer.addPass( new OutputPass() );                 // ALWAYS last

function animate() {
  composer.render(); // replaces renderer.render( scene, camera )
}
Pass Ordering Rules
  1. RenderPass MUST be the FIRST pass -- it renders the scene to the internal buffer
  2. Effect passes go in the MIDDLE in any order (bloom, SSAO, outline, etc.)
  3. Anti-aliasing passes (SMAA, FXAA) go AFTER effect passes
  4. OutputPass MUST be the LAST pass -- it applies tone mapping and color space conversion
  5. NEVER omit OutputPass -- without it, colors appear washed out or incorrect
Critical Warnings

NEVER mix three/addons/postprocessing/EffectComposer with pmndrs/postprocessing EffectComposer. They use incompatible buffer formats and will produce rendering artifacts or crashes.

NEVER forget to call composer.setSize() on window resize. Failing to resize the composer causes blurry or misaligned effects.

NEVER call renderer.render( scene, camera ) when using EffectComposer. ALWAYS call composer.render() instead -- calling both renders the scene twice.

NEVER use WebGL post-processing passes with the WebGPU renderer. WebGPU uses an entirely separate node-based PostProcessing class.

ALWAYS include RenderPass as the first pass. Without it, subsequent passes receive an empty buffer.


EffectComposer API

Constructor
new EffectComposer( renderer: WebGLRenderer, renderTarget?: WebGLRenderTarget )
  • renderer -- the WebGLRenderer instance
  • renderTarget -- optional custom render target; auto-created if omitted
Properties
PropertyTypeDescription
.passesPass[]Ordered array of post-processing passes
.readBufferWebGLRenderTargetInternal read buffer
.writeBufferWebGLRenderTargetInternal write buffer
.renderToScreenbooleanWhether the final pass renders to screen (default true)
.rendererWebGLRendererThe renderer instance
Methods
MethodDescription
.addPass( pass )Appends a pass to the end of the chain
.insertPass( pass, index )Inserts a pass at a specific position
.removePass( pass )Removes a pass from the chain
.render( deltaTime? )Executes all enabled passes in order
.setSize( width, height )Resizes all internal buffers and passes
.setPixelRatio( ratio )Configures device pixel ratio
.swapBuffers()Exchanges read/write buffers
.reset( renderTarget? )Restores internal state
.dispose()Frees all GPU resources

Available Pass Types

Core Passes (ALWAYS needed)
PassImportPurpose
RenderPasspostprocessing/RenderPass.jsRenders scene to buffer; ALWAYS first
OutputPasspostprocessing/OutputPass.jsTone mapping + color space; ALWAYS last
Effect Passes
PassConstructorPurpose
UnrealBloomPass( resolution, strength?, radius?, threshold? )HDR bloom glow
SSAOPass( scene, camera, width?, height? )Screen-space ambient occlusion
GTAOPass( scene, camera, width?, height? )Ground truth AO (higher quality)
SAOPass( scene, camera )Scalable ambient obscurance
OutlinePass( resolution, scene, camera, selectedObjects? )Object selection outlines
BokehPass( scene, camera, params )Depth of field
SSRPass( params )Screen-space reflections
FilmPass( intensity?, grayscale? )Film grain / scanlines
GlitchPass( dtSize? )Digital glitch effect
HalftonePass( width, height, params )Halftone dot pattern
DotScreenPass( center?, angle?, scale? )Dot screen overlay
AfterimagePass( damp? )Motion trails / ghosting
LUTPass( params )Color LUT grading
RenderPixelatedPass( pixelSize, scene, camera )Pixelation effect
Anti-Aliasing Passes
PassConstructorQualityCost
FXAAPass()Low -- fast approximationCheapest
SMAAPass( width, height )Medium -- subpixel morphologicalModerate
SSAARenderPass( scene, camera )High -- super-samplingExpensive
TAARenderPass( scene, camera )High -- temporal accumulationExpensive
Utility Passes
PassPurpose
ShaderPassCustom GLSL shader effect
MaskPassStencil masking
ClearMaskPassClears stencil mask
ClearPassClears buffer
TexturePassRenders a texture
CubeTexturePassRenders cubemap background
SavePassSaves current buffer to render target
RenderTransitionPassAnimated transition between two scenes

ALL passes import from three/addons/postprocessing/{PassName}.js.


Key Effect Configuration

UnrealBloomPass
import { UnrealBloomPass } from 'three/addons/postprocessing/UnrealBloomPass.js';

const bloom = new UnrealBloomPass(
  new THREE.Vector2( window.innerWidth, window.innerHeight ),
  1.5,  // strength -- bloom intensity [0, 3+]
  0.4,  // radius -- bloom spread [0, 1]
  0.85  // threshold -- luminance cutoff [0, 1]
);

Tone mapping MUST be enabled on the renderer for bloom to work correctly. Selective bloom (per-object) is NOT natively supported -- use the layers system with multiple render passes as a workaround.

SSAOPass / GTAOPass

ALWAYS prefer GTAOPass over SSAOPass for production quality. Use SSAOPass only for prototyping.

// SSAOPass (faster, lower quality)
const ssao = new SSAOPass( scene, camera, width, height );
ssao.kernelRadius = 8;
ssao.minDistance = 0.005;
ssao.maxDistance = 0.1;

// GTAOPass (slower, production quality)
const gtao = new GTAOPass( scene, camera, width, height );
OutlinePass
const outline = new OutlinePass(
  new THREE.Vector2( window.innerWidth, window.innerHeight ),
  scene, camera
);
outline.selectedObjects = [ mesh1, mesh2 ];
outline.edgeStrength = 3;
outline.edgeGlow = 0;
outline.edgeThickness = 1;
outline.visibleEdgeColor.set( 0xffffff );
outline.hiddenEdgeColor.set( 0x190a05 );

Custom ShaderPass

import { ShaderPass } from 'three/addons/postprocessing/ShaderPass.js';

const myShader = {
  uniforms: {
    tDiffuse: { value: null }, // ALWAYS include -- receives read buffer
    amount: { value: 0.5 }
  },
  vertexShader: `
    varying vec2 vUv;
    void main() {
      vUv = uv;
      gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
    }
  `,
  fragmentShader: `
    uniform sampler2D tDiffuse;
    uniform float amount;
    varying vec2 vUv;
    void main() {
      vec4 color = texture2D( tDiffuse, vUv );
      gl_FragColor = mix( color, vec4( 1.0 - color.rgb, color.a ), amount );
    }
  `
};

const customPass = new ShaderPass( myShader );
composer.insertPass( customPass, 1 ); // after RenderPass

The textureID parameter defaults to 'tDiffuse'. If your shader uses a different uniform name for the input texture, pass it as the second argument: new ShaderPass( myShader, 'myInputTexture' ).


pmndrs/postprocessing

Architecture

The pmndrs/postprocessing library merges multiple effects into a SINGLE shader pass, reducing draw calls significantly compared to the built-in system.

import { EffectComposer, EffectPass, RenderPass, BloomEffect,
         SMAAEffect } from 'postprocessing';

const composer = new EffectComposer( renderer );
composer.addPass( new RenderPass( scene, camera ) );
composer.addPass( new EffectPass( camera, new BloomEffect(), new SMAAEffect() ) );

function animate() {
  composer.render();
}
Key Effects
EffectPurpose
BloomEffectConfigurable bloom with mipmaps
SMAAEffectSubpixel morphological AA
SSAOEffectScreen-space ambient occlusion
DepthOfFieldEffectBokeh depth-of-field
ToneMappingEffectTone mapping operators
VignetteEffectScreen edge darkening
ChromaticAberrationEffectColor fringing
NoiseEffectFilm grain
GodRaysEffectVolumetric light scattering

For React Three Fiber, use @react-three/postprocessing which wraps these effects as JSX components.


Resize Handling

ALWAYS resize both the renderer AND the composer on window resize:

window.addEventListener( 'resize', () => {
  camera.aspect = window.innerWidth / window.innerHeight;
  camera.updateProjectionMatrix();
  renderer.setSize( window.innerWidth, window.innerHeight );
  composer.setSize( window.innerWidth, window.innerHeight ); // MUST resize
});

WebGPU PostProcessing

The WebGPU renderer uses a SEPARATE node-based system:

import { PostProcessing } from 'three/addons/tsl/display/PostProcessing.js';

const postProcessing = new PostProcessing( renderer );
// Uses TSL (Three Shading Language) node graphs for effects

This is entirely incompatible with both the built-in WebGL EffectComposer and pmndrs/postprocessing.


Reference Links

Official Sources

Comments

Markdown supported