@react-three/postprocessing 3.0.5 → 3.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (106) hide show
  1. package/README.md +6 -7
  2. package/dist/EffectComposer.d.ts +9 -2
  3. package/dist/EffectGroup.d.ts +9 -0
  4. package/dist/createEffectComponent.d.ts +30 -0
  5. package/dist/effects/ASCII.d.ts +28 -7
  6. package/dist/effects/Autofocus.d.ts +1 -1
  7. package/dist/effects/Bloom.d.ts +9 -1
  8. package/dist/effects/BrightnessContrast.d.ts +14 -1
  9. package/dist/effects/ChromaticAberration.d.ts +26 -3
  10. package/dist/effects/ColorAverage.d.ts +7 -7
  11. package/dist/effects/ColorDepth.d.ts +7 -1
  12. package/dist/effects/Depth.d.ts +12 -1
  13. package/dist/effects/DotScreen.d.ts +14 -1
  14. package/dist/effects/FXAA.d.ts +10 -1
  15. package/dist/effects/Glitch.d.ts +16 -11
  16. package/dist/effects/GodRays.d.ts +1 -1
  17. package/dist/effects/Grid.d.ts +7 -6
  18. package/dist/effects/HueSaturation.d.ts +14 -1
  19. package/dist/effects/LUT.d.ts +1 -1
  20. package/dist/effects/LensFlare.d.ts +46 -5
  21. package/dist/effects/Noise.d.ts +7 -1
  22. package/dist/effects/Outline.d.ts +6 -3
  23. package/dist/effects/Pixelation.d.ts +1 -1
  24. package/dist/effects/Ramp.d.ts +58 -6
  25. package/dist/effects/SMAA.d.ts +9 -1
  26. package/dist/effects/SSAO.d.ts +1 -1
  27. package/dist/effects/ScanlineEffect.d.ts +12 -1
  28. package/dist/effects/SelectiveBloom.d.ts +1 -1
  29. package/dist/effects/Sepia.d.ts +12 -1
  30. package/dist/effects/ShockWave.d.ts +11 -1
  31. package/dist/effects/Texture.d.ts +4 -4
  32. package/dist/effects/TiltShift.d.ts +9 -1
  33. package/dist/effects/TiltShift2.d.ts +44 -4
  34. package/dist/effects/ToneMapping.d.ts +9 -3
  35. package/dist/effects/Vignette.d.ts +18 -1
  36. package/dist/effects/Water.d.ts +17 -1
  37. package/dist/index.d.ts +4 -1
  38. package/dist/index.js +2205 -713
  39. package/dist/index.js.map +1 -1
  40. package/dist/passes/DepthPicking.d.ts +11 -0
  41. package/dist/{effects → passes}/N8AO.d.ts +2 -1
  42. package/dist/util.d.ts +9 -15
  43. package/dist/wrapEffect.d.ts +3 -3
  44. package/package.json +6 -5
  45. package/src/EffectComposer.tsx +143 -42
  46. package/src/EffectGroup.tsx +74 -0
  47. package/src/createEffectComponent.tsx +71 -0
  48. package/src/effects/ASCII.tsx +75 -28
  49. package/src/effects/Autofocus.tsx +24 -77
  50. package/src/effects/Bloom.tsx +32 -4
  51. package/src/effects/BrightnessContrast.tsx +5 -2
  52. package/src/effects/ChromaticAberration.tsx +13 -21
  53. package/src/effects/ColorAverage.tsx +3 -16
  54. package/src/effects/ColorDepth.tsx +23 -2
  55. package/src/effects/Depth.tsx +4 -2
  56. package/src/effects/DepthOfField.tsx +48 -26
  57. package/src/effects/DotScreen.tsx +5 -2
  58. package/src/effects/FXAA.tsx +4 -2
  59. package/src/effects/Glitch.tsx +26 -31
  60. package/src/effects/GodRays.tsx +90 -6
  61. package/src/effects/Grid.tsx +17 -18
  62. package/src/effects/HueSaturation.tsx +5 -2
  63. package/src/effects/LUT.tsx +9 -11
  64. package/src/effects/LensFlare.tsx +209 -55
  65. package/src/effects/Noise.tsx +14 -2
  66. package/src/effects/Outline.tsx +55 -56
  67. package/src/effects/Pixelation.tsx +11 -8
  68. package/src/effects/Ramp.tsx +96 -7
  69. package/src/effects/SMAA.tsx +18 -2
  70. package/src/effects/SSAO.tsx +130 -22
  71. package/src/effects/ScanlineEffect.tsx +6 -6
  72. package/src/effects/SelectiveBloom.tsx +26 -44
  73. package/src/effects/Sepia.tsx +4 -2
  74. package/src/effects/ShockWave.tsx +30 -2
  75. package/src/effects/Texture.tsx +10 -9
  76. package/src/effects/TiltShift.tsx +30 -2
  77. package/src/effects/TiltShift2.tsx +72 -6
  78. package/src/effects/ToneMapping.tsx +16 -3
  79. package/src/effects/Vignette.tsx +5 -2
  80. package/src/effects/Water.tsx +21 -6
  81. package/src/index.ts +4 -2
  82. package/src/passes/DepthPicking.tsx +63 -0
  83. package/src/{effects → passes}/N8AO.tsx +28 -4
  84. package/src/tests/Autofocus.test.tsx +38 -0
  85. package/src/tests/Bloom.test.tsx +76 -0
  86. package/src/tests/ChromaticAberration.test.tsx +50 -0
  87. package/src/tests/ColorDepth.test.tsx +71 -0
  88. package/src/tests/DepthOfField.test.tsx +130 -0
  89. package/src/tests/DepthPicking.test.tsx +250 -0
  90. package/src/tests/EffectComposer.test.tsx +501 -8
  91. package/src/tests/EffectGroup.test.tsx +337 -0
  92. package/src/tests/Glitch.test.tsx +56 -0
  93. package/src/tests/GodRays.test.tsx +101 -0
  94. package/src/tests/Grid.test.tsx +34 -0
  95. package/src/tests/LUT.test.tsx +64 -0
  96. package/src/tests/N8AO.test.tsx +92 -0
  97. package/src/tests/Outline.test.tsx +122 -0
  98. package/src/tests/SSAO.test.tsx +140 -0
  99. package/src/tests/SelectiveBloom.test.tsx +48 -0
  100. package/src/tests/ShockWave.test.tsx +95 -0
  101. package/src/tests/TiltShift.test.tsx +76 -0
  102. package/src/tests/createEffectComponent.test.tsx +286 -0
  103. package/src/tests/effects.smoke.test.tsx +46 -42
  104. package/src/util.tsx +148 -23
  105. package/src/wrapEffect.tsx +3 -2
  106. package/dist/tests/test-utils.d.ts +0 -12
