chroma-noise 0.0.2 → 0.0.4
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/README.md +1 -7
- package/dist/Gradient.svelte +111 -117
- package/dist/Gradient.svelte.d.ts +1 -1
- package/dist/{gradient.frag.glsl → fragment.txt} +13 -11
- package/dist/gradient.worker.d.ts +1 -0
- package/dist/gradient.worker.js +83 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/types.d.ts +16 -0
- package/dist/utils.d.ts +22 -0
- package/dist/utils.js +115 -0
- package/package.json +2 -3
- package/dist/gradient.vert.glsl +0 -4
package/README.md
CHANGED
|
@@ -1,12 +1,6 @@
|
|
|
1
1
|
# chroma-noise
|
|
2
2
|
|
|
3
|
-
Gradient noise for Svelte
|
|
4
|
-
|
|
5
|
-
### New in 0.0.2
|
|
6
|
-
- types
|
|
7
|
-
- new modes: swirl and radial waves
|
|
8
|
-
- removed unused dependencies
|
|
9
|
-
|
|
3
|
+
Gradient noise for Svelte. With warp and grain.
|
|
10
4
|
|
|
11
5
|
### Site & playground
|
|
12
6
|
https://chromanoise.netlify.app
|
package/dist/Gradient.svelte
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import
|
|
3
|
-
import frag from './gradient.frag.glsl?raw';
|
|
4
|
-
import { hexToRgb } from 'ventoui-utils';
|
|
2
|
+
import frag from './fragment.txt?raw';
|
|
5
3
|
import { onMount, onDestroy } from 'svelte';
|
|
6
4
|
import type { Warp, Grain, GradientOptions } from './index.js';
|
|
5
|
+
import { hexToRgb, isBrowser } from './utils.js';
|
|
6
|
+
import gradientWorker from './gradient.worker.js?url';
|
|
7
7
|
|
|
8
8
|
let {
|
|
9
9
|
points = [],
|
|
@@ -14,6 +14,8 @@
|
|
|
14
14
|
grain: initialGrain = {},
|
|
15
15
|
maxPoints = 12,
|
|
16
16
|
seed = Math.random(),
|
|
17
|
+
currentState = $bindable('loading'),
|
|
18
|
+
class: c = '',
|
|
17
19
|
...rest
|
|
18
20
|
}: GradientOptions = $props();
|
|
19
21
|
|
|
@@ -22,69 +24,17 @@
|
|
|
22
24
|
|
|
23
25
|
const warp: Required<Warp> = $derived({ ...defaultWarp, ...initialWarp });
|
|
24
26
|
const grain: Required<Grain> = $derived({ ...defaultGrain, ...initialGrain });
|
|
25
|
-
const browser = () => typeof window !== 'undefined' && typeof document !== 'undefined';
|
|
26
27
|
|
|
27
28
|
let canvas: HTMLCanvasElement | undefined = $state();
|
|
28
|
-
let
|
|
29
|
-
let
|
|
30
|
-
|
|
31
|
-
let uniforms: Record<string, WebGLUniformLocation> = {} as Record<string, WebGLUniformLocation>;
|
|
32
|
-
let frameId: number;
|
|
29
|
+
let worker: Worker | undefined = $state();
|
|
30
|
+
let frame: number;
|
|
33
31
|
let running = true;
|
|
34
32
|
let lastTime = 0;
|
|
35
33
|
let animTime = 0;
|
|
36
34
|
|
|
37
|
-
const shaderUniforms = ['u_resolution',
|
|
38
|
-
'u_time', 'u_count', 'u_colors',
|
|
39
|
-
'u_positions', 'u_radius', 'u_intensity',
|
|
40
|
-
'u_warpMode', 'u_warpSize', 'u_warpAmount',
|
|
41
|
-
'u_grainAmount', 'u_grainSize', 'u_seed'
|
|
42
|
-
];
|
|
43
|
-
|
|
44
|
-
function createShader(gl: WebGLRenderingContext, type: number, src: string) {
|
|
45
|
-
const s = gl.createShader(type)!;
|
|
46
|
-
gl.shaderSource(s, src);
|
|
47
|
-
gl.compileShader(s);
|
|
48
|
-
if (!gl.getShaderParameter(s, gl.COMPILE_STATUS)) {
|
|
49
|
-
console.error(gl.getShaderInfoLog(s));
|
|
50
|
-
gl.deleteShader(s);
|
|
51
|
-
throw new Error('Shader compile error');
|
|
52
|
-
}
|
|
53
|
-
return s;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
function createProgram(gl: WebGLRenderingContext, vsSrc: string, fsSrc: string) {
|
|
57
|
-
const vs = createShader(gl, gl.VERTEX_SHADER, vsSrc);
|
|
58
|
-
const fs = createShader(gl, gl.FRAGMENT_SHADER, fsSrc);
|
|
59
|
-
const p = gl.createProgram()!;
|
|
60
|
-
gl.attachShader(p, vs);
|
|
61
|
-
gl.attachShader(p, fs);
|
|
62
|
-
gl.linkProgram(p);
|
|
63
|
-
if (!gl.getProgramParameter(p, gl.LINK_STATUS)) {
|
|
64
|
-
console.error(gl.getProgramInfoLog(p));
|
|
65
|
-
gl.deleteProgram(p);
|
|
66
|
-
throw new Error('Program link error');
|
|
67
|
-
}
|
|
68
|
-
return p;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
function resizeCanvasToDisplaySize() {
|
|
72
|
-
if (!canvas) return false;
|
|
73
|
-
const dpr = Math.max(1, window.devicePixelRatio || 1);
|
|
74
|
-
const width = Math.floor(canvas.clientWidth * dpr);
|
|
75
|
-
const height = Math.floor(canvas.clientHeight * dpr);
|
|
76
|
-
if (canvas.width !== width || canvas.height !== height) {
|
|
77
|
-
canvas.width = width;
|
|
78
|
-
canvas.height = height;
|
|
79
|
-
return true;
|
|
80
|
-
}
|
|
81
|
-
return false;
|
|
82
|
-
}
|
|
83
35
|
|
|
84
36
|
function updateUniforms() {
|
|
85
|
-
if (!
|
|
86
|
-
gl.useProgram(program);
|
|
87
|
-
gl.uniform2f(uniforms.u_resolution, canvas.width, canvas.height);
|
|
37
|
+
if (!worker) return;
|
|
88
38
|
|
|
89
39
|
const colorsArr = new Float32Array(maxPoints * 3);
|
|
90
40
|
const posArr = new Float32Array(maxPoints * 2);
|
|
@@ -95,83 +45,127 @@
|
|
|
95
45
|
posArr.set([points[i].x, points[i].y], i * 2);
|
|
96
46
|
}
|
|
97
47
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
48
|
+
worker.postMessage({
|
|
49
|
+
type: 'updateUniforms',
|
|
50
|
+
uniforms: {
|
|
51
|
+
u_colors: colorsArr,
|
|
52
|
+
u_positions: posArr,
|
|
53
|
+
u_count: Math.min(points.length, maxPoints),
|
|
54
|
+
u_radius: radius,
|
|
55
|
+
u_intensity: intensity,
|
|
56
|
+
u_warpMode: warp.mode,
|
|
57
|
+
u_warpSize: warp.size,
|
|
58
|
+
u_warpAmount: warp.amount,
|
|
59
|
+
u_grainAmount: grain.amount,
|
|
60
|
+
u_grainSize: grain.size,
|
|
61
|
+
u_seed: seed
|
|
62
|
+
}
|
|
63
|
+
});
|
|
109
64
|
}
|
|
110
65
|
|
|
111
|
-
function
|
|
112
|
-
if (!
|
|
113
|
-
|
|
114
|
-
const
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
gl.useProgram(program);
|
|
124
|
-
gl.uniform1f(uniforms.u_time, animTime);
|
|
125
|
-
updateUniforms();
|
|
126
|
-
gl.drawArrays(gl.TRIANGLES, 0, 6);
|
|
127
|
-
|
|
128
|
-
if (running) frameId = requestAnimationFrame(render);
|
|
66
|
+
function updateResolution() {
|
|
67
|
+
if (!worker || !canvas) return;
|
|
68
|
+
const dpr = Math.max(1, window.devicePixelRatio || 1);
|
|
69
|
+
const width = Math.floor(canvas.clientWidth * dpr);
|
|
70
|
+
const height = Math.floor(canvas.clientHeight * dpr);
|
|
71
|
+
worker.postMessage({
|
|
72
|
+
type: 'updateUniforms',
|
|
73
|
+
uniforms: {
|
|
74
|
+
u_resolution: [width, height]
|
|
75
|
+
}
|
|
76
|
+
});
|
|
129
77
|
}
|
|
130
78
|
|
|
131
|
-
function
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
79
|
+
function render(time: number) {
|
|
80
|
+
if (worker) {
|
|
81
|
+
if (lastTime === 0) lastTime = time;
|
|
82
|
+
const dt = (time - lastTime) * 0.001;
|
|
83
|
+
lastTime = time;
|
|
84
|
+
animTime += dt * speed;
|
|
85
|
+
|
|
86
|
+
worker.postMessage({
|
|
87
|
+
type: 'render',
|
|
88
|
+
time: animTime
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
if (running) {
|
|
92
|
+
currentState = 'playing';
|
|
93
|
+
frame = requestAnimationFrame(render);
|
|
94
|
+
} else {
|
|
95
|
+
currentState = 'paused';
|
|
96
|
+
}
|
|
97
|
+
}
|
|
135
98
|
}
|
|
136
99
|
|
|
137
100
|
onMount(() => {
|
|
138
|
-
if (!canvas || !
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
101
|
+
if (!canvas || !isBrowser()) return;
|
|
102
|
+
currentState = 'loading';
|
|
103
|
+
|
|
104
|
+
worker = new Worker(gradientWorker, {
|
|
105
|
+
type: 'module'
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
worker.onmessage = (event: MessageEvent) => {
|
|
109
|
+
const { type } = event.data;
|
|
110
|
+
if (type === 'ready') {
|
|
111
|
+
updateUniforms();
|
|
112
|
+
currentState = 'playing';
|
|
113
|
+
} else if (type === 'rendering') {
|
|
114
|
+
currentState = 'playing';
|
|
115
|
+
} else if (type === 'paused') {
|
|
116
|
+
currentState = 'paused';
|
|
117
|
+
} else if (type === 'error') {
|
|
118
|
+
console.error('Worker error:', event.data.error);
|
|
119
|
+
currentState = 'paused';
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
const dpr = Math.max(1, window.devicePixelRatio || 1);
|
|
124
|
+
const width = Math.floor(canvas.clientWidth * dpr);
|
|
125
|
+
const height = Math.floor(canvas.clientHeight * dpr);
|
|
126
|
+
canvas.width = width;
|
|
127
|
+
canvas.height = height;
|
|
156
128
|
|
|
157
|
-
|
|
158
|
-
|
|
129
|
+
try {
|
|
130
|
+
const offscreenCanvas = canvas.transferControlToOffscreen();
|
|
131
|
+
worker.postMessage({
|
|
132
|
+
type: 'init',
|
|
133
|
+
offscreenCanvas,
|
|
134
|
+
fragShader: frag,
|
|
135
|
+
speed
|
|
136
|
+
}, [offscreenCanvas]);
|
|
137
|
+
|
|
138
|
+
const resizeObserver = new ResizeObserver(() => {
|
|
139
|
+
updateResolution();
|
|
140
|
+
});
|
|
141
|
+
resizeObserver.observe(canvas);
|
|
142
|
+
frame = requestAnimationFrame(render);
|
|
143
|
+
} catch (error) {
|
|
144
|
+
console.error('Could not transfer canvas to worker: ', error);
|
|
145
|
+
}
|
|
159
146
|
});
|
|
160
147
|
|
|
161
148
|
onDestroy(() => {
|
|
162
149
|
running = false;
|
|
163
|
-
if (!
|
|
164
|
-
|
|
165
|
-
|
|
150
|
+
if (!isBrowser()) return;
|
|
151
|
+
|
|
152
|
+
if (worker) {
|
|
153
|
+
worker.postMessage({ type: 'destroy' });
|
|
154
|
+
worker.terminate();
|
|
155
|
+
}
|
|
166
156
|
});
|
|
167
157
|
|
|
168
158
|
$effect(() => {
|
|
169
|
-
if
|
|
159
|
+
if(worker && (points.length || radius || intensity || warp || grain || seed)) {
|
|
160
|
+
updateUniforms();
|
|
161
|
+
}
|
|
170
162
|
});
|
|
171
163
|
</script>
|
|
172
164
|
|
|
173
|
-
<canvas bind:this={canvas} {...rest}></canvas>
|
|
165
|
+
<canvas bind:this={canvas} {...rest} class="{c} {currentState}"></canvas>
|
|
174
166
|
|
|
175
167
|
<style>
|
|
176
|
-
canvas { width: 100%; height: 100%; display: block; }
|
|
168
|
+
canvas { width: 100%; height: 100%; display: block; transition: opacity 1s ease-out }
|
|
169
|
+
canvas.loading { opacity: 0; }
|
|
170
|
+
canvas.playing { opacity: 1; }
|
|
177
171
|
</style>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import type { GradientOptions } from './index.js';
|
|
2
|
-
declare const Gradient: import("svelte").Component<GradientOptions, {}, "">;
|
|
2
|
+
declare const Gradient: import("svelte").Component<GradientOptions, {}, "currentState">;
|
|
3
3
|
type Gradient = ReturnType<typeof Gradient>;
|
|
4
4
|
export default Gradient;
|
|
@@ -92,24 +92,26 @@ vec2 warpSwirl(vec2 uv, float t) {
|
|
|
92
92
|
vec2 centered = uv - 0.5;
|
|
93
93
|
float radius = length(centered);
|
|
94
94
|
float angle = radius * u_warpSize * 5.0 - t * 0.5;
|
|
95
|
-
float s = sin(angle)
|
|
96
|
-
float c = cos(angle)
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
95
|
+
float s = sin(angle);
|
|
96
|
+
float c = cos(angle);
|
|
97
|
+
vec2 warped = vec2(c * centered.x - s * centered.y, s * centered.x + c * centered.y);
|
|
98
|
+
warped.x *= u_resolution.x / u_resolution.y;
|
|
99
|
+
warped = warped + 0.5;
|
|
100
|
+
return mix(uv, warped, u_warpAmount);
|
|
100
101
|
}
|
|
101
102
|
|
|
102
103
|
vec2 warpRadial(vec2 uv, float t) {
|
|
103
104
|
vec2 centered = uv - 0.5;
|
|
104
105
|
float r = length(centered);
|
|
105
106
|
float theta = atan(centered.y, centered.x);
|
|
106
|
-
float wave = sin(r * u_warpSize * 20.0 - t * 2.0)
|
|
107
|
-
r
|
|
107
|
+
float wave = sin(r * u_warpSize * 20.0 - t * 2.0);
|
|
108
|
+
float r_warped = r + wave;
|
|
108
109
|
vec2 p;
|
|
109
|
-
p.x =
|
|
110
|
-
p.y =
|
|
110
|
+
p.x = r_warped * cos(theta);
|
|
111
|
+
p.y = r_warped * sin(theta);
|
|
111
112
|
p.x *= u_resolution.x / u_resolution.y;
|
|
112
|
-
|
|
113
|
+
p = p + 0.5;
|
|
114
|
+
return mix(uv, p, u_warpAmount);
|
|
113
115
|
}
|
|
114
116
|
|
|
115
117
|
vec2 applyWarp(vec2 uv) {
|
|
@@ -152,4 +154,4 @@ void main() {
|
|
|
152
154
|
color *= mix(0.98, 1.02, vign);
|
|
153
155
|
color += g * u_grainAmount;
|
|
154
156
|
gl_FragColor = vec4(color, 1.0);
|
|
155
|
-
}
|
|
157
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { SHADER_UNIFORMS, VERTEX_SHADER, createProgram, getUniform, setupQuadBuffer, setUniform } from './utils.js';
|
|
2
|
+
let gl = null;
|
|
3
|
+
let program = null;
|
|
4
|
+
let uniforms = {};
|
|
5
|
+
let currentUniforms = {};
|
|
6
|
+
function initWorker(offscreenCanvas, fragShader) {
|
|
7
|
+
try {
|
|
8
|
+
gl = offscreenCanvas.getContext('webgl2', { antialias: true });
|
|
9
|
+
if (!gl) {
|
|
10
|
+
throw new Error('WebGL2 not supported');
|
|
11
|
+
}
|
|
12
|
+
program = createProgram(gl, VERTEX_SHADER, fragShader);
|
|
13
|
+
gl.useProgram(program);
|
|
14
|
+
setupQuadBuffer(gl, program);
|
|
15
|
+
for (const name of SHADER_UNIFORMS) {
|
|
16
|
+
uniforms[name] = getUniform(gl, program, name);
|
|
17
|
+
}
|
|
18
|
+
self.postMessage({ type: 'ready' });
|
|
19
|
+
}
|
|
20
|
+
catch (error) {
|
|
21
|
+
self.postMessage({ type: 'error', error: error.message });
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
function performRender(time) {
|
|
25
|
+
if (!gl || !program || Object.keys(currentUniforms).length === 0) {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
|
|
29
|
+
gl.clearColor(0, 0, 0, 0);
|
|
30
|
+
gl.clear(gl.COLOR_BUFFER_BIT);
|
|
31
|
+
gl.useProgram(program);
|
|
32
|
+
gl.uniform1f(uniforms.u_time, time);
|
|
33
|
+
for (const [key, value] of Object.entries(currentUniforms)) {
|
|
34
|
+
const uniform = uniforms[key];
|
|
35
|
+
if (uniform)
|
|
36
|
+
setUniform(gl, uniform, key, value);
|
|
37
|
+
}
|
|
38
|
+
gl.drawArrays(gl.TRIANGLES, 0, 6);
|
|
39
|
+
}
|
|
40
|
+
self.onmessage = (event) => {
|
|
41
|
+
const { type } = event.data;
|
|
42
|
+
switch (type) {
|
|
43
|
+
case 'init': {
|
|
44
|
+
const { offscreenCanvas, fragShader } = event.data;
|
|
45
|
+
if (offscreenCanvas && fragShader) {
|
|
46
|
+
initWorker(offscreenCanvas, fragShader);
|
|
47
|
+
}
|
|
48
|
+
break;
|
|
49
|
+
}
|
|
50
|
+
case 'render': {
|
|
51
|
+
const { time } = event.data;
|
|
52
|
+
if (time !== undefined) {
|
|
53
|
+
performRender(time);
|
|
54
|
+
}
|
|
55
|
+
break;
|
|
56
|
+
}
|
|
57
|
+
case 'updateUniforms': {
|
|
58
|
+
const { uniforms: newUniforms } = event.data;
|
|
59
|
+
if (newUniforms) {
|
|
60
|
+
currentUniforms = newUniforms;
|
|
61
|
+
}
|
|
62
|
+
break;
|
|
63
|
+
}
|
|
64
|
+
case 'resize': {
|
|
65
|
+
const { width, height, dpr } = event.data;
|
|
66
|
+
if (gl && width && height && dpr) {
|
|
67
|
+
const scaledWidth = Math.floor(width);
|
|
68
|
+
const scaledHeight = Math.floor(height);
|
|
69
|
+
gl.canvas.width = scaledWidth;
|
|
70
|
+
gl.canvas.height = scaledHeight;
|
|
71
|
+
currentUniforms = {
|
|
72
|
+
...currentUniforms,
|
|
73
|
+
u_resolution: [scaledWidth, scaledHeight]
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
break;
|
|
77
|
+
}
|
|
78
|
+
case 'destroy': {
|
|
79
|
+
break;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
export {};
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
package/dist/types.d.ts
CHANGED
|
@@ -29,6 +29,7 @@ export interface Grain {
|
|
|
29
29
|
/** Grain size, default: 1 */
|
|
30
30
|
size?: number;
|
|
31
31
|
}
|
|
32
|
+
export type shaderState = 'loading' | 'playing' | 'paused';
|
|
32
33
|
/**
|
|
33
34
|
* Configuration for the gradient.
|
|
34
35
|
*/
|
|
@@ -49,4 +50,19 @@ export interface GradientOptions {
|
|
|
49
50
|
seed?: number;
|
|
50
51
|
/** Grain effect configuration */
|
|
51
52
|
grain?: Grain;
|
|
53
|
+
/** Current state: 'loading', 'playing', or 'paused' */
|
|
54
|
+
currentState?: shaderState;
|
|
55
|
+
/** Class applied to the canvas element **/
|
|
56
|
+
class?: string;
|
|
57
|
+
}
|
|
58
|
+
export interface RenderMessage {
|
|
59
|
+
type: 'init' | 'render' | 'resize' | 'updateUniforms' | 'destroy';
|
|
60
|
+
offscreenCanvas?: OffscreenCanvas;
|
|
61
|
+
fragShader?: string;
|
|
62
|
+
width?: number;
|
|
63
|
+
height?: number;
|
|
64
|
+
dpr?: number;
|
|
65
|
+
uniforms?: Record<string, any>;
|
|
66
|
+
time?: number;
|
|
67
|
+
speed?: number;
|
|
52
68
|
}
|
package/dist/utils.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export declare function hexToRgb(hex: string): [number, number, number];
|
|
2
|
+
export declare const SHADER_UNIFORMS: readonly ["u_resolution", "u_time", "u_count", "u_colors", "u_positions", "u_radius", "u_intensity", "u_warpMode", "u_warpSize", "u_warpAmount", "u_grainAmount", "u_grainSize", "u_seed"];
|
|
3
|
+
export declare const VERTEX_SHADER = "attribute vec2 a_pos;void main() {gl_Position = vec4(a_pos, 0.0, 1.0);}";
|
|
4
|
+
export declare const FULLSCREEN_QUAD: Float32Array<ArrayBuffer>;
|
|
5
|
+
export declare function createShader(gl: WebGL2RenderingContext | WebGLRenderingContext, type: number, src: string): WebGLShader;
|
|
6
|
+
export declare function createProgram(gl: WebGL2RenderingContext | WebGLRenderingContext, vsSrc: string, fsSrc: string): WebGLProgram;
|
|
7
|
+
export declare function getUniform(gl: WebGL2RenderingContext | WebGLRenderingContext, program: WebGLProgram, name: string): WebGLUniformLocation;
|
|
8
|
+
export declare function setupQuadBuffer(gl: WebGL2RenderingContext | WebGLRenderingContext, program: WebGLProgram): void;
|
|
9
|
+
export declare function getCanvasSize(canvas: HTMLCanvasElement): {
|
|
10
|
+
width: number;
|
|
11
|
+
height: number;
|
|
12
|
+
dpr: number;
|
|
13
|
+
};
|
|
14
|
+
export declare const isBrowser: () => boolean;
|
|
15
|
+
/**
|
|
16
|
+
* Uniform type metadata for WebGL setters
|
|
17
|
+
*/
|
|
18
|
+
export declare const UNIFORM_TYPES: Record<string, 'vec2' | 'vec3f' | 'vec2f' | 'i32' | 'f32'>;
|
|
19
|
+
/**
|
|
20
|
+
* Set a WebGL uniform based on its type
|
|
21
|
+
*/
|
|
22
|
+
export declare function setUniform(gl: WebGL2RenderingContext, uniform: WebGLUniformLocation, key: string, value: any): void;
|
package/dist/utils.js
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
export function hexToRgb(hex) {
|
|
2
|
+
hex = hex.replace('#', '');
|
|
3
|
+
if (hex.length === 3)
|
|
4
|
+
hex = hex.split('').map(c => c + c).join('');
|
|
5
|
+
const num = parseInt(hex, 16);
|
|
6
|
+
const r = ((num >> 16) & 255) / 255;
|
|
7
|
+
const g = ((num >> 8) & 255) / 255;
|
|
8
|
+
const b = (num & 255) / 255;
|
|
9
|
+
return [r, g, b];
|
|
10
|
+
}
|
|
11
|
+
export const SHADER_UNIFORMS = [
|
|
12
|
+
'u_resolution',
|
|
13
|
+
'u_time',
|
|
14
|
+
'u_count',
|
|
15
|
+
'u_colors',
|
|
16
|
+
'u_positions',
|
|
17
|
+
'u_radius',
|
|
18
|
+
'u_intensity',
|
|
19
|
+
'u_warpMode',
|
|
20
|
+
'u_warpSize',
|
|
21
|
+
'u_warpAmount',
|
|
22
|
+
'u_grainAmount',
|
|
23
|
+
'u_grainSize',
|
|
24
|
+
'u_seed'
|
|
25
|
+
];
|
|
26
|
+
export const VERTEX_SHADER = `attribute vec2 a_pos;void main() {gl_Position = vec4(a_pos, 0.0, 1.0);}`;
|
|
27
|
+
export const FULLSCREEN_QUAD = new Float32Array([-1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1]);
|
|
28
|
+
export function createShader(gl, type, src) {
|
|
29
|
+
const s = gl.createShader(type);
|
|
30
|
+
gl.shaderSource(s, src);
|
|
31
|
+
gl.compileShader(s);
|
|
32
|
+
if (!gl.getShaderParameter(s, gl.COMPILE_STATUS)) {
|
|
33
|
+
const error = gl.getShaderInfoLog(s);
|
|
34
|
+
gl.deleteShader(s);
|
|
35
|
+
throw new Error(`Shader compile error: ${error}`);
|
|
36
|
+
}
|
|
37
|
+
return s;
|
|
38
|
+
}
|
|
39
|
+
export function createProgram(gl, vsSrc, fsSrc) {
|
|
40
|
+
const vs = createShader(gl, gl.VERTEX_SHADER, vsSrc);
|
|
41
|
+
const fs = createShader(gl, gl.FRAGMENT_SHADER, fsSrc);
|
|
42
|
+
const p = gl.createProgram();
|
|
43
|
+
gl.attachShader(p, vs);
|
|
44
|
+
gl.attachShader(p, fs);
|
|
45
|
+
gl.linkProgram(p);
|
|
46
|
+
if (!gl.getProgramParameter(p, gl.LINK_STATUS)) {
|
|
47
|
+
const error = gl.getProgramInfoLog(p);
|
|
48
|
+
gl.deleteProgram(p);
|
|
49
|
+
throw new Error(`Program link error: ${error}`);
|
|
50
|
+
}
|
|
51
|
+
return p;
|
|
52
|
+
}
|
|
53
|
+
export function getUniform(gl, program, name) {
|
|
54
|
+
const loc = gl.getUniformLocation(program, name);
|
|
55
|
+
if (!loc)
|
|
56
|
+
throw new Error(`Uniform ${name} not found`);
|
|
57
|
+
return loc;
|
|
58
|
+
}
|
|
59
|
+
export function setupQuadBuffer(gl, program) {
|
|
60
|
+
const vbo = gl.createBuffer();
|
|
61
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, vbo);
|
|
62
|
+
gl.bufferData(gl.ARRAY_BUFFER, FULLSCREEN_QUAD, gl.STATIC_DRAW);
|
|
63
|
+
const a_pos = gl.getAttribLocation(program, 'a_pos');
|
|
64
|
+
gl.enableVertexAttribArray(a_pos);
|
|
65
|
+
gl.vertexAttribPointer(a_pos, 2, gl.FLOAT, false, 0, 0);
|
|
66
|
+
}
|
|
67
|
+
export function getCanvasSize(canvas) {
|
|
68
|
+
const dpr = Math.max(1, window.devicePixelRatio || 1);
|
|
69
|
+
const width = Math.floor(canvas.clientWidth * dpr);
|
|
70
|
+
const height = Math.floor(canvas.clientHeight * dpr);
|
|
71
|
+
return { width, height, dpr };
|
|
72
|
+
}
|
|
73
|
+
export const isBrowser = () => typeof window !== 'undefined' && typeof document !== 'undefined';
|
|
74
|
+
/**
|
|
75
|
+
* Uniform type metadata for WebGL setters
|
|
76
|
+
*/
|
|
77
|
+
export const UNIFORM_TYPES = {
|
|
78
|
+
u_resolution: 'vec2',
|
|
79
|
+
u_colors: 'vec3f',
|
|
80
|
+
u_positions: 'vec2f',
|
|
81
|
+
u_count: 'i32',
|
|
82
|
+
u_warpMode: 'i32',
|
|
83
|
+
u_radius: 'f32',
|
|
84
|
+
u_intensity: 'f32',
|
|
85
|
+
u_warpSize: 'f32',
|
|
86
|
+
u_warpAmount: 'f32',
|
|
87
|
+
u_grainAmount: 'f32',
|
|
88
|
+
u_grainSize: 'f32',
|
|
89
|
+
u_seed: 'f32'
|
|
90
|
+
};
|
|
91
|
+
/**
|
|
92
|
+
* Set a WebGL uniform based on its type
|
|
93
|
+
*/
|
|
94
|
+
export function setUniform(gl, uniform, key, value) {
|
|
95
|
+
const type = UNIFORM_TYPES[key];
|
|
96
|
+
if (!type)
|
|
97
|
+
return;
|
|
98
|
+
switch (type) {
|
|
99
|
+
case 'vec2':
|
|
100
|
+
gl.uniform2f(uniform, value[0], value[1]);
|
|
101
|
+
break;
|
|
102
|
+
case 'vec3f':
|
|
103
|
+
gl.uniform3fv(uniform, value);
|
|
104
|
+
break;
|
|
105
|
+
case 'vec2f':
|
|
106
|
+
gl.uniform2fv(uniform, value);
|
|
107
|
+
break;
|
|
108
|
+
case 'i32':
|
|
109
|
+
gl.uniform1i(uniform, value);
|
|
110
|
+
break;
|
|
111
|
+
case 'f32':
|
|
112
|
+
gl.uniform1f(uniform, value);
|
|
113
|
+
break;
|
|
114
|
+
}
|
|
115
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "chroma-noise",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.4",
|
|
4
4
|
"scripts": {
|
|
5
5
|
"dev": "vite dev",
|
|
6
6
|
"build": "vite build && npm run prepack",
|
|
@@ -30,8 +30,7 @@
|
|
|
30
30
|
}
|
|
31
31
|
},
|
|
32
32
|
"peerDependencies": {
|
|
33
|
-
"svelte": "^5.0.0"
|
|
34
|
-
"ventoui-utils": "^0.0.2"
|
|
33
|
+
"svelte": "^5.0.0"
|
|
35
34
|
},
|
|
36
35
|
"devDependencies": {
|
|
37
36
|
"@sveltejs/adapter-auto": "^6.1.0",
|
package/dist/gradient.vert.glsl
DELETED