foundry-component-library 0.2.9 → 0.2.12

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 (39) hide show
  1. package/lib/components/Capabilities/Item.tsx +4 -7
  2. package/lib/components/Capabilities/index.tsx +1 -1
  3. package/lib/components/ServiceHubsTeaserEffects/TileBalls.tsx +35 -0
  4. package/lib/components/ServiceHubsTeaserEffects/TileFluid.tsx +29 -0
  5. package/lib/components/ServiceHubsTeaserEffects/TileGlass.tsx +23 -0
  6. package/lib/components/ServiceHubsTeaserEffects/TileRays.tsx +98 -0
  7. package/lib/components/ServiceHubsTeaserEffects/bubbles/Bubble.tsx +48 -0
  8. package/lib/components/ServiceHubsTeaserEffects/bubbles/Bubbles.tsx +45 -0
  9. package/lib/components/ServiceHubsTeaserEffects/fluid/Fluid.tsx +192 -0
  10. package/lib/components/ServiceHubsTeaserEffects/fluid/constant.ts +23 -0
  11. package/lib/components/ServiceHubsTeaserEffects/fluid/effect/Fluid.tsx +22 -0
  12. package/lib/components/ServiceHubsTeaserEffects/fluid/effect/FluidEffect.tsx +61 -0
  13. package/lib/components/ServiceHubsTeaserEffects/fluid/glsl/advection.frag +16 -0
  14. package/lib/components/ServiceHubsTeaserEffects/fluid/glsl/base.vert +26 -0
  15. package/lib/components/ServiceHubsTeaserEffects/fluid/glsl/clear.frag +7 -0
  16. package/lib/components/ServiceHubsTeaserEffects/fluid/glsl/composite.frag +41 -0
  17. package/lib/components/ServiceHubsTeaserEffects/fluid/glsl/curl.frag +22 -0
  18. package/lib/components/ServiceHubsTeaserEffects/fluid/glsl/divergence.frag +41 -0
  19. package/lib/components/ServiceHubsTeaserEffects/fluid/glsl/gradientSubstract.frag +26 -0
  20. package/lib/components/ServiceHubsTeaserEffects/fluid/glsl/pressure.frag +28 -0
  21. package/lib/components/ServiceHubsTeaserEffects/fluid/glsl/splat.frag +19 -0
  22. package/lib/components/ServiceHubsTeaserEffects/fluid/glsl/vorticity.frag +36 -0
  23. package/lib/components/ServiceHubsTeaserEffects/fluid/glsl.d.ts +14 -0
  24. package/lib/components/ServiceHubsTeaserEffects/fluid/hooks/useDoubleFBO.tsx +37 -0
  25. package/lib/components/ServiceHubsTeaserEffects/fluid/hooks/useFBOs.tsx +71 -0
  26. package/lib/components/ServiceHubsTeaserEffects/fluid/hooks/useMaterials.tsx +242 -0
  27. package/lib/components/ServiceHubsTeaserEffects/fluid/hooks/usePointer.tsx +54 -0
  28. package/lib/components/ServiceHubsTeaserEffects/fluid/index.ts +5 -0
  29. package/lib/components/ServiceHubsTeaserEffects/fluid/types.ts +27 -0
  30. package/lib/components/ServiceHubsTeaserEffects/fluid/utils.ts +12 -0
  31. package/lib/components/ServiceHubsTeaserEffects/glass/Lens.tsx +75 -0
  32. package/lib/components/ServiceHubsTeaserEffects/index.tsx +70 -0
  33. package/lib/components/ServiceHubsTeaserEffects/rays/LightSource.tsx +65 -0
  34. package/lib/components/ServiceHubsTeaserEffects/rays/noise.js +97 -0
  35. package/lib/components/ServiceHubsTeaserEffects/styles.module.scss +135 -0
  36. package/lib/index.ts +2 -0
  37. package/lib/types/glsl.d.ts +14 -0
  38. package/lib/vite-env.d.ts +5 -0
  39. package/package.json +6 -2