@@ -1,13 +1,81 @@
1
1
  import { BlendFunction, SSAOEffect } from 'postprocessing'
2
- import { Ref, useContext, useMemo } from 'react'
2
+ import { Ref, use, useMemo } from 'react'
3
3
  import { EffectComposerContext } from '../EffectComposer'
4
- import { useDispose } from '../util'
4
+ import { applyPierced, readPierced, useDispose, useLiveDefaults } from '../util'
5
5
 
6
6
  // first two args are camera and texture
7
7
  type SSAOProps = ConstructorParameters<typeof SSAOEffect>[2] & { ref?: Ref<SSAOEffect> }
8
8
 
9
- export function SSAO({ ref, ...props }: SSAOProps) {
10
- const { camera, normalPass, downSamplingPass, resolutionScale } = useContext(EffectComposerContext)
9
+ // Only resolutionScale/resolutionX/resolutionY/width/height and
10
+ // normalDepthBuffer have no live setter in postprocessing - everything else
11
+ // either has a real accessor directly on SSAOEffect, or on the nested
12
+ // ssaoMaterial (rangeThreshold/rangeFalloff are the constructor's names for
13
+ // what ssaoMaterial exposes as proximityThreshold/proximityFalloff).
14
+ // camera+normalBuffer being required constructor args also rule out
15
+ // createEffectComponent (needs `new Effect()` to work with zero args).
16
+ const LIVE_KEYS = [
17
+ 'blendMode-blendFunction',
18
+ 'normalBuffer',
19
+ 'samples',
20
+ 'rings',
21
+ 'radius',
22
+ 'depthAwareUpsampling',
23
+ 'color',
24
+ 'luminanceInfluence',
25
+ 'intensity',
26
+ 'ssaoMaterial-bias',
27
+ 'ssaoMaterial-fade',
28
+ 'ssaoMaterial-minRadiusScale',
29
+ 'ssaoMaterial-distanceThreshold',
30
+ 'ssaoMaterial-distanceFalloff',
31
+ 'ssaoMaterial-worldDistanceThreshold',
32
+ 'ssaoMaterial-worldDistanceFalloff',
33
+ 'rangeThreshold',
34
+ 'rangeFalloff',
35
+ 'ssaoMaterial-worldProximityThreshold',
36
+ 'ssaoMaterial-worldProximityFalloff',
37
+ ]
38
+
39
+ function get(effect: SSAOEffect, key: string): unknown {
40
+ if (key === 'rangeThreshold') return effect.ssaoMaterial.proximityThreshold
41
+ if (key === 'rangeFalloff') return effect.ssaoMaterial.proximityFalloff
42
+ return readPierced(effect, key)
43
+ }
44
+
45
+ function set(effect: SSAOEffect, key: string, value: unknown): void {
46
+ if (key === 'rangeThreshold') effect.ssaoMaterial.proximityThreshold = value as number
47
+ else if (key === 'rangeFalloff') effect.ssaoMaterial.proximityFalloff = value as number
48
+ else applyPierced(effect, key, value)
49
+ }
50
+
51
+ export function SSAO({
52
+ blendFunction = BlendFunction.MULTIPLY,
53
+ samples = 30,
54
+ rings = 4,
55
+ distanceThreshold = 1.0,
56
+ distanceFalloff = 0.0,
57
+ rangeThreshold = 0.5,
58
+ rangeFalloff = 0.1,
59
+ luminanceInfluence = 0.9,
60
+ radius = 20,
61
+ bias = 0.5,
62
+ intensity = 1.0,
63
+ color,
64
+ worldDistanceThreshold,
65
+ worldDistanceFalloff,
66
+ worldProximityThreshold,
67
+ worldProximityFalloff,
68
+ minRadiusScale,
69
+ fade,
70
+ depthAwareUpsampling = true,
71
+ resolutionScale,
72
+ resolutionX,
73
+ resolutionY,
74
+ width,
75
+ height,
76
+ ref,
77
+ }: SSAOProps) {
78
+ const { camera, normalPass, downSamplingPass, resolutionScale: composerResolutionScale } = use(EffectComposerContext)
11
79
 
12
80
  const effect = useMemo<SSAOEffect | {}>(() => {
13
81
  if (normalPass === null && downSamplingPass === null) {
@@ -16,29 +84,69 @@ export function SSAO({ ref, ...props }: SSAOProps) {
16
84
  }
17
85
 
18
86
  return new SSAOEffect(camera, normalPass && !downSamplingPass ? (normalPass as any).texture : null, {
19
- blendFunction: BlendFunction.MULTIPLY,
20
- samples: 30,
21
- rings: 4,
22
- distanceThreshold: 1.0,
23
- distanceFalloff: 0.0,
24
- rangeThreshold: 0.5,
25
- rangeFalloff: 0.1,
26
- luminanceInfluence: 0.9,
27
- radius: 20,
28
- bias: 0.5,
29
- intensity: 1.0,
30
- color: undefined,
87
+ blendFunction,
88
+ samples,
89
+ rings,
90
+ distanceThreshold,
91
+ distanceFalloff,
92
+ rangeThreshold,
93
+ rangeFalloff,
94
+ luminanceInfluence,
95
+ radius,
96
+ bias,
97
+ intensity,
31
98
  // @ts-ignore
32
99
  normalDepthBuffer: downSamplingPass ? downSamplingPass.texture : null,
33
- resolutionScale: resolutionScale ?? 1,
34
- depthAwareUpsampling: true,
35
- ...props,
100
+ resolutionScale: resolutionScale ?? composerResolutionScale ?? 1,
101
+ resolutionX,
102
+ resolutionY,
103
+ width,
104
+ height,
105
+ depthAwareUpsampling,
36
106
  })
37
- // NOTE: `props` is an unstable reference, so we can't memoize it
107
+ // color/worldDistanceThreshold/worldDistanceFalloff/worldProximityThreshold/
108
+ // worldProximityFalloff/minRadiusScale/fade are deliberately left out here
109
+ // even though they're valid constructor options: they have no JS-level
110
+ // default in this component's own signature, so useLiveDefaults' first
111
+ // snapshot must see SSAOEffect's own real default for them, not whatever
112
+ // value happened to be passed on the mounting render - otherwise removing
113
+ // the prop later "resets" to that first-render value instead of the
114
+ // effect's true default. They're still applied immediately below, live.
115
+ //
116
+ // Only the genuinely construction-only options belong here - everything
117
+ // else is applied live below via useLiveDefaults instead.
38
118
  // eslint-disable-next-line react-hooks/exhaustive-deps
39
- }, [camera, downSamplingPass, normalPass, resolutionScale])
119
+ }, [camera, downSamplingPass, normalPass, resolutionScale, composerResolutionScale, resolutionX, resolutionY, width, height])
120
+
121
+ useLiveDefaults(
122
+ effect instanceof SSAOEffect ? effect : null,
123
+ {
124
+ 'blendMode-blendFunction': blendFunction,
125
+ samples,
126
+ rings,
127
+ radius,
128
+ depthAwareUpsampling,
129
+ color,
130
+ luminanceInfluence,
131
+ intensity,
132
+ 'ssaoMaterial-bias': bias,
133
+ 'ssaoMaterial-fade': fade,
134
+ 'ssaoMaterial-minRadiusScale': minRadiusScale,
135
+ 'ssaoMaterial-distanceThreshold': distanceThreshold,
136
+ 'ssaoMaterial-distanceFalloff': distanceFalloff,
137
+ 'ssaoMaterial-worldDistanceThreshold': worldDistanceThreshold,
138
+ 'ssaoMaterial-worldDistanceFalloff': worldDistanceFalloff,
139
+ rangeThreshold,
140
+ rangeFalloff,
141
+ 'ssaoMaterial-worldProximityThreshold': worldProximityThreshold,
142
+ 'ssaoMaterial-worldProximityFalloff': worldProximityFalloff,
143
+ },
144
+ LIVE_KEYS,
145
+ get,
146
+ set
147
+ )
40
148
 
41
- useDispose(effect)
149
+ useDispose(effect as SSAOEffect)
42
150
 
43
151
  return <primitive ref={ref} object={effect} />
44
152
  }
