@react-three/postprocessing 3.0.4 → 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 (114) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +80 -81
  3. package/dist/EffectComposer.d.ts +14 -6
  4. package/dist/EffectGroup.d.ts +9 -0
  5. package/dist/Selection.d.ts +8 -8
  6. package/dist/createEffectComponent.d.ts +30 -0
  7. package/dist/effects/ASCII.d.ts +29 -7
  8. package/dist/effects/Autofocus.d.ts +8 -7
  9. package/dist/effects/Bloom.d.ts +10 -5
  10. package/dist/effects/BrightnessContrast.d.ts +15 -5
  11. package/dist/effects/ChromaticAberration.d.ts +30 -7
  12. package/dist/effects/ColorAverage.d.ts +7 -8
  13. package/dist/effects/ColorDepth.d.ts +8 -5
  14. package/dist/effects/Depth.d.ts +13 -5
  15. package/dist/effects/DepthOfField.d.ts +7 -18
  16. package/dist/effects/DotScreen.d.ts +15 -5
  17. package/dist/effects/FXAA.d.ts +11 -5
  18. package/dist/effects/Glitch.d.ts +17 -29
  19. package/dist/effects/GodRays.d.ts +7 -19
  20. package/dist/effects/Grid.d.ts +8 -8
  21. package/dist/effects/HueSaturation.d.ts +15 -5
  22. package/dist/effects/LUT.d.ts +4 -3
  23. package/dist/effects/LensFlare.d.ts +52 -11
  24. package/dist/effects/Noise.d.ts +8 -5
  25. package/dist/effects/Outline.d.ts +9 -25
  26. package/dist/effects/Pixelation.d.ts +3 -2
  27. package/dist/effects/Ramp.d.ts +58 -10
  28. package/dist/effects/SMAA.d.ts +10 -5
  29. package/dist/effects/SSAO.d.ts +7 -30
  30. package/dist/effects/ScanlineEffect.d.ts +13 -5
  31. package/dist/effects/SelectiveBloom.d.ts +5 -10
  32. package/dist/effects/Sepia.d.ts +13 -5
  33. package/dist/effects/ShockWave.d.ts +12 -5
  34. package/dist/effects/Texture.d.ts +7 -8
  35. package/dist/effects/TiltShift.d.ts +10 -5
  36. package/dist/effects/TiltShift2.d.ts +44 -8
  37. package/dist/effects/ToneMapping.d.ts +9 -7
  38. package/dist/effects/Vignette.d.ts +19 -5
  39. package/dist/effects/Water.d.ts +17 -5
  40. package/dist/index.d.ts +15 -11
  41. package/dist/index.js +1841 -45
  42. package/dist/index.js.map +1 -1
  43. package/dist/passes/DepthPicking.d.ts +11 -0
  44. package/dist/{effects → passes}/N8AO.d.ts +5 -2
  45. package/dist/util.d.ts +19 -17
  46. package/dist/wrapEffect.d.ts +12 -0
  47. package/package.json +74 -67
  48. package/src/EffectComposer.tsx +379 -200
  49. package/src/EffectGroup.tsx +74 -0
  50. package/src/Selection.tsx +86 -46
  51. package/src/createEffectComponent.tsx +71 -0
  52. package/src/effects/ASCII.tsx +183 -135
  53. package/src/effects/Autofocus.tsx +122 -153
  54. package/src/effects/Bloom.tsx +34 -6
  55. package/src/effects/BrightnessContrast.tsx +7 -4
  56. package/src/effects/ChromaticAberration.tsx +22 -5
  57. package/src/effects/ColorAverage.tsx +4 -15
  58. package/src/effects/ColorDepth.tsx +25 -4
  59. package/src/effects/Depth.tsx +6 -4
  60. package/src/effects/DepthOfField.tsx +109 -89
  61. package/src/effects/DotScreen.tsx +7 -4
  62. package/src/effects/FXAA.tsx +6 -4
  63. package/src/effects/Glitch.tsx +32 -40
  64. package/src/effects/GodRays.tsx +104 -16
  65. package/src/effects/Grid.tsx +27 -21
  66. package/src/effects/HueSaturation.tsx +7 -4
  67. package/src/effects/LUT.tsx +25 -26
  68. package/src/effects/LensFlare.tsx +765 -611
  69. package/src/effects/Noise.tsx +16 -4
  70. package/src/effects/Outline.tsx +84 -117
  71. package/src/effects/Pixelation.tsx +20 -15
  72. package/src/effects/Ramp.tsx +239 -150
  73. package/src/effects/SMAA.tsx +20 -4
  74. package/src/effects/SSAO.tsx +152 -41
  75. package/src/effects/ScanlineEffect.tsx +7 -7
  76. package/src/effects/SelectiveBloom.tsx +98 -126
  77. package/src/effects/Sepia.tsx +6 -4
  78. package/src/effects/ShockWave.tsx +32 -4
  79. package/src/effects/Texture.tsx +28 -23
  80. package/src/effects/TiltShift.tsx +32 -4
  81. package/src/effects/TiltShift2.tsx +156 -90
  82. package/src/effects/ToneMapping.tsx +19 -6
  83. package/src/effects/Vignette.tsx +7 -4
  84. package/src/effects/Water.tsx +50 -35
  85. package/src/index.ts +43 -40
  86. package/src/passes/DepthPicking.tsx +63 -0
  87. package/src/passes/N8AO.tsx +105 -0
  88. package/src/tests/Autofocus.test.tsx +38 -0
  89. package/src/tests/Bloom.test.tsx +76 -0
  90. package/src/tests/ChromaticAberration.test.tsx +81 -0
  91. package/src/tests/ColorDepth.test.tsx +71 -0
  92. package/src/tests/DepthOfField.test.tsx +130 -0
  93. package/src/tests/DepthPicking.test.tsx +250 -0
  94. package/src/tests/EffectComposer.test.tsx +1404 -0
  95. package/src/tests/EffectGroup.test.tsx +337 -0
  96. package/src/tests/Glitch.test.tsx +56 -0
  97. package/src/tests/GodRays.test.tsx +101 -0
  98. package/src/tests/Grid.test.tsx +34 -0
  99. package/src/tests/LUT.test.tsx +64 -0
  100. package/src/tests/N8AO.test.tsx +92 -0
  101. package/src/tests/Outline.test.tsx +211 -0
  102. package/src/tests/SSAO.test.tsx +140 -0
  103. package/src/tests/Selection.test.tsx +324 -0
  104. package/src/tests/SelectiveBloom.test.tsx +166 -0
  105. package/src/tests/ShockWave.test.tsx +95 -0
  106. package/src/tests/TiltShift.test.tsx +76 -0
  107. package/src/tests/createEffectComponent.test.tsx +286 -0
  108. package/src/tests/effects.smoke.test.tsx +238 -0
  109. package/src/tests/test-utils.tsx +96 -0
  110. package/src/tests/wrapEffect.test.tsx +209 -0
  111. package/src/util.tsx +221 -55
  112. package/src/wrapEffect.tsx +123 -0
  113. package/src/EffectComposer.test.tsx +0 -126
  114. package/src/effects/N8AO.tsx +0 -81
