@react-three/postprocessing 3.0.5 → 3.1.0

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 +4 -3
  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,83 +1,82 @@
1
1
  import { OutlineEffect } from 'postprocessing'
2
- import { Ref, RefObject, use, useMemo } from 'react'
3
- import { Object3D } from 'three'
2
+ import { Ref, RefObject, use, useEffect, useMemo } from 'react'
3
+ import { Color, Object3D, type ColorRepresentation } from 'three'
4
4
  import { EffectComposerContext } from '../EffectComposer'
5
- import { EMPTY_ARRAY, useDispose, useSelectionSync } from '../util'
5
+ import { applyPierced, EMPTY_ARRAY, readPierced, useDispose, useLiveDefaults, useSelectionSync } from '../util'
6
6
 
7
7
  type ObjectRef = RefObject<Object3D | null>
8
8
 
9
- export type OutlineProps = ConstructorParameters<typeof OutlineEffect>[2] &
9
+ type OutlineEffectOptions = NonNullable<ConstructorParameters<typeof OutlineEffect>[2]>
10
+
11
+ export type OutlineProps = Omit<OutlineEffectOptions, 'visibleEdgeColor' | 'hiddenEdgeColor'> &
10
12
  Partial<{
11
13
  selection: Object3D | Object3D[] | ObjectRef | ObjectRef[]
12
14
  selectionLayer: number
15
+ visibleEdgeColor: ColorRepresentation
16
+ hiddenEdgeColor: ColorRepresentation
13
17
  ref?: Ref<OutlineEffect>
14
18
  }>
15
19
 