@@ -1,7 +1,7 @@
1
- import { BlendFunction, ScanlineEffect } from 'postprocessing'
2
- import { wrapEffect } from '../wrapEffect'
1
+ import { ScanlineEffect } from 'postprocessing'
2
+ import { createEffectComponent, type EffectOptions } from '../createEffectComponent'
3
3
 
4
- export const Scanline = /* @__PURE__ */ wrapEffect(ScanlineEffect, {
5
- blendFunction: BlendFunction.OVERLAY,
6
- density: 1.25,
7
- })
4
+ export const Scanline = /* @__PURE__ */ createEffectComponent<
5
+ typeof ScanlineEffect,
6
+ EffectOptions<typeof ScanlineEffect>
7
+ >(ScanlineEffect)
@@ -4,7 +4,7 @@ import { BlendFunction, SelectiveBloomEffect } from 'postprocessing'
4
4
  import { Ref, RefObject, use, useEffect, useMemo } from 'react'
5
5
  import { Object3D } from 'three'
6
6
  import { EffectComposerContext } from '../EffectComposer'
7
- import { EMPTY_ARRAY, resolveRef, useDispose, useSelectionSync } from '../util'
7
+ import { EMPTY_ARRAY, resolveRef, useDispose, useLiveDefaults, useSelectionSync } from '../util'
8
8
 
