Custom effects

If you plan to use custom effects, make sure to expose the effect itself as a primitive!

r3f never disposes objects rendered via <primitive object={...}> (their state may be owned outside React), so effects rendered this way must dispose themselves - use the useDispose hook exported by this library for that.

import { useMemo } from 'react'
import { PixelationEffect } from 'postprocessing'
import { useDispose } from '@react-three/postprocessing'

export function Pixelation({ granularity = 5, ref }) {
  const effect = useMemo(() => new PixelationEffect(granularity), [granularity])
  useDispose(effect)
  return <primitive ref={ref} object={effect} />
}

For effects that aren't present in postprocessing you should extend the Effect class:

import { useMemo } from 'react'
import { Uniform } from 'three'
import { Effect } from 'postprocessing'
import { useDispose } from '@react-three/postprocessing'

const fragmentShader = `some_shader_code`

// Effect implementation
class MyCustomEffectImpl extends Effect {
  constructor({ param = 0.1 } = {}) {
    super('MyCustomEffect', fragmentShader, {
      uniforms: new Map([['param', new Uniform(param)]]),
    })
  }

  update(renderer, inputBuffer, deltaTime) {
    // read/write per-frame state on `this` (e.g. this.uniforms.get('param').value = ...),
    // never on a module-level variable - that would be shared across every instance
  }
}

// Effect component
export function MyCustomEffect({ param, ref }) {
  const effect = useMemo(() => new MyCustomEffectImpl({ param }), [param])
  useDispose(effect)
  return <primitive ref={ref} object={effect} />
}