shaderz 1.0.0 → 1.0.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.
- package/dist/cli.js +41 -13
- package/package.json +2 -2
- package/shaders/DarkVeilShader.tsx +337 -0
- package/shaders/FrothyGalaxyShader.tsx +216 -0
- package/shaders/LiqMotion.css +13 -0
- package/shaders/LiquidMotionShader.tsx +566 -0
- package/shaders/PlasmaV2Shader.tsx +188 -0
- package/videos/nova-silk.mp4 +0 -0
- package/shaders/GlassTwistShader.tsx +0 -169
- package/shaders/GlossyRibbonShader.tsx +0 -164
package/dist/cli.js
CHANGED
|
@@ -62,30 +62,43 @@ var SHADERS = [
|
|
|
62
62
|
description: "Space-themed nebula shader",
|
|
63
63
|
file: "CosmicNebulaShader"
|
|
64
64
|
},
|
|
65
|
-
{
|
|
66
|
-
name: "glossy-ribbon",
|
|
67
|
-
title: "Glossy Ribbon",
|
|
68
|
-
description: "Glossy ribbon flow shader",
|
|
69
|
-
file: "GlossyRibbonShader"
|
|
70
|
-
},
|
|
71
65
|
{
|
|
72
66
|
name: "silk-flow",
|
|
73
67
|
title: "Silk Flow",
|
|
74
68
|
description: "Smooth silk flow shader",
|
|
75
69
|
file: "SilkFlowShader"
|
|
76
70
|
},
|
|
77
|
-
{
|
|
78
|
-
name: "glass-twist",
|
|
79
|
-
title: "Glass Twist",
|
|
80
|
-
description: "Glass twist effect shader",
|
|
81
|
-
file: "GlassTwistShader"
|
|
82
|
-
},
|
|
83
71
|
{
|
|
84
72
|
name: "plasma",
|
|
85
73
|
title: "Plasma",
|
|
86
74
|
description: "Classic plasma shader",
|
|
87
75
|
file: "PlasmaShader"
|
|
88
76
|
},
|
|
77
|
+
{
|
|
78
|
+
name: "plasma-v2",
|
|
79
|
+
title: "Plasma V2",
|
|
80
|
+
description: "Enhanced plasma shader with more colors",
|
|
81
|
+
file: "PlasmaV2Shader"
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
name: "dark-veil",
|
|
85
|
+
title: "Dark Veil",
|
|
86
|
+
description: "Mysterious dark veil with blue/purple gradient",
|
|
87
|
+
file: "DarkVeilShader"
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
name: "liquid-motion",
|
|
91
|
+
title: "Liquid Motion",
|
|
92
|
+
description: "Advanced fluid simulation with Three.js",
|
|
93
|
+
file: "LiquidMotionShader",
|
|
94
|
+
hasCss: true
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
name: "frothy-galaxy",
|
|
98
|
+
title: "Frothy Galaxy",
|
|
99
|
+
description: "Galactic frothy effect shader",
|
|
100
|
+
file: "FrothyGalaxyShader"
|
|
101
|
+
},
|
|
89
102
|
{
|
|
90
103
|
name: "glossy-film",
|
|
91
104
|
title: "Glossy Film (Video)",
|
|
@@ -93,6 +106,14 @@ var SHADERS = [
|
|
|
93
106
|
file: "VideoBackground",
|
|
94
107
|
isVideo: true,
|
|
95
108
|
videoFile: "glossy-film.mp4"
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
name: "nova-silk",
|
|
112
|
+
title: "Nova Silk (Video)",
|
|
113
|
+
description: "Elegant flowing silk video background",
|
|
114
|
+
file: "VideoBackground",
|
|
115
|
+
isVideo: true,
|
|
116
|
+
videoFile: "nova-silk.mp4"
|
|
96
117
|
}
|
|
97
118
|
];
|
|
98
119
|
async function addShaders() {
|
|
@@ -167,7 +188,14 @@ No components directory found. Will create: ${componentsBase}/shaders/`));
|
|
|
167
188
|
const sourceFile = import_path.default.join(__dirname, "..", "shaders", `${shader.file}.tsx`);
|
|
168
189
|
const targetFile = import_path.default.join(componentsDir, `${shader.file}${fileExtension}`);
|
|
169
190
|
await import_fs_extra.default.copy(sourceFile, targetFile);
|
|
170
|
-
|
|
191
|
+
if (shader.hasCss) {
|
|
192
|
+
const sourceCss = import_path.default.join(__dirname, "..", "shaders", "LiqMotion.css");
|
|
193
|
+
const targetCss = import_path.default.join(componentsDir, "LiqMotion.css");
|
|
194
|
+
await import_fs_extra.default.copy(sourceCss, targetCss);
|
|
195
|
+
spinner.succeed(`Added ${import_chalk.default.green(shader.title)} (with CSS file)`);
|
|
196
|
+
} else {
|
|
197
|
+
spinner.succeed(`Added ${import_chalk.default.green(shader.title)}`);
|
|
198
|
+
}
|
|
171
199
|
spinner.start();
|
|
172
200
|
}
|
|
173
201
|
spinner.stop();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "shaderz",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "CLI tool to add beautiful WebGL shaders to your React/Next.js project",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -48,4 +48,4 @@
|
|
|
48
48
|
"url": "https://github.com/harsh-and-shubham/shaderz/issues"
|
|
49
49
|
},
|
|
50
50
|
"homepage": "https://shaderz.vercel.app"
|
|
51
|
-
}
|
|
51
|
+
}
|
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { useEffect, useRef, useCallback } from 'react';
|
|
3
|
+
|
|
4
|
+
function DarkVeil() {
|
|
5
|
+
const canvasRef = useRef<HTMLCanvasElement>(null);
|
|
6
|
+
const glRef = useRef<WebGLRenderingContext | null>(null);
|
|
7
|
+
const programRef = useRef<WebGLProgram | null>(null);
|
|
8
|
+
const animationRef = useRef<number | null>(null);
|
|
9
|
+
const isContextLostRef = useRef(false);
|
|
10
|
+
|
|
11
|
+
const createRenderer = useCallback(() => {
|
|
12
|
+
const canvas = canvasRef.current;
|
|
13
|
+
if (!canvas) return null;
|
|
14
|
+
|
|
15
|
+
const gl = canvas.getContext('webgl', {
|
|
16
|
+
preserveDrawingBuffer: true,
|
|
17
|
+
powerPreference: 'high-performance',
|
|
18
|
+
failIfMajorPerformanceCaveat: false,
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
if (!gl) {
|
|
22
|
+
console.error('WebGL not supported');
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return gl;
|
|
27
|
+
}, []);
|
|
28
|
+
|
|
29
|
+
const compileShaderProgram = useCallback((gl: WebGLRenderingContext) => {
|
|
30
|
+
const vertexShaderSource = `
|
|
31
|
+
attribute vec2 position;
|
|
32
|
+
void main() {
|
|
33
|
+
gl_Position = vec4(position, 0.0, 1.0);
|
|
34
|
+
}
|
|
35
|
+
`;
|
|
36
|
+
|
|
37
|
+
const fragmentShaderSource = `
|
|
38
|
+
precision highp float;
|
|
39
|
+
uniform vec2 resolution;
|
|
40
|
+
uniform float time;
|
|
41
|
+
|
|
42
|
+
// Organic procedural pattern generation
|
|
43
|
+
float seedPoint(vec2 p) {
|
|
44
|
+
return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
float organicBlend(vec2 p) {
|
|
48
|
+
vec2 i = floor(p);
|
|
49
|
+
vec2 f = fract(p);
|
|
50
|
+
f = f * f * (3.0 - 2.0 * f);
|
|
51
|
+
|
|
52
|
+
float a = seedPoint(i);
|
|
53
|
+
float b = seedPoint(i + vec2(1.0, 0.0));
|
|
54
|
+
float c = seedPoint(i + vec2(0.0, 1.0));
|
|
55
|
+
float d = seedPoint(i + vec2(1.0, 1.0));
|
|
56
|
+
|
|
57
|
+
return mix(mix(a, b, f.x), mix(c, d, f.x), f.y);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
void main() {
|
|
61
|
+
vec2 uv = gl_FragCoord.xy / resolution.xy;
|
|
62
|
+
vec2 center = uv - 0.5;
|
|
63
|
+
center.x *= resolution.x / resolution.y;
|
|
64
|
+
|
|
65
|
+
// Floating movement - smooth and organic
|
|
66
|
+
float floatX = sin(time * 0.3) * 0.15 + cos(time * 0.2) * 0.1;
|
|
67
|
+
float floatY = cos(time * 0.25) * 0.12 + sin(time * 0.35) * 0.08;
|
|
68
|
+
center -= vec2(floatX, floatY);
|
|
69
|
+
|
|
70
|
+
// Distance from center
|
|
71
|
+
float dist = length(center);
|
|
72
|
+
|
|
73
|
+
// Smooth noise-based distortion (no sharp angles)
|
|
74
|
+
float n1 = organicBlend(center * 3.0 + time * 0.2) * 0.1;
|
|
75
|
+
float n2 = organicBlend(center * 5.0 - time * 0.15) * 0.05;
|
|
76
|
+
dist += n1 + n2;
|
|
77
|
+
|
|
78
|
+
// Very soft gradient falloff - blurred edges
|
|
79
|
+
float gradient = 1.0 - smoothstep(0.0, 0.8, dist);
|
|
80
|
+
gradient = pow(gradient, 0.8); // Softer power for blur effect
|
|
81
|
+
|
|
82
|
+
// Smooth the center completely - no sharp point
|
|
83
|
+
float centerBlur = smoothstep(0.0, 0.15, dist);
|
|
84
|
+
gradient *= centerBlur * 0.5 + 0.5;
|
|
85
|
+
|
|
86
|
+
// Blue to Purple gradient based on smooth organicBlend
|
|
87
|
+
vec3 blue = vec3(0.2, 0.4, 0.9);
|
|
88
|
+
vec3 purple = vec3(0.6, 0.2, 0.8);
|
|
89
|
+
vec3 magenta = vec3(0.8, 0.1, 0.6);
|
|
90
|
+
|
|
91
|
+
// Smooth color mixing based on organicBlend (not angle)
|
|
92
|
+
float colorVariation = organicBlend(center * 2.0 + time * 0.1);
|
|
93
|
+
float colorVariation2 = organicBlend(center * 1.5 - time * 0.08);
|
|
94
|
+
|
|
95
|
+
vec3 gradientColor = mix(blue, purple, colorVariation);
|
|
96
|
+
gradientColor = mix(gradientColor, magenta, colorVariation2 * 0.4);
|
|
97
|
+
|
|
98
|
+
// Apply gradient strength
|
|
99
|
+
vec3 col = gradientColor * gradient;
|
|
100
|
+
|
|
101
|
+
// Soft outer glow
|
|
102
|
+
float glow = 1.0 - smoothstep(0.0, 1.2, dist);
|
|
103
|
+
glow = pow(glow, 2.5) * 0.2;
|
|
104
|
+
col += gradientColor * glow;
|
|
105
|
+
|
|
106
|
+
// Pure black background
|
|
107
|
+
gl_FragColor = vec4(col, 1.0);
|
|
108
|
+
}
|
|
109
|
+
`;
|
|
110
|
+
|
|
111
|
+
const buildGLShader = (gl: WebGLRenderingContext, type: number, source: string) => {
|
|
112
|
+
const shader = gl.createShader(type);
|
|
113
|
+
if (!shader) return null;
|
|
114
|
+
|
|
115
|
+
gl.shaderSource(shader, source);
|
|
116
|
+
gl.compileShader(shader);
|
|
117
|
+
|
|
118
|
+
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
|
|
119
|
+
console.error('Shader compile error:', gl.getShaderInfoLog(shader));
|
|
120
|
+
gl.deleteShader(shader);
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
return shader;
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
const vertexShader = buildGLShader(gl, gl.VERTEX_SHADER, vertexShaderSource);
|
|
127
|
+
const fragmentShader = buildGLShader(gl, gl.FRAGMENT_SHADER, fragmentShaderSource);
|
|
128
|
+
|
|
129
|
+
if (!vertexShader || !fragmentShader) return null;
|
|
130
|
+
|
|
131
|
+
const program = gl.createProgram();
|
|
132
|
+
if (!program) return null;
|
|
133
|
+
|
|
134
|
+
gl.attachShader(program, vertexShader);
|
|
135
|
+
gl.attachShader(program, fragmentShader);
|
|
136
|
+
gl.linkProgram(program);
|
|
137
|
+
|
|
138
|
+
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
|
|
139
|
+
console.error('Program link error:', gl.getProgramInfoLog(program));
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return program;
|
|
144
|
+
}, []);
|
|
145
|
+
|
|
146
|
+
useEffect(() => {
|
|
147
|
+
const canvas = canvasRef.current;
|
|
148
|
+
if (!canvas) return;
|
|
149
|
+
|
|
150
|
+
let gl = createRenderer();
|
|
151
|
+
if (!gl) return;
|
|
152
|
+
|
|
153
|
+
glRef.current = gl;
|
|
154
|
+
|
|
155
|
+
const updateCanvasSize = () => {
|
|
156
|
+
if (!canvas || !gl || isContextLostRef.current) return;
|
|
157
|
+
const parent = canvas.parentElement;
|
|
158
|
+
if (parent) {
|
|
159
|
+
canvas.width = parent.clientWidth;
|
|
160
|
+
canvas.height = parent.clientHeight;
|
|
161
|
+
} else {
|
|
162
|
+
canvas.width = window.innerWidth;
|
|
163
|
+
canvas.height = window.innerHeight;
|
|
164
|
+
}
|
|
165
|
+
gl.viewport(0, 0, canvas.width, canvas.height);
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
updateCanvasSize();
|
|
169
|
+
window.addEventListener('resize', updateCanvasSize);
|
|
170
|
+
|
|
171
|
+
let program = compileShaderProgram(gl);
|
|
172
|
+
if (!program) return;
|
|
173
|
+
|
|
174
|
+
programRef.current = program;
|
|
175
|
+
|
|
176
|
+
const positionBuffer = gl.createBuffer();
|
|
177
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
|
|
178
|
+
const positions = new Float32Array([
|
|
179
|
+
-1, -1,
|
|
180
|
+
1, -1,
|
|
181
|
+
-1, 1,
|
|
182
|
+
1, 1
|
|
183
|
+
]);
|
|
184
|
+
gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW);
|
|
185
|
+
|
|
186
|
+
let positionLocation = gl.getAttribLocation(program, 'position');
|
|
187
|
+
let resolutionLocation = gl.getUniformLocation(program, 'resolution');
|
|
188
|
+
let timeLocation = gl.getUniformLocation(program, 'time');
|
|
189
|
+
|
|
190
|
+
const startTime = performance.now();
|
|
191
|
+
|
|
192
|
+
const drawFrame = () => {
|
|
193
|
+
if (isContextLostRef.current || !gl || !program) {
|
|
194
|
+
animationRef.current = requestAnimationFrame(drawFrame);
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const elapsedTime = (performance.now() - startTime) * 0.001;
|
|
199
|
+
|
|
200
|
+
gl.clearColor(0.0, 0.0, 0.0, 1.0); // Pure black
|
|
201
|
+
gl.clear(gl.COLOR_BUFFER_BIT);
|
|
202
|
+
gl.useProgram(program);
|
|
203
|
+
|
|
204
|
+
gl.enableVertexAttribArray(positionLocation);
|
|
205
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
|
|
206
|
+
gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
|
|
207
|
+
|
|
208
|
+
gl.uniform2f(resolutionLocation, canvas.width, canvas.height);
|
|
209
|
+
gl.uniform1f(timeLocation, elapsedTime);
|
|
210
|
+
|
|
211
|
+
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
|
|
212
|
+
|
|
213
|
+
animationRef.current = requestAnimationFrame(drawFrame);
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
const onContextLost = (event: Event) => {
|
|
217
|
+
event.preventDefault();
|
|
218
|
+
isContextLostRef.current = true;
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
const onContextRestored = () => {
|
|
222
|
+
isContextLostRef.current = false;
|
|
223
|
+
gl = createRenderer();
|
|
224
|
+
if (!gl) return;
|
|
225
|
+
glRef.current = gl;
|
|
226
|
+
updateCanvasSize();
|
|
227
|
+
program = compileShaderProgram(gl);
|
|
228
|
+
if (!program) return;
|
|
229
|
+
programRef.current = program;
|
|
230
|
+
|
|
231
|
+
const newBuffer = gl.createBuffer();
|
|
232
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, newBuffer);
|
|
233
|
+
gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW);
|
|
234
|
+
|
|
235
|
+
positionLocation = gl.getAttribLocation(program, 'position');
|
|
236
|
+
resolutionLocation = gl.getUniformLocation(program, 'resolution');
|
|
237
|
+
timeLocation = gl.getUniformLocation(program, 'time');
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
const onVisibilityChange = () => {
|
|
241
|
+
if (document.visibilityState === 'visible' && !isContextLostRef.current) {
|
|
242
|
+
updateCanvasSize();
|
|
243
|
+
}
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
canvas.addEventListener('webglcontextlost', onContextLost);
|
|
247
|
+
canvas.addEventListener('webglcontextrestored', onContextRestored);
|
|
248
|
+
document.addEventListener('visibilitychange', onVisibilityChange);
|
|
249
|
+
|
|
250
|
+
animationRef.current = requestAnimationFrame(drawFrame);
|
|
251
|
+
|
|
252
|
+
return () => {
|
|
253
|
+
window.removeEventListener('resize', updateCanvasSize);
|
|
254
|
+
canvas.removeEventListener('webglcontextlost', onContextLost);
|
|
255
|
+
canvas.removeEventListener('webglcontextrestored', onContextRestored);
|
|
256
|
+
document.removeEventListener('visibilitychange', onVisibilityChange);
|
|
257
|
+
if (animationRef.current) {
|
|
258
|
+
cancelAnimationFrame(animationRef.current);
|
|
259
|
+
}
|
|
260
|
+
};
|
|
261
|
+
}, [createRenderer, compileShaderProgram]);
|
|
262
|
+
|
|
263
|
+
return (
|
|
264
|
+
<div className="absolute inset-0 w-full h-full bg-black overflow-hidden">
|
|
265
|
+
<canvas
|
|
266
|
+
ref={canvasRef}
|
|
267
|
+
className="absolute top-0 left-0 w-full h-full pointer-events-none"
|
|
268
|
+
/>
|
|
269
|
+
</div>
|
|
270
|
+
);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// Export the fragment shader for ShaderPreview compatibility
|
|
274
|
+
export const fragment = `
|
|
275
|
+
precision highp float;
|
|
276
|
+
uniform vec2 resolution;
|
|
277
|
+
uniform float time;
|
|
278
|
+
|
|
279
|
+
float seedPoint(vec2 p) {
|
|
280
|
+
return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
float organicBlend(vec2 p) {
|
|
284
|
+
vec2 i = floor(p);
|
|
285
|
+
vec2 f = fract(p);
|
|
286
|
+
f = f * f * (3.0 - 2.0 * f);
|
|
287
|
+
|
|
288
|
+
float a = seedPoint(i);
|
|
289
|
+
float b = seedPoint(i + vec2(1.0, 0.0));
|
|
290
|
+
float c = seedPoint(i + vec2(0.0, 1.0));
|
|
291
|
+
float d = seedPoint(i + vec2(1.0, 1.0));
|
|
292
|
+
|
|
293
|
+
return mix(mix(a, b, f.x), mix(c, d, f.x), f.y);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
void main() {
|
|
297
|
+
vec2 uv = gl_FragCoord.xy / resolution.xy;
|
|
298
|
+
vec2 center = uv - 0.5;
|
|
299
|
+
center.x *= resolution.x / resolution.y;
|
|
300
|
+
|
|
301
|
+
float floatX = sin(time * 0.3) * 0.15 + cos(time * 0.2) * 0.1;
|
|
302
|
+
float floatY = cos(time * 0.25) * 0.12 + sin(time * 0.35) * 0.08;
|
|
303
|
+
center -= vec2(floatX, floatY);
|
|
304
|
+
|
|
305
|
+
float dist = length(center);
|
|
306
|
+
|
|
307
|
+
float n1 = organicBlend(center * 3.0 + time * 0.2) * 0.1;
|
|
308
|
+
float n2 = organicBlend(center * 5.0 - time * 0.15) * 0.05;
|
|
309
|
+
dist += n1 + n2;
|
|
310
|
+
|
|
311
|
+
float gradient = 1.0 - smoothstep(0.0, 0.8, dist);
|
|
312
|
+
gradient = pow(gradient, 0.8);
|
|
313
|
+
|
|
314
|
+
float centerBlur = smoothstep(0.0, 0.15, dist);
|
|
315
|
+
gradient *= centerBlur * 0.5 + 0.5;
|
|
316
|
+
|
|
317
|
+
vec3 blue = vec3(0.2, 0.4, 0.9);
|
|
318
|
+
vec3 purple = vec3(0.6, 0.2, 0.8);
|
|
319
|
+
vec3 magenta = vec3(0.8, 0.1, 0.6);
|
|
320
|
+
|
|
321
|
+
float colorVariation = organicBlend(center * 2.0 + time * 0.1);
|
|
322
|
+
float colorVariation2 = organicBlend(center * 1.5 - time * 0.08);
|
|
323
|
+
|
|
324
|
+
vec3 gradientColor = mix(blue, purple, colorVariation);
|
|
325
|
+
gradientColor = mix(gradientColor, magenta, colorVariation2 * 0.4);
|
|
326
|
+
|
|
327
|
+
vec3 col = gradientColor * gradient;
|
|
328
|
+
|
|
329
|
+
float glow = 1.0 - smoothstep(0.0, 1.2, dist);
|
|
330
|
+
glow = pow(glow, 2.5) * 0.2;
|
|
331
|
+
col += gradientColor * glow;
|
|
332
|
+
|
|
333
|
+
gl_FragColor = vec4(col, 1.0);
|
|
334
|
+
}
|
|
335
|
+
`;
|
|
336
|
+
|
|
337
|
+
export default DarkVeil;
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import React, { useRef, useEffect } from 'react';
|
|
3
|
+
|
|
4
|
+
const FrothyGalaxyShader: React.FC = () => {
|
|
5
|
+
const canvasRef = useRef<HTMLCanvasElement>(null);
|
|
6
|
+
const containerRef = useRef<HTMLDivElement>(null);
|
|
7
|
+
const animationRef = useRef<number | null>(null);
|
|
8
|
+
|
|
9
|
+
useEffect(() => {
|
|
10
|
+
const canvas = canvasRef.current;
|
|
11
|
+
const container = containerRef.current;
|
|
12
|
+
if (!canvas || !container) return;
|
|
13
|
+
|
|
14
|
+
const gl = canvas.getContext('webgl2') || canvas.getContext('webgl');
|
|
15
|
+
if (!gl) return;
|
|
16
|
+
|
|
17
|
+
const resizeCanvas = () => {
|
|
18
|
+
const rect = container.getBoundingClientRect();
|
|
19
|
+
canvas.width = rect.width;
|
|
20
|
+
canvas.height = rect.height;
|
|
21
|
+
gl.viewport(0, 0, canvas.width, canvas.height);
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
resizeCanvas();
|
|
25
|
+
window.addEventListener('resize', resizeCanvas);
|
|
26
|
+
|
|
27
|
+
const vertexShaderSource = `
|
|
28
|
+
attribute vec2 position;
|
|
29
|
+
void main() {
|
|
30
|
+
gl_Position = vec4(position, 0.0, 1.0);
|
|
31
|
+
}
|
|
32
|
+
`;
|
|
33
|
+
|
|
34
|
+
const fragmentShaderSource = `
|
|
35
|
+
precision highp float;
|
|
36
|
+
uniform vec2 resolution;
|
|
37
|
+
uniform float time;
|
|
38
|
+
|
|
39
|
+
float hash(vec2 p) {
|
|
40
|
+
p = fract(p * vec2(123.34, 456.21));
|
|
41
|
+
p += dot(p, p + 45.32);
|
|
42
|
+
return fract(p.x * p.y);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
float noise(vec2 p) {
|
|
46
|
+
vec2 i = floor(p);
|
|
47
|
+
vec2 f = fract(p);
|
|
48
|
+
f = f * f * (3.0 - 2.0 * f);
|
|
49
|
+
|
|
50
|
+
float a = hash(i);
|
|
51
|
+
float b = hash(i + vec2(1.0, 0.0));
|
|
52
|
+
float c = hash(i + vec2(0.0, 1.0));
|
|
53
|
+
float d = hash(i + vec2(1.0, 1.0));
|
|
54
|
+
|
|
55
|
+
return mix(mix(a, b, f.x), mix(c, d, f.x), f.y);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
float fbm(vec2 p) {
|
|
59
|
+
float value = 0.0;
|
|
60
|
+
float amplitude = 0.5;
|
|
61
|
+
float frequency = 1.0;
|
|
62
|
+
|
|
63
|
+
for(int i = 0; i < 5; i++) {
|
|
64
|
+
value += amplitude * noise(p * frequency);
|
|
65
|
+
frequency *= 2.0;
|
|
66
|
+
amplitude *= 0.5;
|
|
67
|
+
}
|
|
68
|
+
return value;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
void main() {
|
|
72
|
+
vec2 uv = gl_FragCoord.xy / resolution.xy;
|
|
73
|
+
vec2 p = (uv - 0.5) * 2.0;
|
|
74
|
+
p.x *= resolution.x / resolution.y;
|
|
75
|
+
|
|
76
|
+
// Smooth flowing waves
|
|
77
|
+
float wave1 = sin(p.x * 2.0 + time * 0.5) * 0.3;
|
|
78
|
+
float wave2 = sin(p.x * 1.5 - p.y * 0.8 + time * 0.4) * 0.2;
|
|
79
|
+
float wave3 = sin(p.x * 3.0 + p.y * 1.5 + time * 0.6) * 0.15;
|
|
80
|
+
float waves = wave1 + wave2 + wave3;
|
|
81
|
+
|
|
82
|
+
// Add noise detail
|
|
83
|
+
waves += fbm(p * 2.0 + vec2(time * 0.1, 0.0)) * 0.2;
|
|
84
|
+
|
|
85
|
+
// Normalized wave height
|
|
86
|
+
float h = waves * 0.5 + 0.5;
|
|
87
|
+
|
|
88
|
+
// Beautiful gradient colors
|
|
89
|
+
vec3 color1 = vec3(0.1, 0.3, 0.6); // Deep blue
|
|
90
|
+
vec3 color2 = vec3(0.2, 0.5, 0.8); // Medium blue
|
|
91
|
+
vec3 color3 = vec3(0.4, 0.7, 0.9); // Light blue
|
|
92
|
+
vec3 color4 = vec3(0.6, 0.85, 0.95); // Pale blue
|
|
93
|
+
|
|
94
|
+
// Smooth color transitions
|
|
95
|
+
vec3 color;
|
|
96
|
+
if (h < 0.33) {
|
|
97
|
+
color = mix(color1, color2, h * 3.0);
|
|
98
|
+
} else if (h < 0.66) {
|
|
99
|
+
color = mix(color2, color3, (h - 0.33) * 3.0);
|
|
100
|
+
} else {
|
|
101
|
+
color = mix(color3, color4, (h - 0.66) * 3.0);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Add shimmer
|
|
105
|
+
float shimmer = fbm(p * 6.0 + vec2(time * 0.3, time * 0.2));
|
|
106
|
+
shimmer = pow(shimmer, 2.0) * 0.3;
|
|
107
|
+
color += vec3(shimmer);
|
|
108
|
+
|
|
109
|
+
// Depth fade
|
|
110
|
+
color = mix(color, color1 * 0.8, (1.0 - uv.y) * 0.4);
|
|
111
|
+
|
|
112
|
+
// Subtle vignette
|
|
113
|
+
float dist = length(uv - 0.5);
|
|
114
|
+
color *= 1.0 - dist * 0.3;
|
|
115
|
+
|
|
116
|
+
gl_FragColor = vec4(color, 1.0);
|
|
117
|
+
}
|
|
118
|
+
`;
|
|
119
|
+
|
|
120
|
+
const createShader = (gl: WebGLRenderingContext, type: number, source: string) => {
|
|
121
|
+
const shader = gl.createShader(type);
|
|
122
|
+
if (!shader) return null;
|
|
123
|
+
|
|
124
|
+
gl.shaderSource(shader, source);
|
|
125
|
+
gl.compileShader(shader);
|
|
126
|
+
|
|
127
|
+
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
|
|
128
|
+
console.error('Shader compile error:', gl.getShaderInfoLog(shader));
|
|
129
|
+
gl.deleteShader(shader);
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return shader;
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
const vertexShader = createShader(gl, gl.VERTEX_SHADER, vertexShaderSource);
|
|
137
|
+
const fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fragmentShaderSource);
|
|
138
|
+
|
|
139
|
+
if (!vertexShader || !fragmentShader) return;
|
|
140
|
+
|
|
141
|
+
const program = gl.createProgram();
|
|
142
|
+
if (!program) return;
|
|
143
|
+
|
|
144
|
+
gl.attachShader(program, vertexShader);
|
|
145
|
+
gl.attachShader(program, fragmentShader);
|
|
146
|
+
gl.linkProgram(program);
|
|
147
|
+
|
|
148
|
+
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
|
|
149
|
+
console.error('Program link error:', gl.getProgramInfoLog(program));
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const positionBuffer = gl.createBuffer();
|
|
154
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
|
|
155
|
+
gl.bufferData(
|
|
156
|
+
gl.ARRAY_BUFFER,
|
|
157
|
+
new Float32Array([
|
|
158
|
+
-1, -1,
|
|
159
|
+
1, -1,
|
|
160
|
+
-1, 1,
|
|
161
|
+
1, 1,
|
|
162
|
+
]),
|
|
163
|
+
gl.STATIC_DRAW
|
|
164
|
+
);
|
|
165
|
+
|
|
166
|
+
const positionLocation = gl.getAttribLocation(program, 'position');
|
|
167
|
+
const resolutionLocation = gl.getUniformLocation(program, 'resolution');
|
|
168
|
+
const timeLocation = gl.getUniformLocation(program, 'time');
|
|
169
|
+
|
|
170
|
+
let startTime = Date.now();
|
|
171
|
+
|
|
172
|
+
const render = () => {
|
|
173
|
+
if (!gl || !canvas) return;
|
|
174
|
+
|
|
175
|
+
gl.clearColor(0, 0, 0, 1);
|
|
176
|
+
gl.clear(gl.COLOR_BUFFER_BIT);
|
|
177
|
+
|
|
178
|
+
gl.useProgram(program);
|
|
179
|
+
|
|
180
|
+
gl.enableVertexAttribArray(positionLocation);
|
|
181
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
|
|
182
|
+
gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
|
|
183
|
+
|
|
184
|
+
gl.uniform2f(resolutionLocation, canvas.width, canvas.height);
|
|
185
|
+
gl.uniform1f(timeLocation, (Date.now() - startTime) / 1000);
|
|
186
|
+
|
|
187
|
+
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
|
|
188
|
+
|
|
189
|
+
animationRef.current = requestAnimationFrame(render);
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
render();
|
|
193
|
+
|
|
194
|
+
return () => {
|
|
195
|
+
window.removeEventListener('resize', resizeCanvas);
|
|
196
|
+
if (animationRef.current) {
|
|
197
|
+
cancelAnimationFrame(animationRef.current);
|
|
198
|
+
}
|
|
199
|
+
gl.deleteProgram(program);
|
|
200
|
+
gl.deleteShader(vertexShader);
|
|
201
|
+
gl.deleteShader(fragmentShader);
|
|
202
|
+
gl.deleteBuffer(positionBuffer);
|
|
203
|
+
};
|
|
204
|
+
}, []);
|
|
205
|
+
|
|
206
|
+
return (
|
|
207
|
+
<div ref={containerRef} className="absolute inset-0 w-full h-full bg-black overflow-hidden">
|
|
208
|
+
<canvas
|
|
209
|
+
ref={canvasRef}
|
|
210
|
+
className="absolute top-0 left-0 w-full h-full"
|
|
211
|
+
/>
|
|
212
|
+
</div>
|
|
213
|
+
);
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
export default FrothyGalaxyShader;
|