@@ -20,7 +20,7 @@ const Item = ({
20
20
  cases: Case[];
21
21
  active: string;
22
22
  setActive: Dispatch<SetStateAction<string>>;
23
- Link?: typeof LinkType | React.ElementType;
23
+ Link?: typeof LinkType;
24
24
  Image: NextImage;
25
25
  }) => {
26
26
  const casesRef = useRef<HTMLDivElement>(null);
@@ -75,8 +75,7 @@ const Item = ({
75
75
  } else {
76
76
  setActive(title);
77
77
  }
78
- }}
79
- >
78
+ }}>
80
79
  {isActive && <span>-</span>}
81
80
  {!isActive && <span>+</span>}
82
81
  </button>
@@ -100,8 +99,7 @@ const Item = ({
100
99
  overflowX: "auto",
101
100
  scrollbarWidth: "none",
102
101
  msOverflowStyle: "none",
103
- }}
104
- >
102
+ }}>
105
103
  {Link &&
106
104
  cases.map((item) => {
107
105
  const { thumbnailImage, mainImage } = item.case;
@@ -112,8 +110,7 @@ const Item = ({
112
110
  key={item.uri}
113
111
  className={styles.case}
114
112
  draggable={false}
115
- onClick={handleClick}
116
- >
113
+ onClick={handleClick}>
117
114
  <div className={styles.caseImage}>
118
115
  {Image && (
119
116
  <Image
@@ -16,7 +16,7 @@ const Capabilities = ({
16
16
  text: string;
17
17
  cases: Case[];
18
18
  }[];
19
- Link?: typeof LinkType | React.ElementType;
19
+ Link?: typeof LinkType;
20
20
  Image: NextImage;
21
21
  }) => {
22
22
  const [active, setActive] = useState("");
@@ -0,0 +1,35 @@
1
+ import { Canvas } from "@react-three/fiber";
2
+ import { Environment, Text } from "@react-three/drei";
3
+ import { EffectComposer, N8AO, TiltShift2 } from "@react-three/postprocessing";
4
+ import Bubbles from "./bubbles/Bubbles";
5
+
6
+ const TileBalls = () => {
7
+ return (
8
+ <Canvas
9
+ shadows
10
+ dpr={[1, 2]}
11
+ gl={{ antialias: false }}
12
+ camera={{ fov: 15, position: [0, 0, 20] }}>
13
+ <color attach="background" args={["#380de8"]} />
14
+ <fog attach="fog" args={["red", 20, -5]} />
15
+ <ambientLight intensity={1.5} />
16
+ <pointLight position={[10, 10, 10]} intensity={1} castShadow />
17
+ <Bubbles />
18
+ <EffectComposer>
19
+ <N8AO aoRadius={6} intensity={2} color="red" />
20
+ <TiltShift2 blur={0.1} />
21
+ </EffectComposer>
22
+ <Environment preset="city" />
23
+ <Text
24
+ position={[0, 0, 0]}
25
+ fontSize={0.4}
26
+ textAlign="center"
27
+ fontWeight={600}
28
+ color="#fff">
29
+ Content and{"\n"}Campaigning
30
+ </Text>
31
+ </Canvas>
32
+ );
33
+ };
34
+
35
+ export default TileBalls;
@@ -0,0 +1,29 @@
1
+ import { Text } from "@react-three/drei";
2
+ import { Canvas } from "@react-three/fiber";
3
+ import { EffectComposer } from "@react-three/postprocessing";
4
+ import { Fluid } from "./fluid/Fluid";
5
+
6
+ const TileFluid = () => {
7
+ return (
8
+ <Canvas
9
+ style={{
10
+ height: "100%",
11
+ width: "100%",
12
+ }}
13
+ camera={{ position: [0, 0, 20], fov: 15 }}>
14
+ <Text
15
+ position={[0, 0, 0]}
16
+ fontSize={0.4}
17
+ textAlign="center"
18
+ fontWeight={600}
19
+ color="#380de8">
20
+ Branding &{"\n"}Corporate ID
21
+ </Text>
22
+ <EffectComposer>
23
+ <Fluid backgroundColor="#fbff00" fluidColor="#380de8" rainbow={true} />
24
+ </EffectComposer>
25
+ </Canvas>
26
+ );
27
+ };
28
+
29
+ export default TileFluid;
@@ -0,0 +1,23 @@
1
+ import { Canvas } from "@react-three/fiber";
2
+ import { Text } from "@react-three/drei";
3
+ import Lens from "./glass/Lens";
4
+
5
+ const TileGlass = () => {
6
+ return (
7
+ <Canvas camera={{ position: [0, 0, 20], fov: 15 }}>
8
+ <Lens>
9
+ <Text
10
+ position={[0, 0, 0]}
11
+ fontSize={0.4}
12
+ textAlign="center"
13
+ fontWeight={600}
14
+ color="#491b11">
15
+ Strategy &{"\n"}
16
+ Positioning
17
+ </Text>
18
+ </Lens>
19
+ </Canvas>
20
+ );
21
+ };
22
+
23
+ export default TileGlass;
@@ -0,0 +1,98 @@
1
+ "use client";
2
+
3
+ import React, { useRef } from "react";
4
+ import { Canvas, useFrame, useThree } from "@react-three/fiber";
5
+ import { useGLTF } from "@react-three/drei";
6
+ import { MeshBasicMaterial, DoubleSide, Mesh, Group } from "three";
7
+ import { LightSource } from "./rays/LightSource";
8
+ import {
9
+ EffectComposer,
10
+ EffectPass,
11
+ GodRaysEffect,
12
+ RenderPass,
13
+ } from "postprocessing";
14
+
15
+ function Slab({ url }: { url: string }) {
16
+ const gltf = useGLTF(url);
17
+ const slabRef = useRef<Group>(null);
18
+ const { camera, scene, gl } = useThree();
19
+
20
+ // GodRays light
21
+ const lightRef = useRef<Mesh>(null);
22
+ useFrame(({ pointer }) => {
23
+ if (lightRef.current) {
24
+ const LIGHT_RANGE = 5;
25
+ lightRef.current.position.x = pointer.x * LIGHT_RANGE;
26
+ lightRef.current.position.y = pointer.y * LIGHT_RANGE;
27
+ }
28
+ });
29
+
30
+ React.useEffect(() => {
31
+ if (!gltf) return;
32
+
33
+ const slab = gltf.scene;
34
+ slab.rotation.x = Math.PI / 2;
35
+
36
+ slab.traverse((obj) => {
37
+ if (obj instanceof Mesh) {
38
+ obj.material = new MeshBasicMaterial({
39
+ color: 0x491b11,
40
+ side: DoubleSide,
41
+ });
42
+ }
43
+ });
44
+
45
+ if (slabRef.current) {
46
+ slabRef.current.add(slab);
47
+ }
48
+
49
+ // Setup postprocessing
50
+ const composer = new EffectComposer(gl);
51
+ composer.addPass(new RenderPass(scene, camera));
52
+
53
+ if (!lightRef.current) return;
54
+
55
+ const gre = new GodRaysEffect(camera, lightRef.current, {
56
+ height: 480,
57
+ kernelSize: 2,
58
+ density: 1,
59
+ decay: 0.8,
60
+ weight: 0.5,
61
+ exposure: 0.3,
62
+ samples: 20,
63
+ clampMax: 0.95,
64
+ });
65
+
66
+ composer.addPass(new EffectPass(camera, gre));
67
+
68
+ // Render loop override
69
+ // const originalSetAnimationLoop = gl.setAnimationLoop;
70
+ gl.setAnimationLoop(() => composer.render());
71
+
72
+ return () => {
73
+ // Cleanup
74
+ // gl.setAnimationLoop(originalSetAnimationLoop);
75
+ composer.dispose();
76
+ };
77
+ }, [gltf, gl, scene, camera]);
78
+
79
+ return (
80
+ <>
81
+ {/* <mesh ref={lightRef} position={[0, 0, -10]} /> */}
82
+ <group ref={slabRef} />
83
+ <LightSource ref={lightRef} position={[0, 0, -20]} />
84
+ </>
85
+ );
86
+ }
87
+
88
+ export default function SlabScene() {
89
+ return (
90
+ <Canvas
91
+ camera={{ fov: 60, position: [0, 0, 10], near: 1, far: 100 }}
92
+ gl={{ antialias: true, powerPreference: "high-performance" }}>
93
+ {/* <ambientLight intensity={0.2} /> */}
94
+ {/* <OrbitControls /> */}
95
+ <Slab url="/slab-with-text.glb" />
96
+ </Canvas>
97
+ );
98
+ }
@@ -0,0 +1,48 @@
1
+ import { useRef } from "react";
2
+ import { useFrame } from "@react-three/fiber";
3
+ import { Instance } from "@react-three/drei";
4
+ import type { Mesh } from "three";
5
+
6
+ const Bubble = ({
7
+ factor,
8
+ speed,
9
+ xFactor,
10
+ yFactor,
11
+ zFactor,
12
+ }: {
13
+ factor: number;
14
+ speed: number;
15
+ xFactor: number;
16
+ yFactor: number;
17
+ zFactor: number;
18
+ }) => {
19
+ const ref = useRef<Mesh>(null);
20
+
21
+ useFrame((state) => {
22
+ const t = factor + state.clock.elapsedTime * (speed / 2);
23
+
24
+ ref.current!.scale.setScalar(Math.max(1.5, Math.cos(t) * 5));
25
+
26
+ ref.current!.position.set(
27
+ Math.cos(t) +
28
+ Math.sin(t * 1) / 10 +
29
+ xFactor +
30
+ Math.cos((t / 10) * factor) +
31
+ (Math.sin(t * 1) * factor) / 10,
32
+ Math.sin(t) +
33
+ Math.cos(t * 2) / 10 +
34
+ yFactor +
35
+ Math.sin((t / 10) * factor) +
36
+ (Math.cos(t * 2) * factor) / 10,
37
+ Math.sin(t) +
38
+ Math.cos(t * 2) / 10 +
39
+ zFactor +
40
+ Math.cos((t / 10) * factor) +
41
+ (Math.sin(t * 3) * factor) / 4
42
+ );
43
+ });
44
+
45
+ return <Instance ref={ref} />;
46
+ };
47
+
48
+ export default Bubble;
@@ -0,0 +1,45 @@
1
+ import { MathUtils, InstancedMesh } from "three";
2
+ import { useRef } from "react";
3
+ import { useFrame } from "@react-three/fiber";
4
+ import { Instances } from "@react-three/drei";
5
+ import Bubble from "./Bubble";
6
+
7
+ const particles = Array.from({ length: 20 }, () => ({
8
+ factor: MathUtils.randInt(20, 100),
9
+ speed: MathUtils.randFloat(0.01, 0.75),
10
+ xFactor: MathUtils.randFloatSpread(3),
11
+ yFactor: MathUtils.randFloatSpread(3),
12
+ zFactor: MathUtils.randFloatSpread(5),
13
+ }));
14
+
15
+ const Bubbles = () => {
16
+ const ref = useRef<InstancedMesh>(null);
17
+
18
+ useFrame((state, delta) => {
19
+ if (!ref.current) return;
20
+
21
+ ref.current.rotation.y = MathUtils.damp(
22
+ ref.current.rotation.y,
23
+ (-state.pointer.x * Math.PI) / 6,
24
+ 2.75,
25
+ delta
26
+ );
27
+ });
28
+
29
+ return (
30
+ <Instances
31
+ limit={particles.length}
32
+ ref={ref}
33
+ castShadow
34
+ receiveShadow
35
+ position={[0, 2.5, 0]}>
36
+ <sphereGeometry args={[0.45, 64, 64]} />
37
+ <meshStandardMaterial roughness={1} color="#f0f0f0" />
38
+ {particles.map((data, i) => (
39
+ <Bubble key={i} {...data} />
40
+ ))}
41
+ </Instances>
42
+ );
43
+ };
44
+
45
+ export default Bubbles;
@@ -0,0 +1,192 @@
1
+ import { createPortal, useFrame, useThree } from '@react-three/fiber';
2
+ import { useCallback, useMemo, useRef, useState } from 'react';
3
+ import { Camera, Color, Mesh, Scene, Texture, Vector2, Vector3 } from 'three';
4
+ import { ShaderPass } from 'three/examples/jsm/Addons.js';
5
+ import { Effect as FluidEffect } from './effect/Fluid';
6
+ import { useFBOs } from './hooks/useFBOs';
7
+ import { useMaterials } from './hooks/useMaterials';
8
+ import { type FluidProps } from './types';
9
+ import { DEFAULT_CONFIG } from './constant';
10
+ import { usePointer } from './hooks/usePointer';
11
+ import { normalizeScreenHz } from './utils';
12
+
13
+ type MaterialName = keyof ReturnType<typeof useMaterials>;
14
+ type FBONames = keyof ReturnType<typeof useFBOs>;
15
+
16
+ type Uniforms = {
17
+ uColor: Vector3 | Color;
18
+ uPointer: Vector2;
19
+ uTarget: Texture | null;
20
+ uVelocity: Texture;
21
+ uCurl: Texture;
22
+ uTexture: Texture;
23
+ uPressure: Texture;
24
+ uDivergence: Texture;
25
+ uSource: Texture;
26
+ uRadius: number;
27
+ uClearValue: number;
28
+ uCurlValue: number;
29
+ uDissipation: number;
30
+ };
31
+
32
+ export const Fluid = ({
33
+ blend = DEFAULT_CONFIG.blend,
34
+ force = DEFAULT_CONFIG.force,
35
+ radius = DEFAULT_CONFIG.radius,
36
+ curl = DEFAULT_CONFIG.curl,
37
+ swirl = DEFAULT_CONFIG.swirl,
38
+ intensity = DEFAULT_CONFIG.intensity,
39
+ distortion = DEFAULT_CONFIG.distortion,
40
+ fluidColor = DEFAULT_CONFIG.fluidColor,
41
+ backgroundColor = DEFAULT_CONFIG.backgroundColor,
42
+ showBackground = DEFAULT_CONFIG.showBackground,
43
+ rainbow = DEFAULT_CONFIG.rainbow,
44
+ pressure = DEFAULT_CONFIG.pressure,
45
+ densityDissipation = DEFAULT_CONFIG.densityDissipation,
46
+ velocityDissipation = DEFAULT_CONFIG.velocityDissipation,
47
+ blendFunction = DEFAULT_CONFIG.blendFunction,
48
+ }: FluidProps) => {
49
+ const size = useThree((three) => three.size);
50
+ const gl = useThree((three) => three.gl);
51
+
52
+ const [bufferScene] = useState(() => new Scene());
53
+ const bufferCamera = useMemo(() => new Camera(), []);
54
+
55
+ const meshRef = useRef<Mesh>(null);
56
+ const postRef = useRef<ShaderPass>(null);
57
+ const pointerRef = useRef(new Vector2());
58
+ const colorRef = useRef(new Vector3());
59
+
60
+ const FBOs = useFBOs();
61
+ const materials = useMaterials();
62
+ const splatStack = usePointer({ force });
63
+
64
+ const setShaderMaterial = useCallback(
65
+ (name: MaterialName) => {
66
+ if (!meshRef.current) return;
67
+
68
+ meshRef.current.material = materials[name];
69
+ meshRef.current.material.needsUpdate = true;
70
+ },
71
+ [materials],
72
+ );
73
+
74
+ const setRenderTarget = useCallback(
75
+ (name: FBONames) => {
76
+ const target = FBOs[name];
77
+
78
+ if ('write' in target) {
79
+ gl.setRenderTarget(target.write);
80
+ gl.clear();
81
+ gl.render(bufferScene, bufferCamera);
82
+ target.swap();
83
+ } else {
84
+ gl.setRenderTarget(target);
85
+ gl.clear();
86
+ gl.render(bufferScene, bufferCamera);
87
+ }
88
+ },
89
+ [bufferCamera, bufferScene, FBOs, gl],
90
+ );
91
+
92
+ const setUniforms = useCallback(
93
+ <K extends keyof Uniforms>(material: MaterialName, uniform: K, value: Uniforms[K]) => {
94
+ const mat = materials[material];
95
+
96
+ if (mat && mat.uniforms[uniform]) {
97
+ mat.uniforms[uniform].value = value;
98
+ }
99
+ },
100
+ [materials],
101
+ );
102
+
103
+ useFrame((_, delta) => {
104
+ if (!meshRef.current || !postRef.current) return;
105
+
106
+ for (let i = splatStack.length - 1; i >= 0; i--) {
107
+ const { mouseX, mouseY, velocityX, velocityY } = splatStack[i];
108
+
109
+ pointerRef.current.set(mouseX, mouseY);
110
+ colorRef.current.set(velocityX, velocityY, 10.0);
111
+
112
+ setShaderMaterial('splat');
113
+ setUniforms('splat', 'uTarget', FBOs.velocity.read.texture);
114
+ setUniforms('splat', 'uPointer', pointerRef.current);
115
+ setUniforms('splat', 'uColor', colorRef.current);
116
+ setUniforms('splat', 'uRadius', radius / 100.0);
117
+ setRenderTarget('velocity');
118
+ setUniforms('splat', 'uTarget', FBOs.density.read.texture);
119
+ setRenderTarget('density');
120
+
121
+ splatStack.pop();
122
+ }
123
+
124
+ setShaderMaterial('curl');
125
+ setUniforms('curl', 'uVelocity', FBOs.velocity.read.texture);
126
+ setRenderTarget('curl');
127
+
128
+ setShaderMaterial('vorticity');
129
+ setUniforms('vorticity', 'uVelocity', FBOs.velocity.read.texture);
130
+ setUniforms('vorticity', 'uCurl', FBOs.curl.texture);
131
+ setUniforms('vorticity', 'uCurlValue', curl);
132
+ setRenderTarget('velocity');
133
+
134
+ setShaderMaterial('divergence');
135
+ setUniforms('divergence', 'uVelocity', FBOs.velocity.read.texture);
136
+ setRenderTarget('divergence');
137
+
138
+ setShaderMaterial('clear');
139
+ setUniforms('clear', 'uTexture', FBOs.pressure.read.texture);
140
+ setUniforms('clear', 'uClearValue', normalizeScreenHz(pressure, delta));
141
+ setRenderTarget('pressure');
142
+
143
+ setShaderMaterial('pressure');
144
+ setUniforms('pressure', 'uDivergence', FBOs.divergence.texture);
145
+
146
+ for (let i = 0; i < swirl; i++) {
147
+ setUniforms('pressure', 'uPressure', FBOs.pressure.read.texture);
148
+ setRenderTarget('pressure');
149
+ }
150
+
151
+ setShaderMaterial('gradientSubstract');
152
+ setUniforms('gradientSubstract', 'uPressure', FBOs.pressure.read.texture);
153
+ setUniforms('gradientSubstract', 'uVelocity', FBOs.velocity.read.texture);
154
+ setRenderTarget('velocity');
155
+
156
+ setShaderMaterial('advection');
157
+ setUniforms('advection', 'uVelocity', FBOs.velocity.read.texture);
158
+ setUniforms('advection', 'uSource', FBOs.velocity.read.texture);
159
+ setUniforms('advection', 'uDissipation', normalizeScreenHz(velocityDissipation, delta));
160
+
161
+ setRenderTarget('velocity');
162
+ setUniforms('advection', 'uVelocity', FBOs.velocity.read.texture);
163
+ setUniforms('advection', 'uSource', FBOs.density.read.texture);
164
+ setUniforms('advection', 'uDissipation', normalizeScreenHz(densityDissipation, delta));
165
+
166
+ setRenderTarget('density');
167
+ });
168
+
169
+ return (
170
+ <>
171
+ {createPortal(
172
+ <mesh ref={meshRef} scale={[size.width, size.height, 1]}>
173
+ <planeGeometry args={[2, 2]} />
174
+ </mesh>,
175
+ bufferScene,
176
+ )}
177
+
178
+ <FluidEffect
179
+ blendFunction={blendFunction}
180
+ intensity={intensity}
181
+ rainbow={rainbow}
182
+ distortion={distortion}
183
+ backgroundColor={backgroundColor}
184
+ blend={blend}
185
+ fluidColor={fluidColor}
186
+ showBackground={showBackground}
187
+ ref={postRef}
188
+ tFluid={FBOs.density.read.texture}
189
+ />
190
+ </>
191
+ );
192
+ };
@@ -0,0 +1,23 @@
1
+ import { BlendFunction } from 'postprocessing';
2
+
3
+ export const DEFAULT_CONFIG = {
4
+ blend: 5,
5
+ intensity: 2,
6
+ force: 1.1,
7
+ distortion: 0.4,
8
+ curl: 1.9,
9
+ radius: 0.3,
10
+ swirl: 4,
11
+ pressure: 0.8,
12
+ densityDissipation: 0.96,
13
+ velocityDissipation: 1.0,
14
+ fluidColor: '#3300ff',
15
+ backgroundColor: '#070410',
16
+ showBackground: true,
17
+ rainbow: false,
18
+ dyeRes: 512,
19
+ simRes: 128,
20
+ blendFunction: BlendFunction.SET,
21
+ } as const;
22
+
23
+ export const REFRESH_RATE = 60;
@@ -0,0 +1,22 @@
1
+ import { forwardRef, useEffect, useMemo } from 'react';
2
+ import { EffectProps } from '../types';
3
+ import { FluidEffect } from './FluidEffect';
4
+
5
+ export const Effect = forwardRef(function Fluid(props: EffectProps, ref) {
6
+ // prevent re-creating the effect on every render
7
+ // eslint-disable-next-line react-hooks/exhaustive-deps
8
+ const effect = useMemo(() => new FluidEffect(props), []);
9
+
10
+ useEffect(() => {
11
+ effect.state = { ...props };
12
+ effect.update();
13
+ }, [effect, props]);
14
+
15
+ useEffect(() => {
16
+ return () => {
17
+ effect.dispose?.();
18
+ };
19
+ }, [effect]);
20
+
21
+ return <primitive ref={ref} object={effect} dispose={null} />;
22
+ });
@@ -0,0 +1,61 @@
1
+ import { Effect, EffectAttribute } from "postprocessing";
2
+ import { Texture, Uniform, Vector3 } from "three";
3
+ import compositeFrag from "../glsl/composite.frag?raw";
4
+ import { type EffectProps } from "../types";
5
+ import { hexToRgb } from "../utils";
6
+
7
+ type Uniforms = {
8
+ tFluid: Texture;
9
+ uColor: Vector3;
10
+ uBackgroundColor: Vector3;
11
+ uRainbow: boolean;
12
+ uShowBackground: boolean;
13
+ uDistort: number;
14
+ uBlend: number;
15
+ uIntensity: number;
16
+ };
17
+
18
+ export class FluidEffect extends Effect {
19
+ state: EffectProps;
20
+
21
+ constructor(props: EffectProps) {
22
+ const uniforms: Record<keyof Uniforms, Uniform> = {
23
+ tFluid: new Uniform(props.tFluid),
24
+ uDistort: new Uniform(props.distortion),
25
+ uRainbow: new Uniform(props.rainbow),
26
+ uIntensity: new Uniform(props.intensity),
27
+ uBlend: new Uniform(props.blend),
28
+ uShowBackground: new Uniform(props.showBackground),
29
+ uColor: new Uniform(hexToRgb(props.fluidColor!)),
30
+ uBackgroundColor: new Uniform(hexToRgb(props.backgroundColor!)),
31
+ };
32
+
33
+ super("FluidEffect", compositeFrag, {
34
+ blendFunction: props.blendFunction,
35
+ attributes: EffectAttribute.CONVOLUTION,
36
+ uniforms: new Map(Object.entries(uniforms)),
37
+ });
38
+
39
+ this.state = props;
40
+ }
41
+
42
+ private updateUniform<K extends keyof Uniforms>(key: K, value: Uniforms[K]) {
43
+ const uniform = this.uniforms.get(key);
44
+ if (uniform) {
45
+ uniform.value = value;
46
+ }
47
+ }
48
+
49
+ update() {
50
+ this.updateUniform("uIntensity", this.state.intensity);
51
+ this.updateUniform("uDistort", this.state.distortion);
52
+ this.updateUniform("uRainbow", this.state.rainbow);
53
+ this.updateUniform("uBlend", this.state.blend);
54
+ this.updateUniform("uShowBackground", this.state.showBackground);
55
+ this.updateUniform("uColor", hexToRgb(this.state.fluidColor));
56
+ this.updateUniform(
57
+ "uBackgroundColor",
58
+ hexToRgb(this.state.backgroundColor)
59
+ );
60
+ }
61
+ }
@@ -0,0 +1,16 @@
1
+ precision highp float;
2
+
3
+ varying vec2 vUv;
4
+ uniform sampler2D uVelocity;
5
+ uniform sampler2D uSource;
6
+ uniform vec2 texelSize;
7
+ uniform float dt;
8
+ uniform float uDissipation;
9
+
10
+ void main() {
11
+ vec2 coord = vUv - dt * texture2D(uVelocity, vUv).xy * texelSize;
12
+
13
+ gl_FragColor = uDissipation * texture2D(uSource, coord);
14
+
15
+ gl_FragColor.a = 1.0;
16
+ }
@@ -0,0 +1,26 @@
1
+ #ifdef USE_V_UV
2
+ varying vec2 vUv;
3
+ #endif
4
+
5
+ #ifdef USE_OFFSETS
6
+ varying vec2 vL;
7
+ varying vec2 vR;
8
+ varying vec2 vT;
9
+ varying vec2 vB;
10
+ uniform vec2 texelSize;
11
+ #endif
12
+
13
+ void main() {
14
+ #ifdef USE_V_UV
15
+ vUv = uv;
16
+ #endif
17
+
18
+ #ifdef USE_OFFSETS
19
+ vL = uv - vec2(texelSize.x, 0.0);
20
+ vR = uv + vec2(texelSize.x, 0.0);
21
+ vT = uv + vec2(0.0, texelSize.y);
22
+ vB = uv - vec2(0.0, texelSize.y);
23
+ #endif
24
+
25
+ gl_Position = vec4(position, 1.0);
26
+ }
@@ -0,0 +1,7 @@
1
+ precision highp float;
2
+
3
+ varying vec2 vUv;
4
+ uniform sampler2D uTexture;
5
+ uniform float uClearValue;
6
+
7
+ void main() { gl_FragColor = uClearValue * texture2D(uTexture, vUv); }