Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 14 additions & 13 deletions docs/effects/bloom.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,11 @@ A bloom effect.

```jsx
import { Bloom } from '@react-three/postprocessing'
import { BlurPass, Resizer, KernelSize, Resolution } from 'postprocessing'
import { KernelSize, Resolution } from 'postprocessing'

return (
<Bloom
intensity={1.0} // The bloom intensity.
blurPass={undefined} // A blur pass.
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]
Expand Down Expand Up @@ -54,14 +53,16 @@ clamp colors between 0 and 1 again.

## Props

| Name | Type | Default | Description |
| ------------------ | ------------------------------------------------------------------------------------------------------------ | -------------------- | ---------------------------------------------------------------------------------------------------- |
| luminanceThreshold | Number | 0.9 | The luminance threshold. Raise this value to mask out darker elements in the scene. Range is [0, 1]. |
| luminanceSmoothing | Number | 0.025 | Controls the smoothness of the luminance threshold. Range is [0, 1]. |
| blendFunction | BlendFunction | BlendFunction.SCREEN | The blend function of this effect. |
| intensity | Number | 1 | The intensity. |
| resolutionX | Number | Resizer.AUTO_SIZE | The render width. |
| resolutionY | Number | Resizer.AUTO_SIZE | The render height. |
| kernelSize | Number | KernelSize.LARGE | The blur kernel size. |
| blurPass | [BlurPass](https://vanruesc.github.io/postprocessing/public/docs/class/src/passes/BlurPass.js~BlurPass.html) | null | An efficient, incremental blur pass. |
| mipMap | Boolean |
| Name | Type | Default | Description |
| ------------------ | ------------- | -------------------- | ---------------------------------------------------------------------------------------------------- |
| luminanceThreshold | Number | 0.9 | The luminance threshold. Raise this value to mask out darker elements in the scene. Range is [0, 1]. |
| luminanceSmoothing | Number | 0.025 | Controls the smoothness of the luminance threshold. Range is [0, 1]. |
| blendFunction | BlendFunction | BlendFunction.ADD | The blend function of this effect. |
| intensity | Number | 1 | The intensity. |
| resolutionX | Number | Resolution.AUTO_SIZE | The render width. |
| resolutionY | Number | Resolution.AUTO_SIZE | The render height. |
| kernelSize | Number | KernelSize.LARGE | The blur kernel size. |
| mipmapBlur | Boolean | true | Enables mipmap blur. |
| radius | Number | 0.85 | The mipmap blur radius. Only used with mipmapBlur. |
| levels | Number | 8 | The number of mipmap blur levels. Only used with mipmapBlur. |
| opacity | Number | 1 | The opacity of this effect. |
14 changes: 7 additions & 7 deletions docs/effects/grid.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,10 @@ return (

## Props

| Name | Type | Default | Description |
| ------------- | ------------- | -------------------- | ---------------------------------- |
| blendFunction | BlendFunction | BlendFunction.NORMAL | The blend function of this effect. |
| scale | Number | 1 | The scale of the grid pattern. |
| lineWidth | Number | 0 | The blend function of this effect. |
| width | Number | | Overrides the default pass width |
| height | Number | | Overrides the default pass height |
| Name | Type | Default | Description |
| ------------- | ----------------------------------- | --------------------- | -------------------------------------------- |
| blendFunction | BlendFunction | BlendFunction.OVERLAY | The blend function of this effect. |
| scale | Number | 1 | The scale of the grid pattern. |
| lineWidth | Number | 0 | The line width of the grid pattern. |
| size | `{ width: Number, height: Number }` | | Overrides the default pass width and height. |
| opacity | Number | 1 | The opacity of this effect. |
2 changes: 1 addition & 1 deletion src/EffectComposer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export type EffectComposerProps = {
enabled?: boolean
children: ReactNode
depthBuffer?: boolean
/** Only used for SSGI currently, leave it disabled for everything else unless it's needed */
/** Required by SSAO, leave it disabled otherwise. */
enableNormalPass?: boolean
stencilBuffer?: boolean
autoClear?: boolean
Expand Down
7 changes: 5 additions & 2 deletions src/effects/DepthOfField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@ import { applyPierced, readPierced, useDispose, useLiveDefaults } from '../util'
export type DepthOfFieldProps = ConstructorParameters<typeof DepthOfFieldEffect>[1] &
Partial<{
ref: Ref<DepthOfFieldEffect>
opacity: number
target: ReactThreeFiber.Vector3
depthTexture: {
texture: Texture
// TODO: narrow to DepthPackingStrategies
packing: number
}
// TODO: not used
/** @deprecated Has no effect. */
blur: number
}>

Expand All @@ -26,6 +26,7 @@ export type DepthOfFieldProps = ConstructorParameters<typeof DepthOfFieldEffect>
// (needs `new Effect()` to work with zero args).
const LIVE_KEYS = [
'blendMode-blendFunction',
'blendMode-opacity-value',
'bokehScale',
'cocMaterial-focusDistance',
'cocMaterial-focusRange',
Expand Down Expand Up @@ -53,6 +54,7 @@ function set(effect: DepthOfFieldEffect, key: string, value: unknown): void {
export function DepthOfField({
ref,
blendFunction,
opacity,
worldFocusDistance,
worldFocusRange,
focusDistance,
Expand Down Expand Up @@ -93,6 +95,7 @@ export function DepthOfField({
effect,
{
'blendMode-blendFunction': blendFunction,
'blendMode-opacity-value': opacity,
bokehScale,
'cocMaterial-focusDistance': focusDistance,
'cocMaterial-focusRange': focusRange,
Expand Down
12 changes: 9 additions & 3 deletions src/effects/FXAA.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
import { FXAAEffect } from 'postprocessing'
import { createEffectComponent, type EffectOptions } from '../createEffectComponent'

export const FXAA = /* @__PURE__ */ createEffectComponent<typeof FXAAEffect, EffectOptions<typeof FXAAEffect>>(
FXAAEffect
)
// Not constructor options, but live setters on FXAAEffect
export type FXAAProps = EffectOptions<typeof FXAAEffect> & {
minEdgeThreshold?: number
maxEdgeThreshold?: number
subpixelQuality?: number
samples?: number
}

export const FXAA = /* @__PURE__ */ createEffectComponent<typeof FXAAEffect, FXAAProps>(FXAAEffect)
6 changes: 5 additions & 1 deletion src/effects/GodRays.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ import { Mesh, Points } from 'three'
import { EffectComposerContext } from '../EffectComposer'
import { applyPierced, readPierced, resolveRef, useDispose, useLiveDefaults } from '../util'

type GodRaysProps = ConstructorParameters<typeof GodRaysEffect>[2] & {
export type GodRaysProps = ConstructorParameters<typeof GodRaysEffect>[2] & {
sun: Mesh | Points | RefObject<Mesh | Points>
opacity?: number
ref?: Ref<GodRaysEffect>
}

Expand All @@ -18,6 +19,7 @@ type GodRaysProps = ConstructorParameters<typeof GodRaysEffect>[2] & {
// with zero args).
const LIVE_KEYS = [
'blendMode-blendFunction',
'blendMode-opacity-value',
'godRaysMaterial-density',
'godRaysMaterial-decay',
'godRaysMaterial-weight',
Expand All @@ -42,6 +44,7 @@ function set(effect: GodRaysEffect, key: string, value: unknown): void {
export function GodRays({
sun,
blendFunction,
opacity,
density,
decay,
weight,
Expand Down Expand Up @@ -82,6 +85,7 @@ export function GodRays({
effect,
{
'blendMode-blendFunction': blendFunction,
'blendMode-opacity-value': opacity,
'godRaysMaterial-density': density,
'godRaysMaterial-decay': decay,
'godRaysMaterial-weight': weight,
Expand Down
11 changes: 8 additions & 3 deletions src/effects/LUT.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,23 @@ export type LUTProps = {
lut: Texture
blendFunction?: BlendFunction
tetrahedralInterpolation?: boolean
opacity?: number
ref?: Ref<LUT3DEffect>
}

const LIVE_KEYS = ['blendMode-blendFunction', 'lut', 'tetrahedralInterpolation']
const LIVE_KEYS = ['blendMode-blendFunction', 'blendMode-opacity-value', 'lut', 'tetrahedralInterpolation']

// lut is LUT3DEffect's required constructor arg (no default) - only used
// for the initial instance, later changes go through its own live setter
// (via useLiveDefaults below) instead of reconstructing.
export function LUT({ lut, blendFunction, tetrahedralInterpolation, ref }: LUTProps) {
export function LUT({ lut, blendFunction, opacity, tetrahedralInterpolation, ref }: LUTProps) {
const effect = useMemo(() => new LUT3DEffect(lut), [])

useLiveDefaults(effect, { 'blendMode-blendFunction': blendFunction, lut, tetrahedralInterpolation }, LIVE_KEYS)
useLiveDefaults(
effect,
{ 'blendMode-blendFunction': blendFunction, 'blendMode-opacity-value': opacity, lut, tetrahedralInterpolation },
LIVE_KEYS
)
useDispose(effect)

return <primitive ref={ref} object={effect} />
Expand Down
2 changes: 1 addition & 1 deletion src/effects/LensFlare.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -630,7 +630,7 @@ export class LensFlareEffect extends Effect {
}
}

type LensFlareProps = Omit<Partial<LensFlareEffectOptions>, 'lensPosition'> & {
export type LensFlareProps = Omit<Partial<LensFlareEffectOptions>, 'lensPosition'> & {
/** Position of the effect */
lensPosition?: ReactThreeFiber.Vector3
/** The time that it takes to fade the occlusion */
Expand Down
8 changes: 7 additions & 1 deletion src/effects/Outline.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export type OutlineProps = Omit<OutlineEffectOptions, 'visibleEdgeColor' | 'hidd
selectionLayer: number
visibleEdgeColor: ColorRepresentation
hiddenEdgeColor: ColorRepresentation
opacity: number
ref?: Ref<OutlineEffect>
}>

Expand All @@ -38,6 +39,7 @@ const LIVE_KEYS = [
'xRay',
'dithering',
'blendMode-blendFunction',
'blendMode-opacity-value',
]

// The setter stores whatever it's given as-is, unlike the constructor -
Expand All @@ -51,6 +53,7 @@ export function Outline({
selection = EMPTY_ARRAY,
selectionLayer = 10,
blendFunction,
opacity,
resolutionScale,
resolutionX,
resolutionY,
Expand All @@ -72,7 +75,10 @@ export function Outline({

useLiveDefaults(
effect,
{ ...liveProps, 'blendMode-blendFunction': blendFunction } as Record<string, unknown>,
{ ...liveProps, 'blendMode-blendFunction': blendFunction, 'blendMode-opacity-value': opacity } as Record<
string,
unknown
>,
LIVE_KEYS,
readPierced,
set
Expand Down
4 changes: 2 additions & 2 deletions src/effects/SMAA.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { SMAAEffect } from 'postprocessing'
import { BlendFunction, SMAAEffect } from 'postprocessing'
import type { Ref } from 'react'
import { useMemo } from 'react'
import { createEffectComponent, type EffectOptions } from '../createEffectComponent'

type SMAAOptions = EffectOptions<typeof SMAAEffect>
type SMAAOptions = EffectOptions<typeof SMAAEffect> & { blendFunction?: BlendFunction }

const SMAAImpl = /* @__PURE__ */ createEffectComponent<typeof SMAAEffect, SMAAOptions>(SMAAEffect)

Expand Down
13 changes: 12 additions & 1 deletion src/effects/SSAO.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,18 @@
import { BlendFunction, SSAOEffect } from 'postprocessing'
import { Ref, use, useMemo } from 'react'
import type { ColorRepresentation, Texture } from 'three'
import { EffectComposerContext } from '../EffectComposer'
import { applyPierced, readPierced, useDispose, useLiveDefaults } from '../util'

// first two args are camera and texture
type SSAOProps = ConstructorParameters<typeof SSAOEffect>[2] & { ref?: Ref<SSAOEffect> }
export type SSAOProps = Omit<NonNullable<ConstructorParameters<typeof SSAOEffect>[2]>, 'normalDepthBuffer' | 'color'> & {
/** Tints the occlusion. null disables it. */
color?: ColorRepresentation | null
/** @deprecated Ignored, the EffectComposer's normal pass provides it. */
normalDepthBuffer?: Texture
opacity?: number
ref?: Ref<SSAOEffect>
}

// Only resolutionScale/resolutionX/resolutionY/width/height and
// normalDepthBuffer have no live setter in postprocessing - everything else
Expand All @@ -15,6 +23,7 @@ type SSAOProps = ConstructorParameters<typeof SSAOEffect>[2] & { ref?: Ref<SSAOE
// createEffectComponent (needs `new Effect()` to work with zero args).
const LIVE_KEYS = [
'blendMode-blendFunction',
'blendMode-opacity-value',
'normalBuffer',
'samples',
'rings',
Expand Down Expand Up @@ -50,6 +59,7 @@ function set(effect: SSAOEffect, key: string, value: unknown): void {

export function SSAO({
blendFunction = BlendFunction.MULTIPLY,
opacity,
samples = 30,
rings = 4,
distanceThreshold = 1.0,
Expand Down Expand Up @@ -122,6 +132,7 @@ export function SSAO({
effect instanceof SSAOEffect ? effect : null,
{
'blendMode-blendFunction': blendFunction,
'blendMode-opacity-value': opacity,
samples,
rings,
radius,
Expand Down
20 changes: 18 additions & 2 deletions src/effects/SelectiveBloom.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export type SelectiveBloomProps = BloomEffectOptions &
selectionLayer: number
inverted: boolean
ignoreBackground: boolean
opacity: number
ref?: Ref<SelectiveBloomEffect>
}>

Expand All @@ -26,12 +27,23 @@ const removeLight = (light: Object3D, effect: SelectiveBloomEffect) => light.lay
// radius/levels/resolution* are construction-only in postprocessing itself.
// scene/camera being required constructor args also rules out
// createEffectComponent (needs `new Effect()` to work with zero args).
const LIVE_KEYS = ['width', 'height', 'kernelSize', 'intensity', 'inverted', 'ignoreBackground']
const LIVE_KEYS = [
'width',
'height',
'kernelSize',
'intensity',
'inverted',
'ignoreBackground',
'blendMode-blendFunction',
'blendMode-opacity-value',
]

export function SelectiveBloom({
selection = EMPTY_ARRAY,
selectionLayer = 10,
lights = EMPTY_ARRAY,
blendFunction = BlendFunction.ADD,
opacity,
luminanceThreshold,
luminanceSmoothing,
mipmapBlur,
Expand Down Expand Up @@ -63,7 +75,11 @@ export function SelectiveBloom({
[scene, camera, luminanceThreshold, luminanceSmoothing, mipmapBlur, radius, levels, resolutionScale, resolutionX, resolutionY]
)

useLiveDefaults(effect, liveProps as Record<string, unknown>, LIVE_KEYS)
useLiveDefaults(
effect,
{ ...liveProps, 'blendMode-blendFunction': blendFunction, 'blendMode-opacity-value': opacity } as Record<string, unknown>,
LIVE_KEYS
)

// Must run before the lights effect below: addLight/removeLight read
// effect.selection.layer live, so it needs to already reflect the
Expand Down
5 changes: 1 addition & 4 deletions src/tests/DepthOfField.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -95,10 +95,7 @@ describe('DepthOfField', () => {
await React.act(async () => render())
await flush()
expect(ref.current).toBe(first)
// Reverts to no manually-provided depth texture (undefined), the state
// useLiveDefaults captured as this instance's default on first apply -
// not whatever EffectComposer's own depth-attribute auto-wiring later
// assigns, which runs separately and after this.
// Reverts to the default captured on first apply, not the composer's later depth wiring
expect(currentDepthBuffer()).toBeUndefined()

await React.act(async () => root.render(null))
Expand Down
21 changes: 4 additions & 17 deletions src/tests/DepthPicking.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,7 @@ describe('DepthPicking', () => {
await React.act(async () => root.render(null))
})

// Depth reads are position-independent (postprocessing's stable depth
// texture is populated once per frame off RenderPass, not off wherever
// DepthPicking's own pass happens to land) - verified against real
// geometry in test-env, not just structurally here. enableNormalPass adds
// a NormalPass right after RenderPass too, shifting what "index 1" used
// to mean back when this was added at a fixed index.
// enableNormalPass adds a NormalPass after RenderPass, shifting pass indices
it('still keeps its trailing CopyPass owning renderToScreen with enableNormalPass and other effects around it', async () => {
const composerRef = React.createRef<EffectComposerImpl>()

Expand Down Expand Up @@ -76,13 +71,8 @@ describe('DepthPicking', () => {
await React.act(async () => root.render(null))
})

// DepthPickingPass.render() is conditional on a pending readDepth() call -
// unlike a normal pass, a frame with nothing pending renders nothing at
// all. If it ever owned renderToScreen (e.g. as the structurally-last
// pass with no other effects present), those frames would leave the
// screen showing whatever was already in the framebuffer. Its own
// trailing CopyPass (always unconditional) must own renderToScreen
// instead, even with no other effects around.
// DepthPickingPass renders nothing without a pending readDepth(), so it must never
// own renderToScreen - its always-running CopyPass does instead
it('never owns renderToScreen even with no other effects - its own trailing CopyPass does instead', async () => {
const composerRef = React.createRef<EffectComposerImpl>()

Expand Down Expand Up @@ -189,10 +179,7 @@ describe('useDepthPicking', () => {
expect((hit as THREE.Vector3).toArray()).toEqual(expected.toArray())
})

// The point of taking `pass` as a plain ref (rather than reading
// DepthPicking's own context) - the hook itself never touches
// EffectComposerContext, so it works from anywhere under <Canvas>, not
// just from inside the <EffectComposer> the pass happens to live in.
// The hook takes a plain ref, not EffectComposerContext, so it works anywhere under <Canvas>
it('works when called outside the <EffectComposer> the pass is mounted in', async () => {
const fakePass: DepthPickingApi = { readDepth: async () => 0.5 }
const passRef = { current: fakePass }
Expand Down
Loading
Loading