react-native-effects 0.0.1 → 0.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.
- package/LICENSE +20 -0
- package/README.md +251 -0
- package/lib/module/components/Aurora.js +184 -0
- package/lib/module/components/Aurora.js.map +1 -0
- package/lib/module/components/CalicoSwirl.js +155 -0
- package/lib/module/components/CalicoSwirl.js.map +1 -0
- package/lib/module/components/Campfire.js +225 -0
- package/lib/module/components/Campfire.js.map +1 -0
- package/lib/module/components/CircularGradient.js +52 -0
- package/lib/module/components/CircularGradient.js.map +1 -0
- package/lib/module/components/Iridescence.js +57 -0
- package/lib/module/components/Iridescence.js.map +1 -0
- package/lib/module/components/LinearGradient.js +48 -0
- package/lib/module/components/LinearGradient.js.map +1 -0
- package/lib/module/components/LiquidChrome.js +75 -0
- package/lib/module/components/LiquidChrome.js.map +1 -0
- package/lib/module/components/ShaderView/index.js +224 -0
- package/lib/module/components/ShaderView/index.js.map +1 -0
- package/lib/module/components/ShaderView/types.js +4 -0
- package/lib/module/components/ShaderView/types.js.map +1 -0
- package/lib/module/components/Silk.js +83 -0
- package/lib/module/components/Silk.js.map +1 -0
- package/lib/module/consts.js +154 -0
- package/lib/module/consts.js.map +1 -0
- package/lib/module/hooks/useClock.js +15 -0
- package/lib/module/hooks/useClock.js.map +1 -0
- package/lib/module/hooks/useWGPUSetup.js +54 -0
- package/lib/module/hooks/useWGPUSetup.js.map +1 -0
- package/lib/module/index.js +13 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/module/shaders/TRIANGLE_VERTEX_SHADER.js +20 -0
- package/lib/module/shaders/TRIANGLE_VERTEX_SHADER.js.map +1 -0
- package/lib/module/shaders/uniforms.js +20 -0
- package/lib/module/shaders/uniforms.js.map +1 -0
- package/lib/module/utils/backgroundRuntime.js +12 -0
- package/lib/module/utils/backgroundRuntime.js.map +1 -0
- package/lib/module/utils/colors.js +94 -0
- package/lib/module/utils/colors.js.map +1 -0
- package/lib/module/utils/initWebGPU.js +40 -0
- package/lib/module/utils/initWebGPU.js.map +1 -0
- package/lib/typescript/package.json +1 -0
- package/lib/typescript/src/components/Aurora.d.ts +17 -0
- package/lib/typescript/src/components/Aurora.d.ts.map +1 -0
- package/lib/typescript/src/components/CalicoSwirl.d.ts +13 -0
- package/lib/typescript/src/components/CalicoSwirl.d.ts.map +1 -0
- package/lib/typescript/src/components/Campfire.d.ts +17 -0
- package/lib/typescript/src/components/Campfire.d.ts.map +1 -0
- package/lib/typescript/src/components/CircularGradient.d.ts +19 -0
- package/lib/typescript/src/components/CircularGradient.d.ts.map +1 -0
- package/lib/typescript/src/components/Iridescence.d.ts +11 -0
- package/lib/typescript/src/components/Iridescence.d.ts.map +1 -0
- package/lib/typescript/src/components/LinearGradient.d.ts +15 -0
- package/lib/typescript/src/components/LinearGradient.d.ts.map +1 -0
- package/lib/typescript/src/components/LiquidChrome.d.ts +17 -0
- package/lib/typescript/src/components/LiquidChrome.d.ts.map +1 -0
- package/lib/typescript/src/components/ShaderView/index.d.ts +3 -0
- package/lib/typescript/src/components/ShaderView/index.d.ts.map +1 -0
- package/lib/typescript/src/components/ShaderView/types.d.ts +15 -0
- package/lib/typescript/src/components/ShaderView/types.d.ts.map +1 -0
- package/lib/typescript/src/components/Silk.d.ts +17 -0
- package/lib/typescript/src/components/Silk.d.ts.map +1 -0
- package/lib/typescript/src/consts.d.ts +2 -0
- package/lib/typescript/src/consts.d.ts.map +1 -0
- package/lib/typescript/src/hooks/useClock.d.ts +3 -0
- package/lib/typescript/src/hooks/useClock.d.ts.map +1 -0
- package/lib/typescript/src/hooks/useWGPUSetup.d.ts +15 -0
- package/lib/typescript/src/hooks/useWGPUSetup.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +12 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/lib/typescript/src/shaders/TRIANGLE_VERTEX_SHADER.d.ts +2 -0
- package/lib/typescript/src/shaders/TRIANGLE_VERTEX_SHADER.d.ts.map +1 -0
- package/lib/typescript/src/shaders/uniforms.d.ts +6 -0
- package/lib/typescript/src/shaders/uniforms.d.ts.map +1 -0
- package/lib/typescript/src/utils/backgroundRuntime.d.ts +3 -0
- package/lib/typescript/src/utils/backgroundRuntime.d.ts.map +1 -0
- package/lib/typescript/src/utils/colors.d.ts +22 -0
- package/lib/typescript/src/utils/colors.d.ts.map +1 -0
- package/lib/typescript/src/utils/initWebGPU.d.ts +23 -0
- package/lib/typescript/src/utils/initWebGPU.d.ts.map +1 -0
- package/package.json +174 -7
- package/src/components/Aurora.tsx +203 -0
- package/src/components/CalicoSwirl.tsx +167 -0
- package/src/components/Campfire.tsx +244 -0
- package/src/components/CircularGradient.tsx +76 -0
- package/src/components/Iridescence.tsx +67 -0
- package/src/components/LinearGradient.tsx +62 -0
- package/src/components/LiquidChrome.tsx +94 -0
- package/src/components/ShaderView/index.tsx +225 -0
- package/src/components/ShaderView/types.ts +15 -0
- package/src/components/Silk.tsx +102 -0
- package/src/consts.ts +152 -0
- package/src/hooks/useClock.ts +20 -0
- package/src/hooks/useWGPUSetup.tsx +73 -0
- package/src/index.tsx +23 -0
- package/src/shaders/TRIANGLE_VERTEX_SHADER.ts +17 -0
- package/src/shaders/uniforms.ts +17 -0
- package/src/utils/backgroundRuntime.ts +10 -0
- package/src/utils/colors.ts +117 -0
- package/src/utils/initWebGPU.ts +47 -0
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
import { PixelRatio, StyleSheet } from 'react-native';
|
|
2
|
+
import { Canvas } from 'react-native-wgpu';
|
|
3
|
+
import { useEffect, useRef } from 'react';
|
|
4
|
+
import { createSynchronizable, scheduleOnRuntime } from 'react-native-worklets';
|
|
5
|
+
import { colorToVec4 } from '../../utils/colors';
|
|
6
|
+
import { useWGPUSetup } from '../../hooks/useWGPUSetup';
|
|
7
|
+
import { TRIANGLE_VERTEX_SHADER } from '../../shaders/TRIANGLE_VERTEX_SHADER';
|
|
8
|
+
import {
|
|
9
|
+
UNIFORM_BUFFER_SIZE,
|
|
10
|
+
UNIFORM_FLOAT_COUNT,
|
|
11
|
+
} from '../../shaders/uniforms';
|
|
12
|
+
import type { ShaderViewProps } from './types';
|
|
13
|
+
|
|
14
|
+
// Synchronizable layout: [c0r,c0g,c0b,c0a, c1r,c1g,c1b,c1a, speed, p0..p7, alive]
|
|
15
|
+
// Total: 4 + 4 + 1 + 8 + 1 = 18 floats
|
|
16
|
+
const SYNC_SIZE = 18;
|
|
17
|
+
const IDX_SPEED = 8;
|
|
18
|
+
const IDX_PARAMS = 9; // 9..16
|
|
19
|
+
const IDX_ALIVE = 17;
|
|
20
|
+
|
|
21
|
+
export default function ShaderView({
|
|
22
|
+
fragmentShader,
|
|
23
|
+
colors = [],
|
|
24
|
+
speed = 1.0,
|
|
25
|
+
params = [],
|
|
26
|
+
isStatic = false,
|
|
27
|
+
style,
|
|
28
|
+
...viewProps
|
|
29
|
+
}: ShaderViewProps) {
|
|
30
|
+
const { canvasRef, runtime, resources } = useWGPUSetup();
|
|
31
|
+
|
|
32
|
+
const propsSync = useRef(
|
|
33
|
+
createSynchronizable<Float64Array>(new Float64Array(SYNC_SIZE))
|
|
34
|
+
).current;
|
|
35
|
+
|
|
36
|
+
// Convert props to flat floats and push to synchronizable
|
|
37
|
+
useEffect(() => {
|
|
38
|
+
const data = new Float64Array(SYNC_SIZE);
|
|
39
|
+
|
|
40
|
+
// color0 (indices 0-3)
|
|
41
|
+
if (colors[0] !== undefined) {
|
|
42
|
+
const c0 = colorToVec4(colors[0]);
|
|
43
|
+
data[0] = c0.r;
|
|
44
|
+
data[1] = c0.g;
|
|
45
|
+
data[2] = c0.b;
|
|
46
|
+
data[3] = c0.a;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// color1 (indices 4-7)
|
|
50
|
+
if (colors[1] !== undefined) {
|
|
51
|
+
const c1 = colorToVec4(colors[1]);
|
|
52
|
+
data[4] = c1.r;
|
|
53
|
+
data[5] = c1.g;
|
|
54
|
+
data[6] = c1.b;
|
|
55
|
+
data[7] = c1.a;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// speed
|
|
59
|
+
data[IDX_SPEED] = speed;
|
|
60
|
+
|
|
61
|
+
// params (indices 9-16)
|
|
62
|
+
for (let i = 0; i < 8; i++) {
|
|
63
|
+
data[IDX_PARAMS + i] = params[i] ?? 0;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// alive
|
|
67
|
+
data[IDX_ALIVE] = 1;
|
|
68
|
+
|
|
69
|
+
propsSync.setBlocking(() => data);
|
|
70
|
+
}, [colors, speed, params, propsSync]);
|
|
71
|
+
|
|
72
|
+
// Signal cleanup on unmount
|
|
73
|
+
useEffect(() => {
|
|
74
|
+
return () => {
|
|
75
|
+
propsSync.setBlocking((prev) => {
|
|
76
|
+
prev[IDX_ALIVE] = 0;
|
|
77
|
+
return prev;
|
|
78
|
+
});
|
|
79
|
+
};
|
|
80
|
+
}, [propsSync]);
|
|
81
|
+
|
|
82
|
+
// Start render loop when GPU resources are ready
|
|
83
|
+
useEffect(() => {
|
|
84
|
+
if (!resources) {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const { device, context, presentationFormat } = resources;
|
|
89
|
+
const dpr = PixelRatio.get();
|
|
90
|
+
|
|
91
|
+
scheduleOnRuntime(runtime, () => {
|
|
92
|
+
'worklet';
|
|
93
|
+
|
|
94
|
+
// Create pipeline once
|
|
95
|
+
const pipeline = device.createRenderPipeline({
|
|
96
|
+
layout: 'auto',
|
|
97
|
+
vertex: {
|
|
98
|
+
module: device.createShaderModule({ code: TRIANGLE_VERTEX_SHADER }),
|
|
99
|
+
entryPoint: 'main',
|
|
100
|
+
},
|
|
101
|
+
fragment: {
|
|
102
|
+
module: device.createShaderModule({ code: fragmentShader }),
|
|
103
|
+
entryPoint: 'main',
|
|
104
|
+
targets: [{ format: presentationFormat }],
|
|
105
|
+
},
|
|
106
|
+
primitive: { topology: 'triangle-list' },
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
// Create uniform buffer + bind group once, reuse via writeBuffer
|
|
110
|
+
const uniformBuffer = device.createBuffer({
|
|
111
|
+
size: UNIFORM_BUFFER_SIZE,
|
|
112
|
+
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
const bindGroup = device.createBindGroup({
|
|
116
|
+
layout: pipeline.getBindGroupLayout(0),
|
|
117
|
+
entries: [{ binding: 0, resource: { buffer: uniformBuffer } }],
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
const uniformData = new Float32Array(UNIFORM_FLOAT_COUNT);
|
|
121
|
+
let accumulatedTime = 0;
|
|
122
|
+
let lastTimestamp = 0;
|
|
123
|
+
|
|
124
|
+
function render(timestamp: number) {
|
|
125
|
+
const props = propsSync.getDirty();
|
|
126
|
+
if (props[IDX_ALIVE] === 0) {
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Compute dt
|
|
131
|
+
const dt = lastTimestamp === 0 ? 0 : (timestamp - lastTimestamp) / 1000;
|
|
132
|
+
lastTimestamp = timestamp;
|
|
133
|
+
|
|
134
|
+
// Accumulate time with speed
|
|
135
|
+
const currentSpeed = props[IDX_SPEED]!;
|
|
136
|
+
accumulatedTime += dt * currentSpeed;
|
|
137
|
+
|
|
138
|
+
// Resolution
|
|
139
|
+
const canvas = context.canvas as typeof context.canvas & {
|
|
140
|
+
width: number;
|
|
141
|
+
height: number;
|
|
142
|
+
};
|
|
143
|
+
const width = canvas.width || 1;
|
|
144
|
+
const height = canvas.height || 1;
|
|
145
|
+
const aspect = width / height;
|
|
146
|
+
|
|
147
|
+
// Fill uniform data (6 × vec4 = 24 floats)
|
|
148
|
+
// resolution: vec4<f32>
|
|
149
|
+
uniformData[0] = width;
|
|
150
|
+
uniformData[1] = height;
|
|
151
|
+
uniformData[2] = aspect;
|
|
152
|
+
uniformData[3] = dpr;
|
|
153
|
+
|
|
154
|
+
// time: vec4<f32>
|
|
155
|
+
uniformData[4] = accumulatedTime;
|
|
156
|
+
uniformData[5] = dt;
|
|
157
|
+
uniformData[6] = 0;
|
|
158
|
+
uniformData[7] = 0;
|
|
159
|
+
|
|
160
|
+
// color0: vec4<f32>
|
|
161
|
+
uniformData[8] = props[0]!;
|
|
162
|
+
uniformData[9] = props[1]!;
|
|
163
|
+
uniformData[10] = props[2]!;
|
|
164
|
+
uniformData[11] = props[3]!;
|
|
165
|
+
|
|
166
|
+
// color1: vec4<f32>
|
|
167
|
+
uniformData[12] = props[4]!;
|
|
168
|
+
uniformData[13] = props[5]!;
|
|
169
|
+
uniformData[14] = props[6]!;
|
|
170
|
+
uniformData[15] = props[7]!;
|
|
171
|
+
|
|
172
|
+
// params0: vec4<f32>
|
|
173
|
+
uniformData[16] = props[IDX_PARAMS]!;
|
|
174
|
+
uniformData[17] = props[IDX_PARAMS + 1]!;
|
|
175
|
+
uniformData[18] = props[IDX_PARAMS + 2]!;
|
|
176
|
+
uniformData[19] = props[IDX_PARAMS + 3]!;
|
|
177
|
+
|
|
178
|
+
// params1: vec4<f32>
|
|
179
|
+
uniformData[20] = props[IDX_PARAMS + 4]!;
|
|
180
|
+
uniformData[21] = props[IDX_PARAMS + 5]!;
|
|
181
|
+
uniformData[22] = props[IDX_PARAMS + 6]!;
|
|
182
|
+
uniformData[23] = props[IDX_PARAMS + 7]!;
|
|
183
|
+
|
|
184
|
+
device.queue.writeBuffer(uniformBuffer, 0, uniformData);
|
|
185
|
+
|
|
186
|
+
const commandEncoder = device.createCommandEncoder();
|
|
187
|
+
const textureView = context.getCurrentTexture().createView();
|
|
188
|
+
const passEncoder = commandEncoder.beginRenderPass({
|
|
189
|
+
colorAttachments: [
|
|
190
|
+
{
|
|
191
|
+
view: textureView,
|
|
192
|
+
clearValue: [0, 0, 0, 1],
|
|
193
|
+
loadOp: 'clear',
|
|
194
|
+
storeOp: 'store',
|
|
195
|
+
},
|
|
196
|
+
],
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
passEncoder.setPipeline(pipeline);
|
|
200
|
+
passEncoder.setBindGroup(0, bindGroup);
|
|
201
|
+
passEncoder.draw(3);
|
|
202
|
+
passEncoder.end();
|
|
203
|
+
|
|
204
|
+
device.queue.submit([commandEncoder.finish()]);
|
|
205
|
+
context.present();
|
|
206
|
+
|
|
207
|
+
if (!isStatic) {
|
|
208
|
+
requestAnimationFrame(render);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
requestAnimationFrame(render);
|
|
213
|
+
});
|
|
214
|
+
}, [resources, runtime, propsSync, fragmentShader, isStatic]);
|
|
215
|
+
|
|
216
|
+
return (
|
|
217
|
+
<Canvas ref={canvasRef} style={[styles.canvas, style]} {...viewProps} />
|
|
218
|
+
);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const styles = StyleSheet.create({
|
|
222
|
+
canvas: {
|
|
223
|
+
flex: 1,
|
|
224
|
+
},
|
|
225
|
+
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { ViewProps } from 'react-native';
|
|
2
|
+
import type { ColorInput } from '../../utils/colors';
|
|
3
|
+
|
|
4
|
+
export type ShaderViewProps = ViewProps & {
|
|
5
|
+
/** WGSL fragment shader source (must declare the Uniforms struct) */
|
|
6
|
+
fragmentShader: string;
|
|
7
|
+
/** Array of colors mapped to u.color0, u.color1 (max 2). Default: [] */
|
|
8
|
+
colors?: ColorInput[];
|
|
9
|
+
/** Time multiplier — controls animation speed. Default: 1.0 */
|
|
10
|
+
speed?: number;
|
|
11
|
+
/** Up to 8 shader-specific floats mapped to u.params0.xyzw and u.params1.xyzw */
|
|
12
|
+
params?: number[];
|
|
13
|
+
/** Render once then stop the RAF loop. Default: false */
|
|
14
|
+
isStatic?: boolean;
|
|
15
|
+
};
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { useMemo } from 'react';
|
|
2
|
+
import type { ViewProps } from 'react-native';
|
|
3
|
+
import type { ColorInput } from '../utils/colors';
|
|
4
|
+
import ShaderView from './ShaderView';
|
|
5
|
+
|
|
6
|
+
type Props = ViewProps & {
|
|
7
|
+
/** The base color for the silk effect. */
|
|
8
|
+
color?: ColorInput;
|
|
9
|
+
/** Animation speed multiplier. Default: 1.0 */
|
|
10
|
+
speed?: number;
|
|
11
|
+
/** Scale of the pattern. Default: 1.0 */
|
|
12
|
+
scale?: number;
|
|
13
|
+
/** Rotation angle in radians. Default: 0.0 */
|
|
14
|
+
rotation?: number;
|
|
15
|
+
/** Intensity of the noise grain. Default: 1.5 */
|
|
16
|
+
noiseIntensity?: number;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export default function Silk({
|
|
20
|
+
color = '#7B7481',
|
|
21
|
+
speed = 1.0,
|
|
22
|
+
scale = 1.0,
|
|
23
|
+
rotation = 0.0,
|
|
24
|
+
noiseIntensity = 1.5,
|
|
25
|
+
...viewProps
|
|
26
|
+
}: Props) {
|
|
27
|
+
const colors = useMemo(() => [color], [color]);
|
|
28
|
+
const params = useMemo(
|
|
29
|
+
() => [scale, rotation, noiseIntensity],
|
|
30
|
+
[scale, rotation, noiseIntensity]
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<ShaderView
|
|
35
|
+
fragmentShader={SILK_SHADER}
|
|
36
|
+
colors={colors}
|
|
37
|
+
params={params}
|
|
38
|
+
speed={speed}
|
|
39
|
+
isStatic={false}
|
|
40
|
+
{...viewProps}
|
|
41
|
+
/>
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const SILK_SHADER = /* wgsl */ `
|
|
46
|
+
struct Uniforms {
|
|
47
|
+
resolution: vec4<f32>,
|
|
48
|
+
time: vec4<f32>,
|
|
49
|
+
color0: vec4<f32>,
|
|
50
|
+
color1: vec4<f32>,
|
|
51
|
+
params0: vec4<f32>,
|
|
52
|
+
params1: vec4<f32>,
|
|
53
|
+
};
|
|
54
|
+
@group(0) @binding(0) var<uniform> u: Uniforms;
|
|
55
|
+
|
|
56
|
+
const e = 2.71828182845904523536;
|
|
57
|
+
|
|
58
|
+
fn noise(texCoord: vec2<f32>) -> f32 {
|
|
59
|
+
let G = e;
|
|
60
|
+
let r = (G * sin(G * texCoord));
|
|
61
|
+
return fract(r.x * r.y * (1.0 + texCoord.x));
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
fn rotateUvs(uv: vec2<f32>, angle: f32) -> vec2<f32> {
|
|
65
|
+
let c = cos(angle);
|
|
66
|
+
let s = sin(angle);
|
|
67
|
+
let rot = mat2x2<f32>(c, -s, s, c);
|
|
68
|
+
return rot * uv;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
@fragment
|
|
72
|
+
fn main(@location(0) ndc: vec2<f32>) -> @location(0) vec4<f32> {
|
|
73
|
+
let time = u.time.x;
|
|
74
|
+
let scale = u.params0.x;
|
|
75
|
+
let rotation = u.params0.y;
|
|
76
|
+
let noiseIntensity = u.params0.z;
|
|
77
|
+
|
|
78
|
+
let resolution2D = u.resolution.xy;
|
|
79
|
+
let vUv = ndc * 0.5 + vec2<f32>(0.5, 0.5);
|
|
80
|
+
let fragCoord = vUv * resolution2D;
|
|
81
|
+
|
|
82
|
+
let rnd = noise(fragCoord);
|
|
83
|
+
let uv = rotateUvs(vUv * scale, rotation);
|
|
84
|
+
var tex = uv * scale;
|
|
85
|
+
let tOffset = time;
|
|
86
|
+
|
|
87
|
+
tex.y = tex.y + 0.03 * sin(8.0 * tex.x - tOffset);
|
|
88
|
+
|
|
89
|
+
let pattern = 0.6 +
|
|
90
|
+
0.4 * sin(5.0 * (tex.x + tex.y +
|
|
91
|
+
cos(3.0 * tex.x + 5.0 * tex.y) +
|
|
92
|
+
0.02 * tOffset) +
|
|
93
|
+
sin(20.0 * (tex.x + tex.y - 0.1 * tOffset)));
|
|
94
|
+
|
|
95
|
+
var col = u.color0 * vec4<f32>(pattern, pattern, pattern, 1.0) -
|
|
96
|
+
vec4<f32>(rnd / 15.0 * noiseIntensity, rnd / 15.0 * noiseIntensity,
|
|
97
|
+
rnd / 15.0 * noiseIntensity, 0.0);
|
|
98
|
+
col.a = u.color0.a;
|
|
99
|
+
|
|
100
|
+
return clamp(col, vec4<f32>(0.0), vec4<f32>(1.0));
|
|
101
|
+
}
|
|
102
|
+
`;
|
package/src/consts.ts
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
export const NAMED_COLORS: Record<string, number> = {
|
|
2
|
+
// Basic colors
|
|
3
|
+
black: 0x000000,
|
|
4
|
+
white: 0xffffff,
|
|
5
|
+
red: 0xff0000,
|
|
6
|
+
green: 0x008000,
|
|
7
|
+
blue: 0x0000ff,
|
|
8
|
+
yellow: 0xffff00,
|
|
9
|
+
cyan: 0x00ffff,
|
|
10
|
+
magenta: 0xff00ff,
|
|
11
|
+
silver: 0xc0c0c0,
|
|
12
|
+
gray: 0x808080,
|
|
13
|
+
grey: 0x808080,
|
|
14
|
+
maroon: 0x800000,
|
|
15
|
+
olive: 0x808000,
|
|
16
|
+
lime: 0x00ff00,
|
|
17
|
+
aqua: 0x00ffff,
|
|
18
|
+
teal: 0x008080,
|
|
19
|
+
navy: 0x000080,
|
|
20
|
+
fuchsia: 0xff00ff,
|
|
21
|
+
purple: 0x800080,
|
|
22
|
+
|
|
23
|
+
// Extended colors
|
|
24
|
+
aliceblue: 0xf0f8ff,
|
|
25
|
+
antiquewhite: 0xfaebd7,
|
|
26
|
+
aquamarine: 0x7fffd4,
|
|
27
|
+
azure: 0xf0ffff,
|
|
28
|
+
beige: 0xf5f5dc,
|
|
29
|
+
bisque: 0xffe4c4,
|
|
30
|
+
blanchedalmond: 0xffebcd,
|
|
31
|
+
blueviolet: 0x8a2be2,
|
|
32
|
+
brown: 0xa52a2a,
|
|
33
|
+
burlywood: 0xdeb887,
|
|
34
|
+
cadetblue: 0x5f9ea0,
|
|
35
|
+
chartreuse: 0x7fff00,
|
|
36
|
+
chocolate: 0xd2691e,
|
|
37
|
+
coral: 0xff7f50,
|
|
38
|
+
cornflowerblue: 0x6495ed,
|
|
39
|
+
cornsilk: 0xfff8dc,
|
|
40
|
+
crimson: 0xdc143c,
|
|
41
|
+
darkblue: 0x00008b,
|
|
42
|
+
darkcyan: 0x008b8b,
|
|
43
|
+
darkgoldenrod: 0xb8860b,
|
|
44
|
+
darkgray: 0xa9a9a9,
|
|
45
|
+
darkgrey: 0xa9a9a9,
|
|
46
|
+
darkgreen: 0x006400,
|
|
47
|
+
darkkhaki: 0xbdb76b,
|
|
48
|
+
darkmagenta: 0x8b008b,
|
|
49
|
+
darkolivegreen: 0x556b2f,
|
|
50
|
+
darkorange: 0xff8c00,
|
|
51
|
+
darkorchid: 0x9932cc,
|
|
52
|
+
darkred: 0x8b0000,
|
|
53
|
+
darksalmon: 0xe9967a,
|
|
54
|
+
darkseagreen: 0x8fbc8f,
|
|
55
|
+
darkslateblue: 0x483d8b,
|
|
56
|
+
darkslategray: 0x2f4f4f,
|
|
57
|
+
darkslategrey: 0x2f4f4f,
|
|
58
|
+
darkturquoise: 0x00ced1,
|
|
59
|
+
darkviolet: 0x9400d3,
|
|
60
|
+
deeppink: 0xff1493,
|
|
61
|
+
deepskyblue: 0x00bfff,
|
|
62
|
+
dimgray: 0x696969,
|
|
63
|
+
dimgrey: 0x696969,
|
|
64
|
+
dodgerblue: 0x1e90ff,
|
|
65
|
+
firebrick: 0xb22222,
|
|
66
|
+
floralwhite: 0xfffaf0,
|
|
67
|
+
forestgreen: 0x228b22,
|
|
68
|
+
gainsboro: 0xdcdcdc,
|
|
69
|
+
ghostwhite: 0xf8f8ff,
|
|
70
|
+
gold: 0xffd700,
|
|
71
|
+
goldenrod: 0xdaa520,
|
|
72
|
+
greenyellow: 0xadff2f,
|
|
73
|
+
honeydew: 0xf0fff0,
|
|
74
|
+
hotpink: 0xff69b4,
|
|
75
|
+
indianred: 0xcd5c5c,
|
|
76
|
+
indigo: 0x4b0082,
|
|
77
|
+
ivory: 0xfffff0,
|
|
78
|
+
khaki: 0xf0e68c,
|
|
79
|
+
lavender: 0xe6e6fa,
|
|
80
|
+
lavenderblush: 0xfff0f5,
|
|
81
|
+
lawngreen: 0x7cfc00,
|
|
82
|
+
lemonchiffon: 0xfffacd,
|
|
83
|
+
lightblue: 0xadd8e6,
|
|
84
|
+
lightcoral: 0xf08080,
|
|
85
|
+
lightcyan: 0xe0ffff,
|
|
86
|
+
lightgoldenrodyellow: 0xfafad2,
|
|
87
|
+
lightgray: 0xd3d3d3,
|
|
88
|
+
lightgrey: 0xd3d3d3,
|
|
89
|
+
lightgreen: 0x90ee90,
|
|
90
|
+
lightpink: 0xffb6c1,
|
|
91
|
+
lightsalmon: 0xffa07a,
|
|
92
|
+
lightseagreen: 0x20b2aa,
|
|
93
|
+
lightskyblue: 0x87cefa,
|
|
94
|
+
lightslategray: 0x778899,
|
|
95
|
+
lightslategrey: 0x778899,
|
|
96
|
+
lightsteelblue: 0xb0c4de,
|
|
97
|
+
lightyellow: 0xffffe0,
|
|
98
|
+
limegreen: 0x32cd32,
|
|
99
|
+
linen: 0xfaf0e6,
|
|
100
|
+
mediumaquamarine: 0x66cdaa,
|
|
101
|
+
mediumblue: 0x0000cd,
|
|
102
|
+
mediumorchid: 0xba55d3,
|
|
103
|
+
mediumpurple: 0x9370db,
|
|
104
|
+
mediumseagreen: 0x3cb371,
|
|
105
|
+
mediumslateblue: 0x7b68ee,
|
|
106
|
+
mediumspringgreen: 0x00fa9a,
|
|
107
|
+
mediumturquoise: 0x48d1cc,
|
|
108
|
+
mediumvioletred: 0xc71585,
|
|
109
|
+
midnightblue: 0x191970,
|
|
110
|
+
mintcream: 0xf5fffa,
|
|
111
|
+
mistyrose: 0xffe4e1,
|
|
112
|
+
moccasin: 0xffe4b5,
|
|
113
|
+
navajowhite: 0xffdead,
|
|
114
|
+
oldlace: 0xfdf5e6,
|
|
115
|
+
olivedrab: 0x6b8e23,
|
|
116
|
+
orange: 0xffa500,
|
|
117
|
+
orangered: 0xff4500,
|
|
118
|
+
orchid: 0xda70d6,
|
|
119
|
+
palegoldenrod: 0xeee8aa,
|
|
120
|
+
palegreen: 0x98fb98,
|
|
121
|
+
paleturquoise: 0xafeeee,
|
|
122
|
+
palevioletred: 0xdb7093,
|
|
123
|
+
papayawhip: 0xffefd5,
|
|
124
|
+
peachpuff: 0xffdab9,
|
|
125
|
+
peru: 0xcd853f,
|
|
126
|
+
pink: 0xffc0cb,
|
|
127
|
+
plum: 0xdda0dd,
|
|
128
|
+
powderblue: 0xb0e0e6,
|
|
129
|
+
rosybrown: 0xbc8f8f,
|
|
130
|
+
royalblue: 0x4169e1,
|
|
131
|
+
saddlebrown: 0x8b4513,
|
|
132
|
+
salmon: 0xfa8072,
|
|
133
|
+
sandybrown: 0xf4a460,
|
|
134
|
+
seagreen: 0x2e8b57,
|
|
135
|
+
seashell: 0xfff5ee,
|
|
136
|
+
sienna: 0xa0522d,
|
|
137
|
+
skyblue: 0x87ceeb,
|
|
138
|
+
slateblue: 0x6a5acd,
|
|
139
|
+
slategray: 0x708090,
|
|
140
|
+
slategrey: 0x708090,
|
|
141
|
+
snow: 0xfffafa,
|
|
142
|
+
springgreen: 0x00ff7f,
|
|
143
|
+
steelblue: 0x4682b4,
|
|
144
|
+
tan: 0xd2b48c,
|
|
145
|
+
thistle: 0xd8bfd8,
|
|
146
|
+
tomato: 0xff6347,
|
|
147
|
+
turquoise: 0x40e0d0,
|
|
148
|
+
violet: 0xee82ee,
|
|
149
|
+
wheat: 0xf5deb3,
|
|
150
|
+
whitesmoke: 0xf5f5f5,
|
|
151
|
+
yellowgreen: 0x9acd32,
|
|
152
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { useCallback } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
useFrameCallback,
|
|
4
|
+
useSharedValue,
|
|
5
|
+
type FrameInfo,
|
|
6
|
+
type SharedValue,
|
|
7
|
+
} from 'react-native-reanimated';
|
|
8
|
+
|
|
9
|
+
export function useClock(): SharedValue<number> {
|
|
10
|
+
const clock = useSharedValue(0);
|
|
11
|
+
const callback = useCallback(
|
|
12
|
+
(info: FrameInfo) => {
|
|
13
|
+
'worklet';
|
|
14
|
+
clock.value = info.timeSinceFirstFrame;
|
|
15
|
+
},
|
|
16
|
+
[clock]
|
|
17
|
+
);
|
|
18
|
+
useFrameCallback(callback);
|
|
19
|
+
return clock;
|
|
20
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { PixelRatio } from 'react-native';
|
|
2
|
+
import {
|
|
3
|
+
useCanvasRef,
|
|
4
|
+
type CanvasRef,
|
|
5
|
+
type RNCanvasContext,
|
|
6
|
+
} from 'react-native-wgpu';
|
|
7
|
+
import { useEffect, useState } from 'react';
|
|
8
|
+
import type { WorkletRuntime } from 'react-native-worklets';
|
|
9
|
+
import { initWebGPU } from '../utils/initWebGPU';
|
|
10
|
+
import { BackgroundRuntime } from '../utils/backgroundRuntime';
|
|
11
|
+
|
|
12
|
+
type GPUResources = {
|
|
13
|
+
device: GPUDevice;
|
|
14
|
+
context: RNCanvasContext;
|
|
15
|
+
presentationFormat: GPUTextureFormat;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
type WGPUSetupResult = {
|
|
19
|
+
canvasRef: React.RefObject<CanvasRef>;
|
|
20
|
+
runtime: WorkletRuntime;
|
|
21
|
+
resources: GPUResources | null;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export function useWGPUSetup(): WGPUSetupResult {
|
|
25
|
+
const canvasRef = useCanvasRef();
|
|
26
|
+
const [resources, setResources] = useState<GPUResources | null>(null);
|
|
27
|
+
const runtime = BackgroundRuntime;
|
|
28
|
+
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
let cancelled = false;
|
|
31
|
+
|
|
32
|
+
(async () => {
|
|
33
|
+
const adapter = await navigator.gpu.requestAdapter();
|
|
34
|
+
if (!adapter || cancelled) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const device = await adapter.requestDevice();
|
|
39
|
+
if (cancelled) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const context = canvasRef.current!.getContext('webgpu')!;
|
|
44
|
+
const canvas = context.canvas as typeof context.canvas & {
|
|
45
|
+
width: number;
|
|
46
|
+
height: number;
|
|
47
|
+
};
|
|
48
|
+
const dpr = PixelRatio.get();
|
|
49
|
+
canvas.width = canvas.width * dpr;
|
|
50
|
+
canvas.height = canvas.height * dpr;
|
|
51
|
+
|
|
52
|
+
const presentationFormat = navigator.gpu.getPreferredCanvasFormat();
|
|
53
|
+
context.configure({
|
|
54
|
+
device,
|
|
55
|
+
format: presentationFormat,
|
|
56
|
+
alphaMode: 'premultiplied',
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
initWebGPU(runtime);
|
|
60
|
+
|
|
61
|
+
if (!cancelled) {
|
|
62
|
+
setResources({ device, context, presentationFormat });
|
|
63
|
+
}
|
|
64
|
+
})();
|
|
65
|
+
|
|
66
|
+
return () => {
|
|
67
|
+
cancelled = true;
|
|
68
|
+
};
|
|
69
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
70
|
+
}, []);
|
|
71
|
+
|
|
72
|
+
return { canvasRef, runtime, resources };
|
|
73
|
+
}
|
package/src/index.tsx
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import CircularGradient from './components/CircularGradient';
|
|
2
|
+
import LinearGradient from './components/LinearGradient';
|
|
3
|
+
import ShaderView from './components/ShaderView';
|
|
4
|
+
import Iridescence from './components/Iridescence';
|
|
5
|
+
import LiquidChrome from './components/LiquidChrome';
|
|
6
|
+
import Silk from './components/Silk';
|
|
7
|
+
import Campfire from './components/Campfire';
|
|
8
|
+
import CalicoSwirl from './components/CalicoSwirl';
|
|
9
|
+
import Aurora from './components/Aurora';
|
|
10
|
+
|
|
11
|
+
export type { ShaderViewProps } from './components/ShaderView/types';
|
|
12
|
+
|
|
13
|
+
export {
|
|
14
|
+
CircularGradient,
|
|
15
|
+
LinearGradient,
|
|
16
|
+
ShaderView,
|
|
17
|
+
Iridescence,
|
|
18
|
+
LiquidChrome,
|
|
19
|
+
Silk,
|
|
20
|
+
Campfire,
|
|
21
|
+
CalicoSwirl,
|
|
22
|
+
Aurora,
|
|
23
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export const TRIANGLE_VERTEX_SHADER = /* wgsl */ `
|
|
2
|
+
struct VSOut {
|
|
3
|
+
@builtin(position) pos: vec4<f32>,
|
|
4
|
+
@location(0) ndc: vec2<f32>,
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
@vertex
|
|
8
|
+
fn main(@builtin(vertex_index) vid: u32) -> VSOut {
|
|
9
|
+
var p = array<vec2<f32>,3>(
|
|
10
|
+
vec2<f32>(-1.0,-3.0), vec2<f32>(-1.0,1.0), vec2<f32>(3.0,1.0)
|
|
11
|
+
);
|
|
12
|
+
var o: VSOut;
|
|
13
|
+
o.pos = vec4<f32>(p[vid], 0, 1.0);
|
|
14
|
+
o.ndc = p[vid];
|
|
15
|
+
return o;
|
|
16
|
+
}
|
|
17
|
+
`;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/** 96 bytes = 6 × vec4<f32> */
|
|
2
|
+
export const UNIFORM_BUFFER_SIZE = 96;
|
|
3
|
+
|
|
4
|
+
/** Number of float32 values in the uniform buffer */
|
|
5
|
+
export const UNIFORM_FLOAT_COUNT = UNIFORM_BUFFER_SIZE / 4; // 24
|
|
6
|
+
|
|
7
|
+
export const UNIFORMS_WGSL = /* wgsl */ `
|
|
8
|
+
struct Uniforms {
|
|
9
|
+
resolution: vec4<f32>, // (width, height, aspect, pixelRatio)
|
|
10
|
+
time: vec4<f32>, // (seconds, dt, 0, 0)
|
|
11
|
+
color0: vec4<f32>, // colors[0] RGBA
|
|
12
|
+
color1: vec4<f32>, // colors[1] RGBA
|
|
13
|
+
params0: vec4<f32>, // params[0..3]
|
|
14
|
+
params1: vec4<f32>, // params[4..7]
|
|
15
|
+
};
|
|
16
|
+
@group(0) @binding(0) var<uniform> u: Uniforms;
|
|
17
|
+
`;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { createWorkletRuntime, scheduleOnRuntime } from 'react-native-worklets';
|
|
2
|
+
|
|
3
|
+
export const BackgroundRuntime = createWorkletRuntime({
|
|
4
|
+
name: 'react-native-effects',
|
|
5
|
+
});
|
|
6
|
+
|
|
7
|
+
export function runOnBackground(callback: (...args: any[]) => void) {
|
|
8
|
+
'worklet';
|
|
9
|
+
return scheduleOnRuntime(BackgroundRuntime, callback);
|
|
10
|
+
}
|