--- name: postfx-composer description: EffectComposer setup and architecture for Three.js post-processing pipelines. Use when setting up multi-pass rendering, combining effects, creating custom passes, managing render targets, or building reusable effect stacks. Foundation skill for all post-processing work. --- # Post-Processing Composer EffectComposer architecture, render pipelines, and custom pass creation. ## Quick Start ```bash npm install @react-three/postprocessing postprocessing three ``` ```tsx import { Canvas } from '@react-three/fiber'; import { EffectComposer, Bloom, Vignette } from '@react-three/postprocessing'; function App() { return ( ); } ``` ## Core Concepts ### Render Pipeline Flow ``` Scene Render → EffectComposer → Effect 1 → Effect 2 → ... → Screen ↓ [Render Target A] → [Render Target B] → [Screen] ``` ### EffectComposer Configuration ```tsx import { EffectComposer } from '@react-three/postprocessing'; import { HalfFloatType } from 'three'; function PostProcessing() { return ( {/* Effects processed in order */} ); } ``` ## Effect Ordering ### Recommended Order ```tsx {/* 1. Scene modification (needs scene data) */} {/* 2. Lighting/color effects */} {/* 3. Color grading */} {/* 4. Screen-space effects */} {/* 5. Noise/grain (always last) */} ``` ### Why Order Matters ```tsx // WRONG: Bloom after color grading reduces glow // CORRECT: Bloom before color grading ``` ## Conditional Effects ### Toggle Effects at Runtime ```tsx function DynamicEffects({ enableBloom, enableDOF }) { return ( {enableBloom && } {enableDOF && } ); } ``` ### Effect Presets ```tsx const PRESETS = { cinematic: { bloom: { intensity: 0.8, threshold: 0.3 }, vignette: { darkness: 0.5, offset: 0.3 }, grain: { opacity: 0.03 } }, scifi: { bloom: { intensity: 2.0, threshold: 0.1 }, chromatic: { offset: [0.003, 0.003] }, vignette: { darkness: 0.6, offset: 0.2 } }, minimal: { vignette: { darkness: 0.3, offset: 0.4 } } }; function PresetEffects({ preset = 'cinematic' }) { const config = PRESETS[preset]; return ( {config.bloom && } {config.chromatic && } {config.vignette && } {config.grain && } ); } ``` ## Custom Effects ### Basic Custom Effect ```tsx import { Effect } from 'postprocessing'; import { Uniform } from 'three'; import { forwardRef, useMemo } from 'react'; class ColorShiftEffect extends Effect { constructor({ shift = 0.0 }) { super('ColorShiftEffect', /* glsl */` uniform float shift; void mainImage(const in vec4 inputColor, const in vec2 uv, out vec4 outputColor) { vec3 color = inputColor.rgb; float hueShift = shift * uv.x; color.r = color.r * cos(hueShift) - color.g * sin(hueShift); color.g = color.r * sin(hueShift) + color.g * cos(hueShift); outputColor = vec4(color, inputColor.a); } `, { uniforms: new Map([['shift', new Uniform(shift)]]) }); } set shift(value) { this.uniforms.get('shift').value = value; } } const ColorShift = forwardRef(({ shift = 0 }, ref) => { const effect = useMemo(() => new ColorShiftEffect({ shift }), []); useEffect(() => { effect.shift = shift; }, [shift, effect]); return ; }); ``` ### Custom Effect with Time ```tsx class PulseEffect extends Effect { constructor() { super('PulseEffect', /* glsl */` uniform float uTime; uniform float uIntensity; void mainImage(const in vec4 inputColor, const in vec2 uv, out vec4 outputColor) { float pulse = sin(uTime * 2.0) * 0.5 + 0.5; float vignette = 1.0 - length(uv - 0.5) * pulse * uIntensity; outputColor = vec4(inputColor.rgb * vignette, inputColor.a); } `, { uniforms: new Map([ ['uTime', new Uniform(0)], ['uIntensity', new Uniform(0.5)] ]) }); } update(renderer, inputBuffer, deltaTime) { this.uniforms.get('uTime').value += deltaTime; } } ``` ## Render Targets ### Manual Render Target Management ```tsx import { useFBO } from '@react-three/drei'; function CustomRenderPipeline() { const { gl, scene, camera } = useThree(); const targetA = useFBO({ width: 1024, height: 1024 }); useFrame(() => { gl.setRenderTarget(targetA); gl.render(scene, camera); gl.setRenderTarget(null); gl.render(scene, camera); }); return null; } ``` ## Performance Patterns ### Resolution Scaling ```tsx function ScaledEffects() { const { size } = useThree(); const scale = 0.5; return ( ); } ``` ### Adaptive Quality ```tsx import { useDetectGPU } from '@react-three/drei'; function AdaptiveQuality() { const { tier } = useDetectGPU(); const quality = useMemo(() => { if (tier >= 3) return { multisampling: 8, bloomLevels: 8 }; if (tier >= 2) return { multisampling: 4, bloomLevels: 5 }; return { multisampling: 0, bloomLevels: 3 }; }, [tier]); return ( ); } ``` ## Temporal Collapse Setup ```tsx import { EffectComposer, Bloom, ChromaticAberration, Vignette, Noise, ToneMapping } from '@react-three/postprocessing'; import { ToneMappingMode, BlendFunction } from 'postprocessing'; import { HalfFloatType } from 'three'; function TemporalCollapseComposer({ children }) { return ( {children} ); } ``` ## Reference - See `postfx-bloom` for bloom-specific techniques - See `postfx-effects` for individual effect configurations - See `shaders-glsl` for writing custom effect shaders