@@ -1,153 +1,122 @@
1
- import * as THREE from 'three'
2
- import React, {
3
- useRef,
4
- useContext,
5
- useState,
6
- useEffect,
7
- useCallback,
8
- forwardRef,
9
- useImperativeHandle,
10
- RefObject,
11
- useMemo,
12
- } from 'react'
13
- import { useThree, useFrame, createPortal, type Vector3 } from '@react-three/fiber'
14
- import { CopyPass, DepthPickingPass, DepthOfFieldEffect } from 'postprocessing'
15
- import { easing } from 'maath'
16
-
17
- import { DepthOfField } from './DepthOfField'
18
- import { EffectComposerContext } from '../EffectComposer'
19
-
20
- export type AutofocusProps = React.ComponentProps<typeof DepthOfField> & {
21
- target?: Vector3
22
- /** should the target follow the pointer */
23
- mouse?: boolean
24
- /** size of the debug green point */
25
- debug?: number
26
- /** manual update */
27
- manual?: boolean
28
- /** approximate time to reach the target */
29
- smoothTime?: number
30
- }
31
-
32
- export type AutofocusApi = {
33
- dofRef: RefObject<DepthOfFieldEffect | null>
34
- hitpoint: THREE.Vector3
35
- update: (delta: number, updateTarget: boolean) => void
36
- }
37
-
38
- export const Autofocus = /* @__PURE__ */ forwardRef<AutofocusApi, AutofocusProps>(
39
- (
40
- { target = undefined, mouse: followMouse = false, debug = undefined, manual = false, smoothTime = 0.25, ...props },
41
- fref
42
- ) => {
43
- const dofRef = useRef<DepthOfFieldEffect>(null)
44
- const hitpointRef = useRef<THREE.Mesh>(null)
45
- const targetRef = useRef<THREE.Mesh>(null)
46
-
47
- const scene = useThree(({ scene }) => scene)
48
- const pointer = useThree(({ pointer }) => pointer)
49
- const { composer, camera } = useContext(EffectComposerContext)
50
-
51
- // see: https://codesandbox.io/s/depthpickingpass-x130hg
52
- const [depthPickingPass] = useState(() => new DepthPickingPass())
53
- const [copyPass] = useState(() => new CopyPass())
54
- useEffect(() => {
55
- composer.addPass(depthPickingPass)
56
- composer.addPass(copyPass)
57
- return () => {
58
- composer.removePass(depthPickingPass)
59
- composer.removePass(copyPass)
60
- }
61
- }, [composer, depthPickingPass, copyPass])
62
-
63
- useEffect(() => {
64
- return () => {
65
- depthPickingPass.dispose()
66
- copyPass.dispose()
67
- }
68
- }, [depthPickingPass, copyPass])
69
-
70
- const [hitpoint] = useState(() => new THREE.Vector3(0, 0, 0))
71
-
72
- const [ndc] = useState(() => new THREE.Vector3(0, 0, 0))
73
- const getHit = useCallback(
74
- async (x: number, y: number) => {
75
- ndc.x = x
76
- ndc.y = y
77
- ndc.z = await depthPickingPass.readDepth(ndc)
78
- ndc.z = ndc.z * 2.0 - 1.0
79
- const hit = 1 - ndc.z > 0.0000001 // it is missed if ndc.z is close to 1
80
- return hit ? ndc.unproject(camera) : false
81
- },
82
- [ndc, depthPickingPass, camera]
83
- )
84
-
85
- const update = useCallback(
86
- async (delta: number, updateTarget = true) => {
87
- // Update hitpoint
88
- if (target) {
89
- hitpoint.set(...(target as [number, number, number]))
90
- } else {
91
- const { x, y } = followMouse ? pointer : { x: 0, y: 0 }
92
- const hit = await getHit(x, y)
93
- if (hit) hitpoint.copy(hit)
94
- }
95
-
96
- // Update target
97
- if (updateTarget && dofRef.current?.target) {
98
- if (smoothTime > 0 && delta > 0) {
99
- easing.damp3(dofRef.current.target, hitpoint, smoothTime, delta)
100
- } else {
101
- dofRef.current.target.copy(hitpoint)
102
- }
103
- }
104
- },
105
- [target, hitpoint, followMouse, getHit, smoothTime, pointer]
106
- )
107
-
108
- useFrame(async (_, delta) => {
109
- if (!manual) {
110
- update(delta)
111
- }
112
- if (hitpointRef.current) {
113
- hitpointRef.current.position.copy(hitpoint)
114
- }
115
- if (targetRef.current && dofRef.current?.target) {
116
- targetRef.current.position.copy(dofRef.current.target)
117
- }
118
- })
119
-
120
- // Ref API
121
- const api = useMemo<AutofocusApi>(
122
- () => ({
123
- dofRef,
124
- hitpoint,
125
- update,
126
- }),
127
- [hitpoint, update]
128
- )
129
- useImperativeHandle(fref, () => api, [api])
130
-
131
- return (
132
- <>
133
- {debug
134
- ? createPortal(
135
- <>
136
- <mesh ref={hitpointRef}>
137
- <sphereGeometry args={[debug, 16, 16]} />
138
- <meshBasicMaterial color="#00ff00" opacity={1} transparent depthWrite={false} />
139
- </mesh>
140
- <mesh ref={targetRef}>
141
- <sphereGeometry args={[debug / 2, 16, 16]} />
142
- <meshBasicMaterial color="#00ff00" opacity={0.5} transparent depthWrite={false} />
143
- </mesh>
144
- </>,
145
- scene
146
- )
147
- : null}
148
-
149
- <DepthOfField ref={dofRef} {...props} target={hitpoint} />
150
- </>
151
- )
152
- }
153
- )
1
+ import { createPortal, useFrame, useThree, type Vector3 as R3FVector3 } from '@react-three/fiber'
2
+ import { easing } from 'maath'
3
+ import { DepthOfFieldEffect } from 'postprocessing'
4
+ import {
5
+ Ref,
6
+ useCallback,
7
+ useImperativeHandle,
8
+ useMemo,
9
+ useRef,
10
+ useState,
11
+ type ComponentProps,
12
+ type RefObject,
13
+ } from 'react'
14
+ import { Mesh, Vector3 } from 'three'
15
+
16
+ import { DepthPicking, useDepthPicking, type DepthPickingApi } from '../passes/DepthPicking'
17
+ import { DepthOfField } from './DepthOfField'
18
+
19
+ export type AutofocusProps = Omit<ComponentProps<typeof DepthOfField>, 'ref'> & {
20
+ target?: R3FVector3
21
+ /** should the target follow the pointer */
22
+ mouse?: boolean
23
+ /** size of the debug green point */
24
+ debug?: number
25
+ /** manual update */
26
+ manual?: boolean
27
+ /** approximate time to reach the target */
28
+ smoothTime?: number
29
+ ref?: Ref<AutofocusApi>
30
+ }
31
+
32
+ export type AutofocusApi = {
33
+ dofRef: RefObject<DepthOfFieldEffect | null>
34
+ hitpoint: Vector3
35
+ update: (delta: number, updateTarget: boolean) => void
36
+ }
37
+
38
+ export function Autofocus({
39
+ target = undefined,
40
+ mouse: followMouse = false,
41
+ debug = undefined,
42
+ manual = false,
43
+ smoothTime = 0.25,
44
+ ref,
45
+ ...props
46
+ }: AutofocusProps) {
47
+ const dofRef = useRef<DepthOfFieldEffect>(null)
48
+ const pickRef = useRef<DepthPickingApi>(null)
49
+ const getHit = useDepthPicking(pickRef)
50
+ const hitpointMarkerRef = useRef<Mesh>(null)
51
+ const dofTargetMarkerRef = useRef<Mesh>(null)
52
+
53
+ const scene = useThree(({ scene }) => scene)
54
+ const pointer = useThree(({ pointer }) => pointer)
55
+
56
+ // A stable non-null value, purely to enable DepthOfField's own autoFocus
57
+ // mode (`target != null`) - the actual per-frame value is applied
58
+ // imperatively to dofRef.current.target below.
59
+ const [autoFocusMarker] = useState(() => new Vector3())
60
+ const [hitpoint] = useState(() => new Vector3())
61
+
62
+ const update = useCallback(
63
+ async (delta: number, updateTarget = true) => {
64
+ if (target) {
65
+ hitpoint.set(...(target as unknown as [number, number, number]))
66
+ } else {
67
+ const { x, y } = followMouse ? pointer : { x: 0, y: 0 }
68
+ const hit = await getHit(x, y)
69
+ if (hit) hitpoint.copy(hit)
70
+ }
71
+
72
+ if (updateTarget && dofRef.current?.target) {
73
+ if (smoothTime > 0 && delta > 0) {
74
+ easing.damp3(dofRef.current.target, hitpoint, smoothTime, delta)
75
+ } else {
76
+ dofRef.current.target.copy(hitpoint)
77
+ }
78
+ }
79
+ },
80
+ [target, hitpoint, followMouse, pointer, getHit, smoothTime]
81
+ )
82
+
83
+ useFrame((_, delta) => {
84
+ if (!manual) {
85
+ update(delta)
86
+ }
87
+ if (hitpointMarkerRef.current) {
88
+ hitpointMarkerRef.current.position.copy(hitpoint)
89
+ }
90
+ if (dofTargetMarkerRef.current && dofRef.current?.target) {
91
+ dofTargetMarkerRef.current.position.copy(dofRef.current.target)
92
+ }
93
+ })
94
+
95
+ // Ref API
96
+ const api = useMemo<AutofocusApi>(() => ({ dofRef, hitpoint, update }), [hitpoint, update])
97
+ useImperativeHandle(ref, () => api, [api])
98
+
99
+ return (
100
+ <>
101
+ <DepthPicking ref={pickRef} />
102
+
103
+ {debug
104
+ ? createPortal(
105
+ <>
106
+ <mesh ref={hitpointMarkerRef}>
107
+ <sphereGeometry args={[debug, 16, 16]} />
108
+ <meshBasicMaterial color="#00ff00" opacity={1} transparent depthWrite={false} />
109
+ </mesh>
110
+ <mesh ref={dofTargetMarkerRef}>
111
+ <sphereGeometry args={[debug / 2, 16, 16]} />
112
+ <meshBasicMaterial color="#00ff00" opacity={0.5} transparent depthWrite={false} />
113
+ </mesh>
114
+ </>,
115
+ scene
116
+ )
117
+ : null}
118
+
119
+ <DepthOfField ref={dofRef} {...props} target={autoFocusMarker} />
120
+ </>
121
+ )
122
+ }
@@ -1,6 +1,34 @@
1
- import { BloomEffect, BlendFunction } from 'postprocessing'
2
- import { wrapEffect } from '../util'
3
-
4
- export const Bloom = /* @__PURE__ */ wrapEffect(BloomEffect, {
5
- blendFunction: BlendFunction.ADD,
6
- })
1
+ import { BlendFunction, BloomEffect } from 'postprocessing'
2
+ import type { Ref } from 'react'
3
+ import { useMemo } from 'react'
4
+ import { createEffectComponent, type EffectOptions } from '../createEffectComponent'
5
+
6
+ type BloomOptions = EffectOptions<typeof BloomEffect>
7
+
8
+ const BloomImpl = /* @__PURE__ */ createEffectComponent<typeof BloomEffect, BloomOptions>(BloomEffect)
9
+
10
+ export type BloomProps = BloomOptions & { opacity?: number; ref?: Ref<BloomEffect> }
11
+
12
+ // luminanceThreshold/luminanceSmoothing/mipmapBlur/radius/levels/resolution*
13
+ // have no live setter in postprocessing - routed through args so they still
14
+ // work as plain props, just via reconstruction instead of mutation.
15
+ export function Bloom({
16
+ blendFunction = BlendFunction.ADD,
17
+ luminanceThreshold,
18
+ luminanceSmoothing,
19
+ mipmapBlur,
20
+ radius,
21
+ levels,
22
+ resolutionScale,
23
+ resolutionX,
24
+ resolutionY,
25
+ ...liveProps
26
+ }: BloomProps) {
27
+ const args = useMemo<[BloomOptions]>(
28
+ () => [
29
+ { luminanceThreshold, luminanceSmoothing, mipmapBlur, radius, levels, resolutionScale, resolutionX, resolutionY },
30
+ ],
31
+ [luminanceThreshold, luminanceSmoothing, mipmapBlur, radius, levels, resolutionScale, resolutionX, resolutionY]
32
+ )
33
+ return <BloomImpl blendFunction={blendFunction} args={args} {...liveProps} />
34
+ }
@@ -1,4 +1,7 @@
1
- import { BrightnessContrastEffect } from 'postprocessing'
2
- import { wrapEffect } from '../util'
3
-
4
- export const BrightnessContrast = /* @__PURE__ */ wrapEffect(BrightnessContrastEffect)
1
+ import { BrightnessContrastEffect } from 'postprocessing'
2
+ import { createEffectComponent, type EffectOptions } from '../createEffectComponent'
3
+
4
+ export const BrightnessContrast = /* @__PURE__ */ createEffectComponent<
5
+ typeof BrightnessContrastEffect,
6
+ EffectOptions<typeof BrightnessContrastEffect>
7
+ >(BrightnessContrastEffect)
@@ -1,5 +1,22 @@
1
- import { ChromaticAberrationEffect } from 'postprocessing'
2
- import { type EffectProps, wrapEffect } from '../util'
3
-
4
- export type ChromaticAberrationProps = EffectProps<typeof ChromaticAberrationEffect>
5
- export const ChromaticAberration = /* @__PURE__ */ wrapEffect(ChromaticAberrationEffect)
1
+ import type { ReactThreeFiber } from '@react-three/fiber'
2
+ import { ChromaticAberrationEffect } from 'postprocessing'
3
+ import type { Ref } from 'react'
4
+ import { createEffectComponent, type EffectOptions } from '../createEffectComponent'
5
+
6
+ // radialModulation/modulationOffset are typed as required by postprocessing's
7
+ // own .d.ts, but its JSDoc confirms both are optional with defaults - an
8
+ // upstream declaration bug, not a real constraint.
9
+ export type ChromaticAberrationProps = Omit<
10
+ EffectOptions<typeof ChromaticAberrationEffect>,
11
+ 'offset' | 'radialModulation' | 'modulationOffset'
12
+ > & {
13
+ offset?: ReactThreeFiber.Vector2
14
+ radialModulation?: boolean
15
+ modulationOffset?: number
16
+ ref?: Ref<ChromaticAberrationEffect>
17
+ }
18
+
19
+ export const ChromaticAberration = /* @__PURE__ */ createEffectComponent<
20
+ typeof ChromaticAberrationEffect,
21
+ ChromaticAberrationProps
22
+ >(ChromaticAberrationEffect)
@@ -1,15 +1,4 @@
1
- import { ColorAverageEffect, BlendFunction } from 'postprocessing'
2
- import React, { Ref, forwardRef, useMemo } from 'react'
3
-
4
- export type ColorAverageProps = Partial<{
5
- blendFunction: BlendFunction
6
- }>
7
-
8
- export const ColorAverage = /* @__PURE__ */ forwardRef<ColorAverageEffect, ColorAverageProps>(function ColorAverage(
9
- { blendFunction = BlendFunction.NORMAL }: ColorAverageProps,
10
- ref: Ref<ColorAverageEffect>
11
- ) {
12
- /** Because ColorAverage blendFunction is not an object but a number, we have to define a custom prop "blendFunction" */
13
- const effect = useMemo(() => new ColorAverageEffect(blendFunction), [blendFunction])
14
- return <primitive ref={ref} object={effect} dispose={null} />
15
- })
1
+ import { ColorAverageEffect } from 'postprocessing'
2
+ import { createEffectComponent } from '../createEffectComponent'
3
+
4
+ export const ColorAverage = /* @__PURE__ */ createEffectComponent<typeof ColorAverageEffect, object>(ColorAverageEffect)
@@ -1,4 +1,25 @@
1
- import { ColorDepthEffect } from 'postprocessing'
2
- import { wrapEffect } from '../util'
3
-
4
- export const ColorDepth = /* @__PURE__ */ wrapEffect(ColorDepthEffect)
1
+ import { ColorDepthEffect } from 'postprocessing'
2
+ import type { Ref } from 'react'
3
+ import { createEffectComponent, type EffectOptions } from '../createEffectComponent'
4
+
5
+ const ColorDepthImpl = /* @__PURE__ */ createEffectComponent<
6
+ typeof ColorDepthEffect,
7
+ Omit<EffectOptions<typeof ColorDepthEffect>, 'bits'> & { bitDepth?: number }
8
+ >(ColorDepthEffect)
9
+
10
+ export type ColorDepthProps = EffectOptions<typeof ColorDepthEffect> & {
11
+ opacity?: number
12
+ ref?: Ref<ColorDepthEffect>
13
+ }
14
+
15
+ // bits (the constructor's option name) has no live setter of its own in
16
+ // postprocessing - only the differently-named bitDepth does (bits is a
17
+ // plain, dead field on the instance). Renamed here so it still works as a
18
+ // plain prop after the initial mount.
19
+ export function ColorDepth({ bits, ...props }: ColorDepthProps) {
20
+ // Only set bitDepth when bits is actually provided - r3f's reset-on-
21
+ // removal only fires when a key is absent from the new props, not when
22
+ // it's present but undefined.
23
+ if (bits !== undefined) (props as Record<string, unknown>).bitDepth = bits
24
+ return <ColorDepthImpl {...props} />
25
+ }
@@ -1,4 +1,6 @@
1
- import { DepthEffect } from 'postprocessing'
2
- import { wrapEffect } from '../util'
3
-
4
- export const Depth = /* @__PURE__ */ wrapEffect(DepthEffect)
1
+ import { DepthEffect } from 'postprocessing'
2
+ import { createEffectComponent, type EffectOptions } from '../createEffectComponent'
3
+
4
+ export const Depth = /* @__PURE__ */ createEffectComponent<typeof DepthEffect, EffectOptions<typeof DepthEffect>>(
5
+ DepthEffect
6
+ )
@@ -1,89 +1,109 @@
1
- import { DepthOfFieldEffect, MaskFunction } from 'postprocessing'
2
- import { Ref, forwardRef, useMemo, useEffect, useContext } from 'react'
3
- import { ReactThreeFiber } from '@react-three/fiber'
4
- import { type DepthPackingStrategies, type Texture, Vector3 } from 'three'
5
- import { EffectComposerContext } from '../EffectComposer'
6
-
7
- type DOFProps = ConstructorParameters<typeof DepthOfFieldEffect>[1] &
8
- Partial<{
9
- target: ReactThreeFiber.Vector3
10
- depthTexture: {
11
- texture: Texture
12
- // TODO: narrow to DepthPackingStrategies
13
- packing: number
14
- }
15
- // TODO: not used
16
- blur: number
17
- }>
18
-
19
- export const DepthOfField = /* @__PURE__ */ forwardRef(function DepthOfField(
20
- {
21
- blendFunction,
22
- worldFocusDistance,
23
- worldFocusRange,
24
- focusDistance,
25
- focusRange,
26
- focalLength,
27
- bokehScale,
28
- resolutionScale,
29
- resolutionX,
30
- resolutionY,
31
- width,
32
- height,
33
- target,
34
- depthTexture,
35
- ...props
36
- }: DOFProps,
37
- ref: Ref<DepthOfFieldEffect>
38
- ) {
39
- const { camera } = useContext(EffectComposerContext)
40
- const autoFocus = target != null
41
- const effect = useMemo(() => {
42
- const effect = new DepthOfFieldEffect(camera, {
43
- blendFunction,
44
- worldFocusDistance,
45
- worldFocusRange,
46
- focusDistance,
47
- focusRange,
48
- focalLength,
49
- bokehScale,
50
- resolutionScale,
51
- resolutionX,
52
- resolutionY,
53
- width,
54
- height,
55
- })
56
- // Creating a target enables autofocus, R3F will set via props
57
- if (autoFocus) effect.target = new Vector3()
58
- // Depth texture for depth picking with optional packing strategy
59
- if (depthTexture) effect.setDepthTexture(depthTexture.texture, depthTexture.packing as DepthPackingStrategies)
60
- // Temporary fix that restores DOF 6.21.3 behavior, everything since then lets shapes leak through the blur
61
- const maskPass = (effect as any).maskPass
62
- maskPass.maskFunction = MaskFunction.MULTIPLY_RGB_SET_ALPHA
63
- return effect
64
- }, [
65
- camera,
66
- blendFunction,
67
- worldFocusDistance,
68
- worldFocusRange,
69
- focusDistance,
70
- focusRange,
71
- focalLength,
72
- bokehScale,
73
- resolutionScale,
74
- resolutionX,
75
- resolutionY,
76
- width,
77
- height,
78
- autoFocus,
79
- depthTexture,
80
- ])
81
-
82
- useEffect(() => {
83
- return () => {
84
- effect.dispose()
85
- }
86
- }, [effect])
87
-
88
- return <primitive {...props} ref={ref} object={effect} target={target} />
89
- })
1
+ import type { ReactThreeFiber } from '@react-three/fiber'
2
+ import { DepthOfFieldEffect, MaskFunction } from 'postprocessing'
3
+ import type { Ref } from 'react'
4
+ import { use, useMemo } from 'react'
5
+ import { type DepthPackingStrategies, type Texture, Vector3 } from 'three'
6
+ import { EffectComposerContext } from '../EffectComposer'
7
+ import { applyPierced, readPierced, useDispose, useLiveDefaults } from '../util'
8
+
9
+ export type DepthOfFieldProps = ConstructorParameters<typeof DepthOfFieldEffect>[1] &
10
+ Partial<{
11
+ ref: Ref<DepthOfFieldEffect>
12
+ target: ReactThreeFiber.Vector3
13
+ depthTexture: {
14
+ texture: Texture
15
+ // TODO: narrow to DepthPackingStrategies
16
+ packing: number
17
+ }
18
+ // TODO: not used
19
+ blur: number
20
+ }>
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
+
53
+ export function DepthOfField({
54
+ ref,
55
+ blendFunction,
56
+ worldFocusDistance,
57
+ worldFocusRange,
58
+ focusDistance,
59
+ focusRange,
60
+ focalLength,
61
+ bokehScale,
62
+ resolutionScale,
63
+ resolutionX,
64
+ resolutionY,
65
+ width,
66
+ height,
67
+ target,
68
+ depthTexture,
69
+ ...props
70
+ }: DepthOfFieldProps) {
71
+ const { camera } = use(EffectComposerContext)
72
+ const autoFocus = target != null
73
+
74
+ const effect = useMemo(() => {
75
+ const effect = new DepthOfFieldEffect(camera, {
76
+ worldFocusDistance,
77
+ worldFocusRange,
78
+ focalLength,
79
+ resolutionScale,
80
+ resolutionX,
81
+ resolutionY,
82
+ width,
83
+ height,
84
+ })
85
+ // Creating a target enables autofocus, R3F will set via props
86
+ if (autoFocus) effect.target = new Vector3()
87
+ // Temporary fix that restores DOF 6.21.3 behavior, everything since then lets shapes leak through the blur
88
+ effect.maskFunction = MaskFunction.MULTIPLY_RGB_SET_ALPHA
89
+ return effect
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
+ )
105
+
106
+ useDispose(effect)
107
+
108
+ return <primitive {...props} object={effect} ref={ref} target={target} />
109
+ }
@@ -1,4 +1,7 @@
1
- import { DotScreenEffect } from 'postprocessing'
2
- import { wrapEffect } from '../util'
3
-
4
- export const DotScreen = /* @__PURE__ */ wrapEffect(DotScreenEffect)
1
+ import { DotScreenEffect } from 'postprocessing'
2
+ import { createEffectComponent, type EffectOptions } from '../createEffectComponent'
3
+
4
+ export const DotScreen = /* @__PURE__ */ createEffectComponent<
5
+ typeof DotScreenEffect,
6
+ EffectOptions<typeof DotScreenEffect>
7
+ >(DotScreenEffect)
@@ -1,4 +1,6 @@
1
- import { FXAAEffect } from 'postprocessing'
2
- import { wrapEffect } from '../util'
3
-
4
- export const FXAA = /* @__PURE__ */ wrapEffect(FXAAEffect)
1
+ import { FXAAEffect } from 'postprocessing'
2
+ import { createEffectComponent, type EffectOptions } from '../createEffectComponent'
3
+
4
+ export const FXAA = /* @__PURE__ */ createEffectComponent<typeof FXAAEffect, EffectOptions<typeof FXAAEffect>>(
5
+ FXAAEffect
6
+ )