@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
@@ -4,7 +4,7 @@ import type { Ref } from 'react'
4
4
  import { use, useMemo } from 'react'
5
5
  import { type DepthPackingStrategies, type Texture, Vector3 } from 'three'
6
6
  import { EffectComposerContext } from '../EffectComposer'
7
- import { useDispose } from '../util'
7
+ import { applyPierced, readPierced, useDispose, useLiveDefaults } from '../util'
8
8
 
9
9
  export type DepthOfFieldProps = ConstructorParameters<typeof DepthOfFieldEffect>[1] &
10
10
  Partial<{
@@ -19,6 +19,37 @@ export type DepthOfFieldProps = ConstructorParameters<typeof DepthOfFieldEffect>
19
19
  blur: number
20
20
  }>
21
21
 
22
+ // Only bokehScale, focusDistance/focusRange (via the nested cocMaterial),
23
+ // depthTexture (via setDepthTexture) and blendFunction have real setters in
24
+ // postprocessing - every resolution option is construction-only. camera
25
+ // being a required constructor arg also rules out createEffectComponent
26
+ // (needs `new Effect()` to work with zero args).
27
+ const LIVE_KEYS = [
28
+ 'blendMode-blendFunction',
29
+ 'bokehScale',
30
+ 'cocMaterial-focusDistance',
31
+ 'cocMaterial-focusRange',
32
+ 'depthTexture',
33
+ ]
34
+
35
+ // cocMaterial.depthBuffer/depthPacking are write-only in postprocessing
36
+ // (setters with no matching getters) - depthPacking can't be read back at
37
+ // all, so a reverted default always re-applies BasicDepthPacking (the same
38
+ // value setDepthTexture itself defaults to when packing is omitted).
39
+ function get(effect: DepthOfFieldEffect, key: string): unknown {
40
+ if (key !== 'depthTexture') return readPierced(effect, key)
41
+ const texture = (effect.cocMaterial as unknown as { uniforms: { depthBuffer: { value: unknown } } }).uniforms
42
+ .depthBuffer.value
43
+ return texture ? { texture } : undefined
44
+ }
45
+
46
+ function set(effect: DepthOfFieldEffect, key: string, value: unknown): void {
47
+ if (key === 'depthTexture') {
48
+ const dt = value as { texture?: Texture; packing?: DepthPackingStrategies } | undefined
49
+ effect.setDepthTexture(dt?.texture as never, dt?.packing)
50
+ } else applyPierced(effect, key, value)
51
+ }
52
+
22
53
  export function DepthOfField({
23
54
  ref,
24
55
  blendFunction,
@@ -42,13 +73,9 @@ export function DepthOfField({
42
73
 
43
74
  const effect = useMemo(() => {
44
75
  const effect = new DepthOfFieldEffect(camera, {
45
- blendFunction,
46
76
  worldFocusDistance,
47
77
  worldFocusRange,
48
- focusDistance,
49
- focusRange,
50
78
  focalLength,
51
- bokehScale,
52
79
  resolutionScale,
53
80
  resolutionX,
54
81
  resolutionY,
@@ -57,29 +84,24 @@ export function DepthOfField({
57
84
  })
58
85
  // Creating a target enables autofocus, R3F will set via props
59
86
  if (autoFocus) effect.target = new Vector3()
60
- // Depth texture for depth picking with optional packing strategy
61
- if (depthTexture) effect.setDepthTexture(depthTexture.texture, depthTexture.packing as DepthPackingStrategies)
62
87
  // Temporary fix that restores DOF 6.21.3 behavior, everything since then lets shapes leak through the blur
63
- const maskPass = (effect as any).maskPass
64
- maskPass.maskFunction = MaskFunction.MULTIPLY_RGB_SET_ALPHA
88
+ effect.maskFunction = MaskFunction.MULTIPLY_RGB_SET_ALPHA
65
89
  return effect
66
- }, [
67
- camera,
68
- blendFunction,
69
- worldFocusDistance,
70
- worldFocusRange,
71
- focusDistance,
72
- focusRange,
73
- focalLength,
74
- bokehScale,
75
- resolutionScale,
76
- resolutionX,
77
- resolutionY,
78
- width,
79
- height,
80
- autoFocus,
81
- depthTexture,
82
- ])
90
+ }, [camera, worldFocusDistance, worldFocusRange, focalLength, resolutionScale, resolutionX, resolutionY, width, height, autoFocus])
91
+
92
+ useLiveDefaults(
93
+ effect,
94
+ {
95
+ 'blendMode-blendFunction': blendFunction,
96
+ bokehScale,
97
+ 'cocMaterial-focusDistance': focusDistance,
98
+ 'cocMaterial-focusRange': focusRange,
99
+ depthTexture,
100
+ },
101
+ LIVE_KEYS,
102
+ get,
103
+ set
104
+ )
83
105
 
84
106
  useDispose(effect)
85
107
 
@@ -1,4 +1,7 @@
1
1
  import { DotScreenEffect } from 'postprocessing'
2
- import { wrapEffect } from '../wrapEffect'
2
+ import { createEffectComponent, type EffectOptions } from '../createEffectComponent'
3
3
 
4
- export const DotScreen = /* @__PURE__ */ wrapEffect(DotScreenEffect)
4
+ export const DotScreen = /* @__PURE__ */ createEffectComponent<
5
+ typeof DotScreenEffect,
6
+ EffectOptions<typeof DotScreenEffect>
7
+ >(DotScreenEffect)
@@ -1,4 +1,6 @@
1
1
  import { FXAAEffect } from 'postprocessing'
2
- import { wrapEffect } from '../wrapEffect'
2
+ import { createEffectComponent, type EffectOptions } from '../createEffectComponent'
3
3
 
4
- export const FXAA = /* @__PURE__ */ wrapEffect(FXAAEffect)
4
+ export const FXAA = /* @__PURE__ */ createEffectComponent<typeof FXAAEffect, EffectOptions<typeof FXAAEffect>>(
5
+ FXAAEffect
6
+ )
@@ -1,37 +1,32 @@
1
- import { ReactThreeFiber, useThree } from '@react-three/fiber'
1
+ import type { ReactThreeFiber } from '@react-three/fiber'
2
2
  import { GlitchEffect, GlitchMode } from 'postprocessing'
3
- import { Ref, useLayoutEffect, useMemo } from 'react'
4
- import { useDispose, useVector2 } from '../util'
3
+ import type { Ref } from 'react'
4
+ import { useMemo } from 'react'
5
+ import { createEffectComponent, type EffectOptions } from '../createEffectComponent'
5
6
 
6
- export type GlitchProps = ConstructorParameters<typeof GlitchEffect>[0] &
7
- Partial<{
8
- mode: GlitchMode
9
- active: boolean
10
- delay: ReactThreeFiber.Vector2
11
- duration: ReactThreeFiber.Vector2
12
- chromaticAberrationOffset: ReactThreeFiber.Vector2
13
- strength: ReactThreeFiber.Vector2
14
- ref?: Ref<GlitchEffect>
15
- }>
16
-
17
- export function Glitch({ active = true, ref, ...props }: GlitchProps) {
18
- const invalidate = useThree((state) => state.invalidate)
19
- const delay = useVector2(props, 'delay')
20
- const duration = useVector2(props, 'duration')
21
- const strength = useVector2(props, 'strength')
22
- const chromaticAberrationOffset = useVector2(props, 'chromaticAberrationOffset')
23
-
24
- const effect = useMemo(
25
- () => new GlitchEffect({ ...props, delay, duration, strength, chromaticAberrationOffset }),
26
- [delay, duration, props, strength, chromaticAberrationOffset]
27
- )
7
+ type GlitchOptions = Omit<
8
+ EffectOptions<typeof GlitchEffect>,
9
+ 'delay' | 'duration' | 'strength' | 'chromaticAberrationOffset'
10
+ > & {
11
+ delay?: ReactThreeFiber.Vector2
12
+ duration?: ReactThreeFiber.Vector2
13
+ strength?: ReactThreeFiber.Vector2
14
+ chromaticAberrationOffset?: ReactThreeFiber.Vector2
15
+ mode?: GlitchMode
16
+ }
28
17
 
29
- useLayoutEffect(() => {
30
- effect.mode = active ? props.mode || GlitchMode.SPORADIC : GlitchMode.DISABLED
31
- invalidate()
32
- }, [active, effect, invalidate, props.mode])
18
+ const GlitchImpl = /* @__PURE__ */ createEffectComponent<typeof GlitchEffect, GlitchOptions>(GlitchEffect)
33
19
 
34
- useDispose(effect)
20
+ export type GlitchProps = GlitchOptions & {
21
+ active?: boolean
22
+ opacity?: number
23
+ ref?: Ref<GlitchEffect>
24
+ }
35
25
 
36
- return <primitive ref={ref} object={effect} />
26
+ // dtSize only seeds the auto-generated perturbation map at construction time
27
+ // (skipped entirely once a perturbationMap is provided) - routed through
28
+ // args so it still works as a plain prop.
29
+ export function Glitch({ active = true, mode = GlitchMode.SPORADIC, dtSize, ...props }: GlitchProps) {
30
+ const args = useMemo<[EffectOptions<typeof GlitchEffect>]>(() => [{ dtSize }], [dtSize])
31
+ return <GlitchImpl args={args} mode={active ? mode : GlitchMode.DISABLED} {...props} />
37
32
  }
@@ -1,18 +1,102 @@
1
+ import { useThree } from '@react-three/fiber'
1
2
  import { GodRaysEffect } from 'postprocessing'
2
- import { Ref, RefObject, useContext, useLayoutEffect, useMemo } from 'react'
3
+ import { Ref, RefObject, use, useEffect, useLayoutEffect, useMemo } from 'react'
3
4
  import { Mesh, Points } from 'three'
4
5
  import { EffectComposerContext } from '../EffectComposer'
5
- import { resolveRef, useDispose } from '../util'
6
+ import { applyPierced, readPierced, resolveRef, useDispose, useLiveDefaults } from '../util'
6
7
 
7
8
  type GodRaysProps = ConstructorParameters<typeof GodRaysEffect>[2] & {
8
9
  sun: Mesh | Points | RefObject<Mesh | Points>
9
10
  ref?: Ref<GodRaysEffect>
10
11
  }
11
12
 
12
- export function GodRays({ ref, ...props }: GodRaysProps) {
13
- const { camera } = useContext(EffectComposerContext)
14
- const effect = useMemo(() => new GodRaysEffect(camera, resolveRef(props.sun), props), [camera, props])
15
- useLayoutEffect(() => void (effect.lightSource = resolveRef(props.sun)), [effect, props.sun])
13
+ // GodRaysMaterial (godRaysMaterial) is where density/decay/weight/exposure
14
+ // actually live - clampMax maps to its differently-named maxIntensity.
15
+ // resolutionScale/resolutionX/resolutionY have no setter at all in
16
+ // postprocessing - construction-only. camera+sun being required constructor
17
+ // args also rule out createEffectComponent (needs `new Effect()` to work
18
+ // with zero args).
19
+ const LIVE_KEYS = [
20
+ 'blendMode-blendFunction',
21
+ 'godRaysMaterial-density',
22
+ 'godRaysMaterial-decay',
23
+ 'godRaysMaterial-weight',
24
+ 'godRaysMaterial-exposure',
25
+ 'clampMax',
26
+ 'blur',
27
+ 'kernelSize',
28
+ 'samples',
29
+ 'width',
30
+ 'height',
31
+ ]
32
+
33
+ function get(effect: GodRaysEffect, key: string): unknown {
34
+ return key === 'clampMax' ? effect.godRaysMaterial.maxIntensity : readPierced(effect, key)
35
+ }
36
+
37
+ function set(effect: GodRaysEffect, key: string, value: unknown): void {
38
+ if (key === 'clampMax') effect.godRaysMaterial.maxIntensity = value as number
39
+ else applyPierced(effect, key, value)
40
+ }
41
+
42
+ export function GodRays({
43
+ sun,
44
+ blendFunction,
45
+ density,
46
+ decay,
47
+ weight,
48
+ exposure,
49
+ clampMax,
50
+ blur,
51
+ kernelSize,
52
+ samples,
53
+ width,
54
+ height,
55
+ resolutionScale,
56
+ resolutionX,
57
+ resolutionY,
58
+ ref,
59
+ }: GodRaysProps) {
60
+ const { camera, autoClear } = use(EffectComposerContext)
61
+ const invalidate = useThree((state) => state.invalidate)
62
+
63
+ const effect = useMemo(
64
+ () => new GodRaysEffect(camera, resolveRef(sun), { resolutionScale, resolutionX, resolutionY }),
65
+ [camera, resolutionScale, resolutionX, resolutionY]
66
+ )
67
+
68
+ useEffect(() => {
69
+ if (autoClear !== false) {
70
+ console.warn(
71
+ 'GodRays renders an internal extra pass that needs <EffectComposer autoClear={false}> - without it, occlusion by other objects will look wrong.'
72
+ )
73
+ }
74
+ }, [autoClear])
75
+
76
+ useLayoutEffect(() => {
77
+ effect.lightSource = resolveRef(sun)
78
+ invalidate()
79
+ }, [effect, sun, invalidate])
80
+
81
+ useLiveDefaults(
82
+ effect,
83
+ {
84
+ 'blendMode-blendFunction': blendFunction,
85
+ 'godRaysMaterial-density': density,
86
+ 'godRaysMaterial-decay': decay,
87
+ 'godRaysMaterial-weight': weight,
88
+ 'godRaysMaterial-exposure': exposure,
89
+ clampMax,
90
+ blur,
91
+ kernelSize,
92
+ samples,
93
+ width,
94
+ height,
95
+ },
96
+ LIVE_KEYS,
97
+ get,
98
+ set
99
+ )
16
100
 
17
101
  useDispose(effect)
18
102
 
@@ -1,28 +1,27 @@
1
1
  import { useThree } from '@react-three/fiber'
2
2
  import { GridEffect } from 'postprocessing'
3
- import { Ref, useLayoutEffect, useMemo } from 'react'
4
- import { useDispose } from '../util'
3
+ import { type Ref, useImperativeHandle, useLayoutEffect, useRef } from 'react'
4
+ import { createEffectComponent, type EffectOptions } from '../createEffectComponent'
5
5
 
6
- type GridProps = ConstructorParameters<typeof GridEffect>[0] &
7
- Partial<{
8
- size: {
9
- width: number
10
- height: number
11
- }
12
- ref: Ref<GridEffect>
13
- }>
6
+ const GridImpl = /* @__PURE__ */ createEffectComponent<typeof GridEffect, EffectOptions<typeof GridEffect>>(GridEffect)
7
+
8
+ export type GridProps = EffectOptions<typeof GridEffect> & {
9
+ size?: { width: number; height: number }
10
+ opacity?: number
11
+ ref?: Ref<GridEffect>
12
+ }
14
13
 
15
14
  export function Grid({ size, ref, ...props }: GridProps) {
16
15
  const invalidate = useThree((state) => state.invalidate)
17
-
18
- const effect = useMemo(() => new GridEffect(props), [props])
16
+ const localRef = useRef<GridEffect>(null)
17
+ useImperativeHandle(ref, () => localRef.current!, [])
19
18
 
20
19
  useLayoutEffect(() => {
21
- if (size) effect.setSize(size.width, size.height)
22
- invalidate()
23
- }, [effect, size, invalidate])
24
-
25
- useDispose(effect)
20
+ if (size) {
21
+ localRef.current?.setSize(size.width, size.height)
22
+ invalidate()
23
+ }
24
+ }, [size, invalidate])
26
25
 
27
- return <primitive ref={ref} object={effect} />
26
+ return <GridImpl ref={localRef} {...props} />
28
27
  }
@@ -1,4 +1,7 @@
1
1
  import { HueSaturationEffect } from 'postprocessing'
2
- import { wrapEffect } from '../wrapEffect'
2
+ import { createEffectComponent, type EffectOptions } from '../createEffectComponent'
3
3
 
4
- export const HueSaturation = /* @__PURE__ */ wrapEffect(HueSaturationEffect)
4
+ export const HueSaturation = /* @__PURE__ */ createEffectComponent<
5
+ typeof HueSaturationEffect,
6
+ EffectOptions<typeof HueSaturationEffect>
7
+ >(HueSaturationEffect)
@@ -1,8 +1,7 @@
1
- import { useThree } from '@react-three/fiber'
2
1
  import { BlendFunction, LUT3DEffect } from 'postprocessing'
3
- import { Ref, useLayoutEffect, useMemo } from 'react'
2
+ import { Ref, useMemo } from 'react'
4
3
  import type { Texture } from 'three'
5
- import { useDispose } from '../util'
4
+ import { useDispose, useLiveDefaults } from '../util'
6
5
 
7
6
  export type LUTProps = {
8
7
  lut: Texture
@@ -11,16 +10,15 @@ export type LUTProps = {
11
10
  ref?: Ref<LUT3DEffect>
12
11
  }
13
12
 
14
- export function LUT({ lut, tetrahedralInterpolation, ref, ...props }: LUTProps) {
15
- const effect = useMemo(() => new LUT3DEffect(lut, props), [lut, props])
16
- const invalidate = useThree((state) => state.invalidate)
13
+ const LIVE_KEYS = ['blendMode-blendFunction', 'lut', 'tetrahedralInterpolation']
17
14
 
18
- useLayoutEffect(() => {
19
- if (tetrahedralInterpolation) effect.tetrahedralInterpolation = tetrahedralInterpolation
20
- if (lut) effect.lut = lut
21
- invalidate()
22
- }, [effect, invalidate, lut, tetrahedralInterpolation])
15
+ // lut is LUT3DEffect's required constructor arg (no default) - only used
16
+ // for the initial instance, later changes go through its own live setter
17
+ // (via useLiveDefaults below) instead of reconstructing.
18
+ export function LUT({ lut, blendFunction, tetrahedralInterpolation, ref }: LUTProps) {
19
+ const effect = useMemo(() => new LUT3DEffect(lut), [])
23
20
 
21
+ useLiveDefaults(effect, { 'blendMode-blendFunction': blendFunction, lut, tetrahedralInterpolation }, LIVE_KEYS)
24
22
  useDispose(effect)
25
23
 
26
24
  return <primitive ref={ref} object={effect} />