9
9
  type ObjectRef = RefObject<Object3D | null>
10
10
 
@@ -21,67 +21,49 @@ export type SelectiveBloomProps = BloomEffectOptions &
21
21
  const addLight = (light: Object3D, effect: SelectiveBloomEffect) => light.layers.enable(effect.selection.layer)
22
22
  const removeLight = (light: Object3D, effect: SelectiveBloomEffect) => light.layers.disable(effect.selection.layer)
23
23
 
24
+ // BloomEffect (which SelectiveBloomEffect extends) only exposes real
25
+ // setters for these - luminanceThreshold/luminanceSmoothing/mipmapBlur/
26
+ // radius/levels/resolution* are construction-only in postprocessing itself.
27
+ // scene/camera being required constructor args also rules out
28
+ // createEffectComponent (needs `new Effect()` to work with zero args).
29
+ const LIVE_KEYS = ['width', 'height', 'kernelSize', 'intensity', 'inverted', 'ignoreBackground']
30
+
24
31
  export function SelectiveBloom({
25
32
  selection = EMPTY_ARRAY,
26
33
  selectionLayer = 10,
27
34
  lights = EMPTY_ARRAY,
28
- inverted = false,
29
- ignoreBackground = false,
30
35
  luminanceThreshold,
31
36
  luminanceSmoothing,
32
37
  mipmapBlur,
33
- intensity,
34
38
  radius,
35
39
  levels,
36
- kernelSize,
37
40
  resolutionScale,
38
- width,
39
- height,
40
41
  resolutionX,
41
42
  resolutionY,
42
43
  ref,
44
+ ...liveProps
43
45
  }: SelectiveBloomProps) {
44
46
  const { scene, camera } = use(EffectComposerContext)
45
47
 
46
48
  const invalidate = useThree((state) => state.invalidate)
47
49
 
48
- const effect = useMemo(() => {
49
- const instance = new SelectiveBloomEffect(scene, camera, {
50
- blendFunction: BlendFunction.ADD,
51
- luminanceThreshold,
52
- luminanceSmoothing,
53
- mipmapBlur,
54
- intensity,
55
- radius,
56
- levels,
57
- kernelSize,
58
- resolutionScale,
59
- width,
60
- height,
61
- resolutionX,
62
- resolutionY,
63
- })
64
- instance.inverted = inverted
65
- instance.ignoreBackground = ignoreBackground
66
- return instance
67
- }, [
68
- scene,
69
- camera,
70
- luminanceThreshold,
71
- luminanceSmoothing,
72
- mipmapBlur,
73
- intensity,
74
- radius,
75
- levels,
76
- kernelSize,
77
- resolutionScale,
78
- width,
79
- height,
80
- resolutionX,
81
- resolutionY,
82
- inverted,
83
- ignoreBackground,
84
- ])
50
+ const effect = useMemo(
51
+ () =>
52
+ new SelectiveBloomEffect(scene, camera, {
53
+ blendFunction: BlendFunction.ADD,
54
+ luminanceThreshold,
55
+ luminanceSmoothing,
56
+ mipmapBlur,
57
+ radius,
58
+ levels,
59
+ resolutionScale,
60
+ resolutionX,
61
+ resolutionY,
62
+ }),
63
+ [scene, camera, luminanceThreshold, luminanceSmoothing, mipmapBlur, radius, levels, resolutionScale, resolutionX, resolutionY]
64
+ )
65
+
66
+ useLiveDefaults(effect, liveProps as Record<string, unknown>, LIVE_KEYS)
85
67
 
86
68
  // Must run before the lights effect below: addLight/removeLight read
87
69
  // effect.selection.layer live, so it needs to already reflect the
@@ -1,4 +1,6 @@
1
1
  import { SepiaEffect } from 'postprocessing'
2
- import { wrapEffect } from '../wrapEffect'
2
+ import { createEffectComponent, type EffectOptions } from '../createEffectComponent'
3
3
 
4
- export const Sepia = /* @__PURE__ */ wrapEffect(SepiaEffect)
4
+ export const Sepia = /* @__PURE__ */ createEffectComponent<typeof SepiaEffect, EffectOptions<typeof SepiaEffect>>(
5
+ SepiaEffect
6
+ )
@@ -1,4 +1,32 @@
1
+ import type { ReactThreeFiber } from '@react-three/fiber'
1
2
  import { ShockWaveEffect } from 'postprocessing'
2
- import { wrapEffect } from '../wrapEffect'
3
+ import { Ref, use, useMemo } from 'react'
4
+ import { EffectComposerContext } from '../EffectComposer'
5
+ import { useDispose, useLiveDefaults, useVector3 } from '../util'
3
6
 
4
- export const ShockWave = /* @__PURE__ */ wrapEffect(ShockWaveEffect)
7
+ export type ShockWaveProps = {
8
+ position?: ReactThreeFiber.Vector3
9
+ speed?: number
10
+ maxRadius?: number
11
+ waveSize?: number
12
+ amplitude?: number
13
+ ref?: Ref<ShockWaveEffect>
14
+ }
15
+
16
+ const LIVE_KEYS = ['position', 'speed', 'maxRadius', 'waveSize', 'amplitude']
17
+
18
+ // ShockWaveEffect's constructor is (camera, position, options) - camera is
19
+ // a required arg, so it can't use createEffectComponent (needs
20
+ // `new Effect()` to work with zero args). Built by hand instead, like
21
+ // Outline/GodRays.
22
+ export function ShockWave(props: ShockWaveProps) {
23
+ const { speed, maxRadius, waveSize, amplitude, ref } = props
24
+ const { camera } = use(EffectComposerContext)
25
+ const effect = useMemo(() => new ShockWaveEffect(camera), [camera])
26
+ const position = useVector3(props, 'position')
27
+
28
+ useLiveDefaults(effect, { position, speed, maxRadius, waveSize, amplitude }, LIVE_KEYS)
29
+ useDispose(effect)
30
+
31
+ return <primitive ref={ref} object={effect} />
32
+ }
@@ -1,17 +1,22 @@
1
1
  import { useLoader } from '@react-three/fiber'
2
2
  import { TextureEffect } from 'postprocessing'
3
- import { Ref, useLayoutEffect, useMemo } from 'react'
3
+ import type { Ref } from 'react'
4
+ import { useLayoutEffect } from 'react'
4
5
  import { RepeatWrapping, SRGBColorSpace, TextureLoader } from 'three'
5
- import { useDispose } from '../util'
6
+ import { createEffectComponent, type EffectOptions } from '../createEffectComponent'
6
7
 
7
- type TextureProps = ConstructorParameters<typeof TextureEffect>[0] & {
8
+ const TextureImpl = /* @__PURE__ */ createEffectComponent<typeof TextureEffect, EffectOptions<typeof TextureEffect>>(
9
+ TextureEffect
10
+ )
11
+
12
+ export type TextureProps = EffectOptions<typeof TextureEffect> & {
8
13
  textureSrc: string
9
14
  /** opacity of provided texture */
10
15
  opacity?: number
11
16
  ref?: Ref<TextureEffect>
12
17
  }
13
18
 
14
- export function Texture({ textureSrc, texture, opacity = 1, ref, ...props }: TextureProps) {
19
+ export function Texture({ textureSrc, texture, opacity = 1, ...props }: TextureProps) {
15
20
  const t = useLoader(TextureLoader, textureSrc)
16
21
 
17
22
  useLayoutEffect(() => {
@@ -19,9 +24,5 @@ export function Texture({ textureSrc, texture, opacity = 1, ref, ...props }: Tex
19
24
  t.wrapS = t.wrapT = RepeatWrapping
20
25
  }, [t])
21
26
 
22
- const effect = useMemo(() => new TextureEffect({ ...props, texture: t || texture }), [])
23
-
24
- useDispose(effect)
25
-
26
- return <primitive ref={ref} object={effect} blendMode-opacity-value={opacity} />
27
+ return <TextureImpl {...props} texture={texture ?? t} opacity={opacity} />
27
28
  }
@@ -1,4 +1,32 @@
1
1
  import { BlendFunction, TiltShiftEffect } from 'postprocessing'
2
- import { wrapEffect } from '../wrapEffect'
2
+ import type { Ref } from 'react'
3
+ import { useMemo } from 'react'
4
+ import { createEffectComponent, type EffectOptions } from '../createEffectComponent'
3
5
 
4
- export const TiltShift = /* @__PURE__ */ wrapEffect(TiltShiftEffect, { blendFunction: BlendFunction.ADD })
6
+ type TiltShiftOptions = EffectOptions<typeof TiltShiftEffect>
7
+
8
+ const TiltShiftImpl = /* @__PURE__ */ createEffectComponent<typeof TiltShiftEffect, TiltShiftOptions>(TiltShiftEffect)
9
+
10
+ export type TiltShiftProps = TiltShiftOptions & {
11
+ opacity?: number
12
+ ref?: Ref<TiltShiftEffect>
13
+ }
14
+
15
+ // kernelSize/resolutionScale/resolutionX/resolutionY have no live setter in
16
+ // postprocessing - routed through args so they still work as plain props
17
+ // (previously they were passed as plain props and silently never reached
18
+ // the effect at all, since there was no setter for diffProps to hit).
19
+ export function TiltShift({
20
+ blendFunction = BlendFunction.ADD,
21
+ kernelSize,
22
+ resolutionScale,
23
+ resolutionX,
24
+ resolutionY,
25
+ ...liveProps
26
+ }: TiltShiftProps) {
27
+ const args = useMemo<[TiltShiftOptions]>(
28
+ () => [{ kernelSize, resolutionScale, resolutionX, resolutionY }],
29
+ [kernelSize, resolutionScale, resolutionX, resolutionY]
30
+ )
31
+ return <TiltShiftImpl blendFunction={blendFunction} args={args} {...liveProps} />
32
+ }
@@ -1,6 +1,7 @@
1
1
  import { BlendFunction, Effect, EffectAttribute } from 'postprocessing'
2
+ import type { Ref } from 'react'
2
3
  import { Uniform } from 'three'
3
- import { wrapEffect } from '../wrapEffect'
4
+ import { createEffectComponent } from '../createEffectComponent'
4
5
 
5
6
  const TiltShiftShader = {
6
7
  fragmentShader: /* glsl */ `
@@ -62,20 +63,22 @@ const TiltShiftShader = {
62
63
  `,
63
64
  }
64
65
 
66
+ type Vec2Tuple = [number, number]
67
+
65
68
  export class TiltShiftEffect extends Effect {
66
69
  constructor({
67
70
  blendFunction = BlendFunction.NORMAL,
68
71
  blur = 0.15, // [0, 1], can go beyond 1 for extra
69
72
  taper = 0.5, // [0, 1], can go beyond 1 for extra
70
- start = [0.5, 0.0], // [0,1] percentage x,y of screenspace
71
- end = [0.5, 1.0], // [0,1] percentage x,y of screenspace
73
+ start = [0.5, 0.0] as Vec2Tuple, // [0,1] percentage x,y of screenspace
74
+ end = [0.5, 1.0] as Vec2Tuple, // [0,1] percentage x,y of screenspace
72
75
  samples = 10.0, // number of blur samples
73
- direction = [1, 1], // direction of blur
76
+ direction = [1, 1] as Vec2Tuple, // direction of blur
74
77
  } = {}) {
75
78
  super('TiltShiftEffect', TiltShiftShader.fragmentShader, {
76
79
  blendFunction,
77
80
  attributes: EffectAttribute.CONVOLUTION,
78
- uniforms: new Map<string, Uniform<number | number[]>>([
81
+ uniforms: new Map<string, Uniform<number | Vec2Tuple>>([
79
82
  ['blur', new Uniform(blur)],
80
83
  ['taper', new Uniform(taper)],
81
84
  ['start', new Uniform(start)],
@@ -85,6 +88,69 @@ export class TiltShiftEffect extends Effect {
85
88
  ]),
86
89
  })
87
90
  }
91
+
92
+ private u<T>(name: string): T {
93
+ return this.uniforms.get(name)!.value
94
+ }
95
+
96
+ private setU(name: string, value: unknown): void {
97
+ this.uniforms.get(name)!.value = value
98
+ }
99
+
100
+ get blur(): number {
101
+ return this.u('blur')
102
+ }
103
+ set blur(value: number) {
104
+ this.setU('blur', value)
105
+ }
106
+
107
+ get taper(): number {
108
+ return this.u('taper')
109
+ }
110
+ set taper(value: number) {
111
+ this.setU('taper', value)
112
+ }
113
+
114
+ get start(): Vec2Tuple {
115
+ return this.u('start')
116
+ }
117
+ set start(value: Vec2Tuple) {
118
+ this.setU('start', value)
119
+ }
120
+
121
+ get end(): Vec2Tuple {
122
+ return this.u('end')
123
+ }
124
+ set end(value: Vec2Tuple) {
125
+ this.setU('end', value)
126
+ }
127
+
128
+ get samples(): number {
129
+ return this.u('samples')
130
+ }
131
+ set samples(value: number) {
132
+ this.setU('samples', value)
133
+ }
134
+
135
+ get direction(): Vec2Tuple {
136
+ return this.u('direction')
137
+ }
138
+ set direction(value: Vec2Tuple) {
139
+ this.setU('direction', value)
140
+ }
141
+ }
142
+
143
+ export type TiltShift2Props = {
144
+ blendFunction?: BlendFunction
145
+ blur?: number
146
+ taper?: number
147
+ start?: Vec2Tuple
148
+ end?: Vec2Tuple
149
+ samples?: number
150
+ direction?: Vec2Tuple
151
+ ref?: Ref<TiltShiftEffect>
88
152
  }
89
153
 
90
- export const TiltShift2 = /* @__PURE__ */ wrapEffect(TiltShiftEffect, { blendFunction: BlendFunction.NORMAL })
154
+ export const TiltShift2 = /* @__PURE__ */ createEffectComponent<typeof TiltShiftEffect, TiltShift2Props>(
155
+ TiltShiftEffect
156
+ )
@@ -1,6 +1,19 @@
1
1
  import { ToneMappingEffect } from 'postprocessing'
2
- import { type EffectProps, wrapEffect } from '../wrapEffect'
2
+ import type { Ref } from 'react'
3
+ import { useMemo } from 'react'
4
+ import { createEffectComponent, type EffectOptions } from '../createEffectComponent'
3
5
 
4
- export type ToneMappingProps = EffectProps<typeof ToneMappingEffect>
6
+ type ToneMappingOptions = EffectOptions<typeof ToneMappingEffect>
5
7
 
6
- export const ToneMapping = /* @__PURE__ */ wrapEffect(ToneMappingEffect)
8
+ const ToneMappingImpl = /* @__PURE__ */ createEffectComponent<typeof ToneMappingEffect, ToneMappingOptions>(
9
+ ToneMappingEffect
10
+ )
11
+
12
+ export type ToneMappingProps = ToneMappingOptions & { opacity?: number; ref?: Ref<ToneMappingEffect> }
13
+
14
+ // minLuminance/maxLuminance have no live setter in postprocessing - routed
15
+ // through args so they still work as plain props.
16
+ export function ToneMapping({ minLuminance, maxLuminance, ...liveProps }: ToneMappingProps) {
17
+ const args = useMemo<[ToneMappingOptions]>(() => [{ minLuminance, maxLuminance }], [minLuminance, maxLuminance])
18
+ return <ToneMappingImpl args={args} {...liveProps} />
19
+ }
@@ -1,4 +1,7 @@
1
1
  import { VignetteEffect } from 'postprocessing'
2
- import { wrapEffect } from '../wrapEffect'
2
+ import { createEffectComponent, type EffectOptions } from '../createEffectComponent'
3
3
 
4
- export const Vignette = /* @__PURE__ */ wrapEffect(VignetteEffect)
4
+ export const Vignette = /* @__PURE__ */ createEffectComponent<
5
+ typeof VignetteEffect,
6
+ EffectOptions<typeof VignetteEffect>
7
+ >(VignetteEffect)
@@ -1,6 +1,7 @@
1
1
  import { BlendFunction, Effect, EffectAttribute } from 'postprocessing'
2
+ import type { Ref } from 'react'
2
3
  import { Uniform } from 'three'
3
- import { wrapEffect } from '../wrapEffect'
4
+ import { createEffectComponent } from '../createEffectComponent'
4
5
 
5
6
  const WaterShader = {
6
7
  fragmentShader: /* glsl */ `
@@ -10,7 +11,7 @@ const WaterShader = {
10
11
  vec2 vUv = uv;
11
12
  float frequency = 6.0 * factor;
12
13
  float amplitude = 0.015 * factor;
13
- float x = vUv.y * frequency + time * 0.7;
14
+ float x = vUv.y * frequency + time * 0.7;
14
15
  float y = vUv.x * frequency + time * 0.3;
15
16
  vUv.x += cos(x + y) * amplitude * cos(y);
16
17
  vUv.y += sin(x - y) * amplitude * cos(y);
@@ -25,11 +26,25 @@ export class WaterEffectImpl extends Effect {
25
26
  super('WaterEffect', WaterShader.fragmentShader, {
26
27
  blendFunction,
27
28
  attributes: EffectAttribute.CONVOLUTION,
28
- uniforms: new Map<string, Uniform<number | number[]>>([['factor', new Uniform(factor)]]),
29
+ uniforms: new Map<string, Uniform<number>>([['factor', new Uniform(factor)]]),
29
30
  })
30
31
  }
32
+
33
+ get factor(): number {
34
+ return this.uniforms.get('factor')!.value
35
+ }
36
+
37
+ set factor(value: number) {
38
+ this.uniforms.get('factor')!.value = value
39
+ }
40
+ }
41
+
42
+ export type WaterEffectProps = {
43
+ blendFunction?: BlendFunction
44
+ factor?: number
45
+ ref?: Ref<WaterEffectImpl>
31
46
  }
32
47
 
33
- export const WaterEffect = /* @__PURE__ */ wrapEffect(WaterEffectImpl, {
34
- blendFunction: BlendFunction.NORMAL,
35
- })
48
+ export const WaterEffect = /* @__PURE__ */ createEffectComponent<typeof WaterEffectImpl, WaterEffectProps>(
49
+ WaterEffectImpl
50
+ )
package/src/index.ts CHANGED
@@ -1,4 +1,6 @@
1
+ export * from './createEffectComponent'
1
2
  export * from './EffectComposer'
3
+ export * from './EffectGroup'
2
4
  export * from './Selection'
3
5
  export * from './util'
4
6
  export * from './wrapEffect'
@@ -37,5 +39,5 @@ export * from './effects/ToneMapping'
37
39
  export * from './effects/Vignette'
38
40
  export * from './effects/Water'
39
41
 
40
- // These are not effect passes
41
- export * from './effects/N8AO'
42
+ export * from './passes/DepthPicking'
43
+ export * from './passes/N8AO'