@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,14 +1,15 @@
1
1
  // Created by Anderson Mancini 2023
2
2
  // From https://github.com/ektogamat/R3F-Ultimate-Lens-Flare
3
3
 
4
- import { useFrame, useThree } from '@react-three/fiber'
4
+ import { useFrame, useThree, type ReactThreeFiber } from '@react-three/fiber'
5
5
  import { easing } from 'maath'
6
6
  import { BlendFunction, Effect } from 'postprocessing'
7
- import { useContext, useEffect, useRef, useState } from 'react'
7
+ import { useContext, useEffect, useRef, useState, type Ref } from 'react'
8
8
  import { Color, Mesh, Texture, Uniform, Vector2, Vector3 } from 'three'
9
9
 
10
+ import { createEffectComponent } from '../createEffectComponent'
10
11
  import { EffectComposerContext } from '../EffectComposer'
11
- import { wrapEffect } from '../wrapEffect'
12
+ import { useMergeRefs, useVector3 } from '../util'
12
13
 
13
14
  const LensFlareShader = {
14
15
  fragmentShader: /* glsl */ `
@@ -441,26 +442,26 @@ type LensFlareEffectOptions = {
441
442
 
442
443
  export class LensFlareEffect extends Effect {
443
444
  constructor({
444
- blendFunction,
445
- enabled,
446
- glareSize,
447
- lensPosition,
448
- screenRes,
449
- starPoints,
450
- flareSize,
451
- flareSpeed,
452
- flareShape,
453
- animated,
454
- anamorphic,
455
- colorGain,
456
- lensDirtTexture,
457
- haloScale,
458
- secondaryGhosts,
459
- aditionalStreaks,
460
- ghostScale,
461
- opacity,
462
- starBurst,
463
- }: LensFlareEffectOptions) {
445
+ blendFunction = BlendFunction.NORMAL,
446
+ enabled = true,
447
+ glareSize = 0.2,
448
+ lensPosition = new Vector3(-25, 6, -60),
449
+ screenRes = new Vector2(0, 0),
450
+ starPoints = 6,
451
+ flareSize = 0.01,
452
+ flareSpeed = 0.01,
453
+ flareShape = 0.01,
454
+ animated = true,
455
+ anamorphic = false,
456
+ colorGain = new Color(20, 20, 20),
457
+ lensDirtTexture = null,
458
+ haloScale = 0.5,
459
+ secondaryGhosts = true,
460
+ aditionalStreaks = true,
461
+ ghostScale = 0.0,
462
+ opacity = 1.0,
463
+ starBurst = false,
464
+ }: Partial<LensFlareEffectOptions> = {}) {
464
465
  super('LensFlareEffect', LensFlareShader.fragmentShader, {
465
466
  blendFunction,
466
467
  uniforms: new Map<string, Uniform>([
@@ -493,40 +494,191 @@ export class LensFlareEffect extends Effect {
493
494
  time.value += deltaTime
494
495
  }
495
496
  }
497
+
498
+ private u<T>(name: string): T {
499
+ return this.uniforms.get(name)!.value
500
+ }
501
+
502
+ private setU(name: string, value: unknown): void {
503
+ this.uniforms.get(name)!.value = value
504
+ }
505
+
506
+ get enabled(): boolean {
507
+ return this.u('enabled')
508
+ }
509
+ set enabled(value: boolean) {
510
+ this.setU('enabled', value)
511
+ }
512
+
513
+ get glareSize(): number {
514
+ return this.u('glareSize')
515
+ }
516
+ set glareSize(value: number) {
517
+ this.setU('glareSize', value)
518
+ }
519
+
520
+ get lensPosition(): Vector3 {
521
+ return this.u('lensPosition')
522
+ }
523
+ set lensPosition(value: Vector3) {
524
+ this.setU('lensPosition', value)
525
+ }
526
+
527
+ get screenRes(): Vector2 {
528
+ return this.u('screenRes')
529
+ }
530
+ set screenRes(value: Vector2) {
531
+ this.setU('screenRes', value)
532
+ }
533
+
534
+ get starPoints(): number {
535
+ return this.u('starPoints')
536
+ }
537
+ set starPoints(value: number) {
538
+ this.setU('starPoints', value)
539
+ }
540
+
541
+ get flareSize(): number {
542
+ return this.u('flareSize')
543
+ }
544
+ set flareSize(value: number) {
545
+ this.setU('flareSize', value)
546
+ }
547
+
548
+ get flareSpeed(): number {
549
+ return this.u('flareSpeed')
550
+ }
551
+ set flareSpeed(value: number) {
552
+ this.setU('flareSpeed', value)
553
+ }
554
+
555
+ get flareShape(): number {
556
+ return this.u('flareShape')
557
+ }
558
+ set flareShape(value: number) {
559
+ this.setU('flareShape', value)
560
+ }
561
+
562
+ get animated(): boolean {
563
+ return this.u('animated')
564
+ }
565
+ set animated(value: boolean) {
566
+ this.setU('animated', value)
567
+ }
568
+
569
+ get anamorphic(): boolean {
570
+ return this.u('anamorphic')
571
+ }
572
+ set anamorphic(value: boolean) {
573
+ this.setU('anamorphic', value)
574
+ }
575
+
576
+ get colorGain(): Color {
577
+ return this.u('colorGain')
578
+ }
579
+ set colorGain(value: Color) {
580
+ this.setU('colorGain', value)
581
+ }
582
+
583
+ get lensDirtTexture(): Texture | null {
584
+ return this.u('lensDirtTexture')
585
+ }
586
+ set lensDirtTexture(value: Texture | null) {
587
+ this.setU('lensDirtTexture', value)
588
+ }
589
+
590
+ get haloScale(): number {
591
+ return this.u('haloScale')
592
+ }
593
+ set haloScale(value: number) {
594
+ this.setU('haloScale', value)
595
+ }
596
+
597
+ get secondaryGhosts(): boolean {
598
+ return this.u('secondaryGhosts')
599
+ }
600
+ set secondaryGhosts(value: boolean) {
601
+ this.setU('secondaryGhosts', value)
602
+ }
603
+
604
+ get aditionalStreaks(): boolean {
605
+ return this.u('aditionalStreaks')
606
+ }
607
+ set aditionalStreaks(value: boolean) {
608
+ this.setU('aditionalStreaks', value)
609
+ }
610
+
611
+ get ghostScale(): number {
612
+ return this.u('ghostScale')
613
+ }
614
+ set ghostScale(value: number) {
615
+ this.setU('ghostScale', value)
616
+ }
617
+
618
+ get starBurst(): boolean {
619
+ return this.u('starBurst')
620
+ }
621
+ set starBurst(value: boolean) {
622
+ this.setU('starBurst', value)
623
+ }
624
+
625
+ get opacity(): number {
626
+ return this.u('opacity')
627
+ }
628
+ set opacity(value: number) {
629
+ this.setU('opacity', value)
630
+ }
496
631
  }
497
632
 
498
- type LensFlareProps = {
633
+ type LensFlareProps = Omit<Partial<LensFlareEffectOptions>, 'lensPosition'> & {
499
634
  /** Position of the effect */
500
- lensPosition?: Vector3
635
+ lensPosition?: ReactThreeFiber.Vector3
501
636
  /** The time that it takes to fade the occlusion */
502
637
  smoothTime?: number
503
- } & Partial<LensFlareEffectOptions>
504
-
505
- const LensFlareWrapped = /* @__PURE__ */ wrapEffect(LensFlareEffect)
506
-
507
- export const LensFlare = ({
508
- smoothTime = 0.07,
509
- //
510
- blendFunction = BlendFunction.NORMAL,
511
- enabled = true,
512
- glareSize = 0.2,
513
- lensPosition = new Vector3(-25, 6, -60),
514
- screenRes = new Vector2(0, 0),
515
- starPoints = 6,
516
- flareSize = 0.01,
517
- flareSpeed = 0.01,
518
- flareShape = 0.01,
519
- animated = true,
520
- anamorphic = false,
521
- colorGain = new Color(20, 20, 20),
522
- lensDirtTexture = null,
523
- haloScale = 0.5,
524
- secondaryGhosts = true,
525
- aditionalStreaks = true,
526
- ghostScale = 0.0,
527
- opacity = 1.0,
528
- starBurst = false,
529
- }: LensFlareProps) => {
638
+ ref?: Ref<LensFlareEffect>
639
+ }
640
+
641
+ const LensFlareWrapped = /* @__PURE__ */ createEffectComponent<
642
+ typeof LensFlareEffect,
643
+ Partial<LensFlareEffectOptions> & { ref?: Ref<LensFlareEffect> }
644
+ >(LensFlareEffect)
645
+
646
+ // Stable identity so a parent re-render (e.g. a Leva control) doesn't hand
647
+ // r3f a brand-new Vector2 every time - r3f would then `.copy()` it onto the
648
+ // uniform in place, stomping the viewport-driven value the effect below
649
+ // maintains and leaving screenRes at (0, 0) until the next resize. Never
650
+ // mutated (only ever copied *from*), so it's safe to share across instances.
651
+ const DEFAULT_SCREEN_RES = /* @__PURE__ */ new Vector2(0, 0)
652
+
653
+ // Same reasoning as DEFAULT_SCREEN_RES above - shared, read-only default.
654
+ const DEFAULT_LENS_POSITION = /* @__PURE__ */ new Vector3(-25, 6, -60)
655
+
656
+ export const LensFlare = (props: LensFlareProps) => {
657
+ const {
658
+ ref: forwardedRef,
659
+ smoothTime = 0.07,
660
+ blendFunction = BlendFunction.NORMAL,
661
+ enabled = true,
662
+ glareSize = 0.2,
663
+ screenRes,
664
+ starPoints = 6,
665
+ flareSize = 0.01,
666
+ flareSpeed = 0.01,
667
+ flareShape = 0.01,
668
+ animated = true,
669
+ anamorphic = false,
670
+ colorGain = new Color(20, 20, 20),
671
+ lensDirtTexture = null,
672
+ haloScale = 0.5,
673
+ secondaryGhosts = true,
674
+ aditionalStreaks = true,
675
+ ghostScale = 0.0,
676
+ opacity = 1.0,
677
+ starBurst = false,
678
+ } = props
679
+
680
+ const lensPosition = useVector3({ lensPosition: props.lensPosition ?? DEFAULT_LENS_POSITION }, 'lensPosition')
681
+
530
682
  const viewport = useThree(({ viewport }) => viewport)
531
683
  const raycaster = useThree(({ raycaster }) => raycaster)
532
684
  const { scene, camera } = useContext(EffectComposerContext)
@@ -534,6 +686,7 @@ export const LensFlare = ({
534
686
  const [projectedPosition] = useState(() => new Vector3())
535
687
 
536
688
  const ref = useRef<LensFlareEffect>(null)
689
+ const setRef = useMergeRefs(ref, forwardedRef)
537
690
 
538
691
  useFrame((_, delta) => {
539
692
  if (!ref?.current) return
@@ -541,7 +694,7 @@ export const LensFlare = ({
541
694
  const uOpacity = ref.current.uniforms.get('opacity')
542
695
  if (!uLensPosition || !uOpacity) return
543
696
 
544
- let target = 1
697
+ let target = 0
545
698
 
546
699
  projectedPosition.copy(lensPosition).project(camera)
547
700
  if (projectedPosition.z > 1) return
@@ -555,6 +708,7 @@ export const LensFlare = ({
555
708
  const intersects = raycaster.intersectObjects(scene.children, true)
556
709
  const { object } = intersects[0] || {}
557
710
  if (object) {
711
+ target = 1
558
712
  if (object.userData?.lensflare === 'no-occlusion') {
559
713
  target = 0
560
714
  } else if (object instanceof Mesh) {
@@ -586,12 +740,12 @@ export const LensFlare = ({
586
740
 
587
741
  return (
588
742
  <LensFlareWrapped
589
- ref={ref}
743
+ ref={setRef}
590
744
  blendFunction={blendFunction}
591
745
  enabled={enabled}
592
746
  glareSize={glareSize}
593
747
  lensPosition={lensPosition}
594
- screenRes={screenRes}
748
+ screenRes={screenRes ?? DEFAULT_SCREEN_RES}
595
749
  starPoints={starPoints}
596
750
  flareSize={flareSize}
597
751
  flareSpeed={flareSpeed}
@@ -1,4 +1,16 @@
1
1
  import { BlendFunction, NoiseEffect } from 'postprocessing'
2
- import { wrapEffect } from '../wrapEffect'
2
+ import type { Ref } from 'react'
3
+ import { createEffectComponent, type EffectOptions } from '../createEffectComponent'
3
4
 
4
- export const Noise = /* @__PURE__ */ wrapEffect(NoiseEffect, { blendFunction: BlendFunction.COLOR_DODGE })
5
+ const NoiseImpl = /* @__PURE__ */ createEffectComponent<typeof NoiseEffect, EffectOptions<typeof NoiseEffect>>(
6
+ NoiseEffect
7
+ )
8
+
9
+ export type NoiseProps = EffectOptions<typeof NoiseEffect> & {
10
+ opacity?: number
11
+ ref?: Ref<NoiseEffect>
12
+ }
13
+
14
+ export function Noise({ blendFunction = BlendFunction.COLOR_DODGE, ...props }: NoiseProps) {
15
+ return <NoiseImpl blendFunction={blendFunction} {...props} />
16
+ }
@@ -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
+ }