20
+ // Every OutlineEffect option that has a real setter (verified against
21
+ // postprocessing's source) - resolutionScale/resolutionX/resolutionY are
22
+ // the only ones without one, since they only feed the internal blur pass
23
+ // at construction time. scene/camera are required constructor args, so
24
+ // OutlineEffect can't use createEffectComponent (needs `new Effect()` to
25
+ // work with zero args) - built by hand instead.
26
+ const LIVE_KEYS = [
27
+ 'patternTexture',
28
+ 'patternScale',
29
+ 'edgeStrength',
30
+ 'pulseSpeed',
31
+ 'visibleEdgeColor',
32
+ 'hiddenEdgeColor',
33
+ 'multisampling',
34
+ 'width',
35
+ 'height',
36
+ 'kernelSize',
37
+ 'blur',
38
+ 'xRay',
39
+ 'dithering',
40
+ 'blendMode-blendFunction',
41
+ ]
42
+
43
+ // The setter stores whatever it's given as-is, unlike the constructor -
44
+ // wrap in a Color here too, or a raw hex/string breaks the shader uniform.
45
+ function set(effect: OutlineEffect, key: string, value: unknown): void {
46
+ if (key === 'visibleEdgeColor' || key === 'hiddenEdgeColor') applyPierced(effect, key, new Color(value as never))
47
+ else applyPierced(effect, key, value)
48
+ }
49
+
16
50
  export function Outline({
17
51
  selection = EMPTY_ARRAY,
18
52
  selectionLayer = 10,
19
53
  blendFunction,
20
- patternTexture,
21
- patternScale,
22
- edgeStrength,
23
- pulseSpeed,
24
- visibleEdgeColor,
25
- hiddenEdgeColor,
26
- multisampling,
27
54
  resolutionScale,
28
55
  resolutionX,
29
56
  resolutionY,
30
- width,
31
- height,
32
- kernelSize,
33
- blur,
34
- xRay,
35
57
  ref,
58
+ ...liveProps
36
59
  }: OutlineProps) {
37
- const { scene, camera } = use(EffectComposerContext)
60
+ const { scene, camera, autoClear } = use(EffectComposerContext)
38
61
 
39
62
  const effect = useMemo(
40
- () =>
41
- new OutlineEffect(scene, camera, {
42
- blendFunction,
43
- patternTexture,
44
- patternScale,
45
- edgeStrength,
46
- pulseSpeed,
47
- visibleEdgeColor,
48
- hiddenEdgeColor,
49
- multisampling,
50
- resolutionScale,
51
- resolutionX,
52
- resolutionY,
53
- width,
54
- height,
55
- kernelSize,
56
- blur,
57
- xRay,
58
- }),
59
- [
60
- blendFunction,
61
- patternTexture,
62
- patternScale,
63
- edgeStrength,
64
- pulseSpeed,
65
- visibleEdgeColor,
66
- hiddenEdgeColor,
67
- multisampling,
68
- resolutionScale,
69
- resolutionX,
70
- resolutionY,
71
- width,
72
- height,
73
- kernelSize,
74
- blur,
75
- xRay,
76
- camera,
77
- scene,
78
- ]
63
+ () => new OutlineEffect(scene, camera, { resolutionScale, resolutionX, resolutionY }),
64
+ [scene, camera, resolutionScale, resolutionX, resolutionY]
79
65
  )
80
66
 
67
+ useEffect(() => {
68
+ if (autoClear !== false) {
69
+ console.warn('Outline requires <EffectComposer autoClear={false}> to render correctly.')
70
+ }
71
+ }, [autoClear])
72
+
73
+ useLiveDefaults(
74
+ effect,
75
+ { ...liveProps, 'blendMode-blendFunction': blendFunction } as Record<string, unknown>,
76
+ LIVE_KEYS,
77
+ readPierced,
78
+ set
79
+ )
81
80
  useSelectionSync(effect, selection, selectionLayer)
82
81
  useDispose(effect)
83
82
 
@@ -1,6 +1,14 @@
1
1
  import { PixelationEffect } from 'postprocessing'
2
- import { Ref, useMemo } from 'react'
3
- import { useDispose } from '../util'
2
+ import type { Ref } from 'react'
3
+ import { createEffectComponent } from '../createEffectComponent'
4
+
5
+ // PixelationEffect's sole constructor arg is a bare number, not an options
6
+ // object - granularity is a real live setter though, so it's just a normal
7
+ // prop; only the curated default (5, vs the class's own default of 30)
8
+ // needs a thin wrapper.
9
+ const PixelationImpl = /* @__PURE__ */ createEffectComponent<typeof PixelationEffect, { granularity?: number }>(
10
+ PixelationEffect
11
+ )
4
12
 
5
13
  export type PixelationProps = {
6
14
  granularity?: number
@@ -8,10 +16,5 @@ export type PixelationProps = {
8
16
  }
9
17
 
10
18
  export function Pixelation({ granularity = 5, ref }: PixelationProps) {
11
- /** Because GlitchEffect granularity is not an object but a number, we have to define a custom prop "granularity" */
12
- const effect = useMemo(() => new PixelationEffect(granularity), [granularity])
13
-
14
- useDispose(effect)
15
-
16
- return <primitive ref={ref} object={effect} />
19
+ return <PixelationImpl granularity={granularity} ref={ref} />
17
20
  }
@@ -1,6 +1,7 @@
1
- import { Effect } from 'postprocessing'
1
+ import { BlendFunction, Effect } 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 RampShader = {
6
7
  fragmentShader: /* glsl */ `
@@ -72,6 +73,9 @@ export enum RampType {
72
73
  MirroredLinear,
73
74
  }
74
75
 
76
+ type RampTuple2 = [number, number]
77
+ type RampTuple4 = [number, number, number, number]
78
+
75
79
  export class RampEffect extends Effect {
76
80
  constructor({
77
81
  /**
@@ -83,25 +87,25 @@ export class RampEffect extends Effect {
83
87
  *
84
88
  * Ranges from `[0 - 1]` as `[x, y]`. Default is `[0.5, 0.5]`.
85
89
  */
86
- rampStart = [0.5, 0.5],
90
+ rampStart = [0.5, 0.5] as RampTuple2,
87
91
  /**
88
92
  * Ending point of the ramp gradient in normalized coordinates.
89
93
  *
90
94
  * Ranges from `[0 - 1]` as `[x, y]`. Default is `[1, 1]`
91
95
  */
92
- rampEnd = [1, 1],
96
+ rampEnd = [1, 1] as RampTuple2,
93
97
  /**
94
98
  * Color at the starting point of the gradient.
95
99
  *
96
100
  * Default is black: `[0, 0, 0, 1]`
97
101
  */
98
- startColor = [0, 0, 0, 1],
102
+ startColor = [0, 0, 0, 1] as RampTuple4,
99
103
  /**
100
104
  * Color at the ending point of the gradient.
101
105
  *
102
106
  * Default is white: `[1, 1, 1, 1]`
103
107
  */
104
- endColor = [1, 1, 1, 1],
108
+ endColor = [1, 1, 1, 1] as RampTuple4,
105
109
  /**
106
110
  * Bias for the interpolation curve when both bias and gain are 0.5.
107
111
  *
@@ -145,6 +149,91 @@ export class RampEffect extends Effect {
145
149
  ]),
146
150
  })
147
151
  }
152
+
153
+ private u<T>(name: string): T {
154
+ return this.uniforms.get(name)!.value
155
+ }
156
+
157
+ private setU(name: string, value: unknown): void {
158
+ this.uniforms.get(name)!.value = value
159
+ }
160
+
161
+ get rampType(): RampType {
162
+ return this.u('rampType')
163
+ }
164
+ set rampType(value: RampType) {
165
+ this.setU('rampType', value)
166
+ }
167
+
168
+ get rampStart(): RampTuple2 {
169
+ return this.u('rampStart')
170
+ }
171
+ set rampStart(value: RampTuple2) {
172
+ this.setU('rampStart', value)
173
+ }
174
+
175
+ get rampEnd(): RampTuple2 {
176
+ return this.u('rampEnd')
177
+ }
178
+ set rampEnd(value: RampTuple2) {
179
+ this.setU('rampEnd', value)
180
+ }
181
+
182
+ get startColor(): RampTuple4 {
183
+ return this.u('startColor')
184
+ }
185
+ set startColor(value: RampTuple4) {
186
+ this.setU('startColor', value)
187
+ }
188
+
189
+ get endColor(): RampTuple4 {
190
+ return this.u('endColor')
191
+ }
192
+ set endColor(value: RampTuple4) {
193
+ this.setU('endColor', value)
194
+ }
195
+
196
+ get rampBias(): number {
197
+ return this.u('rampBias')
198
+ }
199
+ set rampBias(value: number) {
200
+ this.setU('rampBias', value)
201
+ }
202
+
203
+ get rampGain(): number {
204
+ return this.u('rampGain')
205
+ }
206
+ set rampGain(value: number) {
207
+ this.setU('rampGain', value)
208
+ }
209
+
210
+ get rampMask(): boolean {
211
+ return this.u('rampMask')
212
+ }
213
+ set rampMask(value: boolean) {
214
+ this.setU('rampMask', value)
215
+ }
216
+
217
+ get rampInvert(): boolean {
218
+ return this.u('rampInvert')
219
+ }
220
+ set rampInvert(value: boolean) {
221
+ this.setU('rampInvert', value)
222
+ }
223
+ }
224
+
225
+ export type RampProps = {
226
+ blendFunction?: BlendFunction
227
+ rampType?: RampType
228
+ rampStart?: RampTuple2
229
+ rampEnd?: RampTuple2
230
+ startColor?: RampTuple4
231
+ endColor?: RampTuple4
232
+ rampBias?: number
233
+ rampGain?: number
234
+ rampMask?: boolean
235
+ rampInvert?: boolean
236
+ ref?: Ref<RampEffect>
148
237
  }
149
238
 
150
- export const Ramp = /* @__PURE__ */ wrapEffect(RampEffect)
239
+ export const Ramp = /* @__PURE__ */ createEffectComponent<typeof RampEffect, RampProps>(RampEffect)
@@ -1,4 +1,20 @@
1
1
  import { SMAAEffect } 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 SMAA = /* @__PURE__ */ wrapEffect(SMAAEffect)
6
+ type SMAAOptions = EffectOptions<typeof SMAAEffect>
7
+
8
+ const SMAAImpl = /* @__PURE__ */ createEffectComponent<typeof SMAAEffect, SMAAOptions>(SMAAEffect)
9
+
10
+ export type SMAAProps = SMAAOptions & { opacity?: number; ref?: Ref<SMAAEffect> }
11
+
12
+ // preset/edgeDetectionMode/predicationMode have no live setter in
13
+ // postprocessing - routed through args so they still work as plain props.
14
+ export function SMAA({ preset, edgeDetectionMode, predicationMode, ...liveProps }: SMAAProps) {
15
+ const args = useMemo<[SMAAOptions]>(
16
+ () => [{ preset, edgeDetectionMode, predicationMode }],
17
+ [preset, edgeDetectionMode, predicationMode]
18
+ )
19
+ return <SMAAImpl args={args} {...liveProps} />
20
+ }
@@ -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
+ }