@react-three/postprocessing

repository·master·Indexed 23 days ago

https://github.com/pmndrs/react-postprocessing

A React wrapper for the postprocessing library designed specifically for @react-three/fiber. It provides a declarative component-based syntax to add visual effects such as Bloom, Depth of Field, Noise, and Vignette to 3D scenes. The library utilizes an EffectComposer to manage the effects chain, offering performance optimizations like EffectPass for merging effects and single-triangle screen filling to improve GPU rasterization.

Tokens
16.6K
Snippets
36
Records
82
Agent score
76%

What's inside @react-three/postprocessing

  1. Why use react-postprocessing instead of three/examples/jsm/postprocessing

    master

    While Three.js provides built-in post-processing examples, react-postprocessing (via the underlying postprocessing library) offers several advantages:

    • Performance: Uses an EffectPass to merge effects, reducing render operations.
    • GPU Optimization: Uses a single triangle to fill the screen, which is more efficient for modern GPU rasterization and GPGPU passes than the traditional quad approach.
    • Blending: Every effect can choose its own blend function.
    • Anti-aliasing: Supports WebGL2 MSAA (multi-sample anti-aliasing) by default for high-performance, crisp results without jagged edges.
    • Gamma Correction: Supports gamma correction out of the box.
  2. Configure materials for LensFlare glass-like behavior

    master

    The LensFlare effect uses a raycaster to identify glass-like materials (specifically MeshTransmissionMaterial or MeshPhysicalMaterial). For an object to be recognized as glass and affect the flare, its material must meet one of these criteria:

    1. Have transmission = 1.
    2. Have transparent = true AND an opacity value (the effect uses this opacity number to determine the brightness of the flare).
  3. Why use react-postprocessing instead of standard Three.js post-processing

    master

    react-postprocessing is a wrapper for the postprocessing library. It offers several advantages over traditional Three.js pass chaining:

    • Automatic Optimization: The EffectPass automatically organizes and merges combinations of effects, minimizing render operations and reducing performance penalties.
    • Efficient Rendering: Fullscreen render operations use a single triangle to fill the screen instead of a quad, which aligns better with modern GPU rasterization and eliminates unnecessary fragment calculations (especially beneficial for GPGPU passes).
    • Built-in Features: Supports gamma correction out of the box and uses WebGL2 MSAA by default for high-quality anti-aliasing without jagged edges.
    • Flexible Blending: Every effect can choose its own blend function.
  4. Create a new custom effect by extending the Effect class

    master

    If you need an effect that is not provided by the postprocessing library, you can create one by extending the Effect class.

    1. Implement the Class: Create a class that extends Effect. Pass a unique name and your fragment shader to super(). Define your uniforms using a Map of three Uniform objects.
    2. Implement update: Use the update(renderer, inputBuffer, deltaTime) method to update uniform values every frame.
    3. Wrap in a Component: Create a React component using forwardRef and useMemo that returns the effect instance via a <primitive /> component.
    import React, { forwardRef, useMemo } from 'react'
    import { Uniform } from 'three'
    import { Effect } from 'postprocessing'
    
    const fragmentShader = `some_shader_code`
    
    let _uParam
    
    // Effect implementation
    class MyCustomEffectImpl extends Effect {
      constructor({ param = 0.1 } = {}) {
        super('MyCustomEffect', fragmentShader, {
          uniforms: new Map([['param', new Uniform(param)]]),
        })
    
        _uParam = param
      }
    
      update(renderer, inputBuffer, deltaTime) {
        this.uniforms.get('param').value = _uParam
      }
    }
    
    // Effect component
    export const MyCustomEffect = forwardRef(({ param }, ref) => {
      const effect = useMemo(() => new MyCustomEffectImpl(param), [param])
      return <primitive ref={ref} object={effect} dispose={null} />
    })
  5. Configure Bloom to glow specific materials

    master

    To achieve a selective bloom where only specific objects glow, follow these two requirements:

    1. Set toneMapped={false} on the material. If toneMapped is true, colors will be clamped between 0 and 1, preventing them from exceeding the luminanceThreshold.
    2. Exceed the threshold: Use emissive properties or color arrays with values greater than 1 (e.g., [2, 0, 0]).

    Examples

    Correct usage (will glow):

    // Using emissive intensity
    <meshStandardMaterial emissive="red" emissiveIntensity={2} toneMapped={false} />
    
    // Using color values > 1
    <meshBasicMaterial color={[2,0,0]} toneMapped={false} />

    Incorrect usage (will NOT glow):

    // Clamped by tone-mapping
    <meshBasicMaterial color={[2,0,0]} />
    
    // Standard color is within 0-1 range
    <meshStandardMaterial color="red"/>
    <Bloom mipmapBlur luminanceThreshold={1} />
  6. Use SMAA (Subpixel Morphological Antialiasing)

    master

    SMAA is an alternative to native WebGL2 multisampling (MSAA). Use SMAA if you are working with WebGL1 exclusively or if you encounter visual artefacts when using MSAA with certain effects.

    Important Requirements:

    1. Suspense: The SMAA effect is asynchronous and must be wrapped in a <Suspense> component.
    2. Disable MSAA: To avoid conflicts or redundant processing, you should disable native multisampling by setting multisampling={0} on the <EffectComposer>.
    import React, { Suspense } from 'react'
    import { EffectComposer, SMAA } from '@react-three/postprocessing'
    
    return (
      <Suspense fallback={null}>
        <EffectComposer multisampling={0}>
          <SMAA />
        </EffectComposer>
      </Suspense>
    )
  7. How to use EffectComposer and effects in React Three Fiber

    master

    To apply post-processing effects to your scene, wrap your desired effects inside an <EffectComposer /> component within your <Canvas />. The <EffectComposer /> manages the effect chain and optimizes rendering by merging effects where possible.

    Commonly used effects include <Bloom />, <DepthOfField />, <Noise />, and <Vignette />.

    Note: This library uses WebGL2 MSAA (multi-sample anti-aliasing) by default for high-performance, crisp results.

    import React from 'react'
    import { Bloom, DepthOfField, EffectComposer, Noise, Vignette } from '@react-three/postprocessing'
    import { Canvas } from '@react-three/fiber'
    
    function App() {
      return (
        <Canvas>
          {/* Your regular scene contents go here, like always ... */}
          <EffectComposer>
            <DepthOfField focusDistance={0} focalLength={0.02} bokehScale={2} height={480} />
            <Bloom luminanceThreshold={0} luminanceSmoothing={0.9} height={300} />
            <Noise opacity={0.02} />
            <Vignette eskil={false} offset={0.1} darkness={1.1} />
          </EffectComposer>
        </Canvas>
      )
    }
  8. Use EffectComposer to chain effects

    master

    To apply post-processing effects to your scene, wrap your desired effects inside an <EffectComposer /> component within your <Canvas />. The EffectComposer acts as a wrapper that manages the effects chain.

    This approach is more efficient than traditional pass chaining because it uses an EffectPass that automatically organizes and merges effects, minimizing render operations. It also uses a single triangle to fill the screen instead of a quad, which optimizes GPU rasterization and fragment shader performance.

    import React from 'react'
    import { Bloom, DepthOfField, EffectComposer, Noise, Vignette } from '@react-three/postprocessing'
    import { Canvas } from '@react-three/fiber'
    
    function App() {
      return (
        <Canvas>
          {/* Your regular scene contents go here, like always ... */}
          <EffectComposer>
            <DepthOfField focusDistance={0} focalLength={0.02} bokehScale={2} height={480} />
            <Bloom luminanceThreshold={0} luminanceSmoothing={0.9} height={300} />
            <Noise opacity={0.02} />
            <Vignette eskil={false} offset={0.1} darkness={1.1} />
          </EffectComposer>
        </Canvas>
      )
    }
  9. Use the SelectiveBloom effect

    master

    The SelectiveBloom effect applies bloom only to a specific subset of objects in your scene. This is useful when you want to prevent the entire scene from glowing and only want certain elements to bloom.

    Performance Note: If you do not need to limit bloom to specific objects, use the BloomEffect instead, as it is more performant.

    To use SelectiveBloom, you must provide both a lights array (containing all lights that affect the effect) and a selection array (containing the objects that should bloom). You can also use a selectionLayer to define which layer the selected objects reside on.

    import { SelectiveBloom } from '@react-three/postprocessing'
    import { BlurPass, Resizer, KernelSize } from 'postprocessing'
    
    return (
      <SelectiveBloom
        lights={[lightRef1, lightRef2]} // ⚠️ REQUIRED! all relevant lights
        selection={[meshRef1, meshRef2]} // selection of objects that will have bloom effect
        selectionLayer={10} // selection layer
        intensity={1.0} // The bloom intensity.
        blurPass={undefined} // A blur pass.
        width={Resizer.AUTO_SIZE} // render width
        height={Resizer.AUTO_SIZE} // render height
        kernelSize={KernelSize.LARGE} // blur kernel size
        luminanceThreshold={0.9} // luminance threshold. Raise this value to mask out darker elements in the scene.
        luminanceSmoothing={0.025} // smoothness of the luminance threshold. Range is [0, 1]
      />
    )