shaderz 1.0.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/README.md +91 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +208 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +18 -0
- package/package.json +51 -0
- package/shaders/CosmicNebulaShader.tsx +265 -0
- package/shaders/GlassTwistShader.tsx +169 -0
- package/shaders/GlossyRibbonShader.tsx +164 -0
- package/shaders/GradientWavesShader.tsx +277 -0
- package/shaders/LiquidOrangeShader.tsx +246 -0
- package/shaders/NeonFluidShader.tsx +259 -0
- package/shaders/OceanWavesShader.tsx +229 -0
- package/shaders/PlasmaShader.tsx +174 -0
- package/shaders/SilkFlowShader.tsx +281 -0
- package/shaders/VideoBackground.tsx +37 -0
- package/videos/glossy-film.mp4 +0 -0
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { useEffect, useRef } from 'react';
|
|
3
|
+
|
|
4
|
+
function LiquidOrangeShader() {
|
|
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
|
+
|
|
10
|
+
useEffect(() => {
|
|
11
|
+
const canvas = canvasRef.current;
|
|
12
|
+
if (!canvas) return;
|
|
13
|
+
|
|
14
|
+
const gl = canvas.getContext('webgl');
|
|
15
|
+
glRef.current = gl;
|
|
16
|
+
|
|
17
|
+
if (!gl) {
|
|
18
|
+
console.error('WebGL not supported');
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const resizeCanvas = () => {
|
|
23
|
+
canvas.width = window.innerWidth;
|
|
24
|
+
canvas.height = window.innerHeight;
|
|
25
|
+
gl.viewport(0, 0, canvas.width, canvas.height);
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
resizeCanvas();
|
|
29
|
+
window.addEventListener('resize', resizeCanvas);
|
|
30
|
+
|
|
31
|
+
const vertexShaderSource = `
|
|
32
|
+
attribute vec2 position;
|
|
33
|
+
void main() {
|
|
34
|
+
gl_Position = vec4(position, 0.0, 1.0);
|
|
35
|
+
}
|
|
36
|
+
`;
|
|
37
|
+
|
|
38
|
+
const fragmentShaderSource = `
|
|
39
|
+
precision highp float;
|
|
40
|
+
uniform vec2 resolution;
|
|
41
|
+
uniform float time;
|
|
42
|
+
|
|
43
|
+
float random(vec2 st) {
|
|
44
|
+
return fract(sin(dot(st.xy, vec2(12.9898, 78.233))) * 43758.5453123);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
float noise(vec2 st) {
|
|
48
|
+
vec2 i = floor(st);
|
|
49
|
+
vec2 f = fract(st);
|
|
50
|
+
|
|
51
|
+
float a = random(i);
|
|
52
|
+
float b = random(i + vec2(1.0, 0.0));
|
|
53
|
+
float c = random(i + vec2(0.0, 1.0));
|
|
54
|
+
float d = random(i + vec2(1.0, 1.0));
|
|
55
|
+
|
|
56
|
+
vec2 u = f * f * (3.0 - 2.0 * f);
|
|
57
|
+
|
|
58
|
+
return mix(a, b, u.x) + (c - a) * u.y * (1.0 - u.x) + (d - b) * u.x * u.y;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
float fbm(vec2 st, int octaves) {
|
|
62
|
+
float value = 0.0;
|
|
63
|
+
float amplitude = 0.5;
|
|
64
|
+
float frequency = 1.0;
|
|
65
|
+
|
|
66
|
+
for(int i = 0; i < 8; i++) {
|
|
67
|
+
if(i >= octaves) break;
|
|
68
|
+
value += amplitude * noise(st * frequency);
|
|
69
|
+
frequency *= 1.8;
|
|
70
|
+
amplitude *= 0.55;
|
|
71
|
+
}
|
|
72
|
+
return value;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
vec2 curl(vec2 p, float time) {
|
|
76
|
+
float eps = 0.01;
|
|
77
|
+
float n1 = fbm(p + vec2(eps, 0.0) + time * 0.1, 6);
|
|
78
|
+
float n2 = fbm(p + vec2(-eps, 0.0) + time * 0.1, 6);
|
|
79
|
+
float n3 = fbm(p + vec2(0.0, eps) + time * 0.1, 6);
|
|
80
|
+
float n4 = fbm(p + vec2(0.0, -eps) + time * 0.1, 6);
|
|
81
|
+
|
|
82
|
+
return vec2(n3 - n4, n2 - n1) / (2.0 * eps);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
void main() {
|
|
86
|
+
vec2 uv = gl_FragCoord.xy / resolution.xy;
|
|
87
|
+
vec2 st = uv * 2.0 - 1.0;
|
|
88
|
+
st.x *= resolution.x / resolution.y;
|
|
89
|
+
|
|
90
|
+
vec2 flow1 = curl(st * 1.2 + vec2(time * 0.03), time);
|
|
91
|
+
vec2 flow2 = curl(st * 1.5 - vec2(time * 0.02, time * 0.025), time * 1.2);
|
|
92
|
+
|
|
93
|
+
vec2 totalFlow = flow1 * 0.6 + flow2 * 0.4;
|
|
94
|
+
|
|
95
|
+
vec2 distorted = st;
|
|
96
|
+
for(int i = 0; i < 2; i++) {
|
|
97
|
+
distorted += curl(distorted * 1.5 + totalFlow, time + float(i) * 0.5) * 0.1;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
float liquid = 0.0;
|
|
101
|
+
|
|
102
|
+
for(int i = 0; i < 5; i++) {
|
|
103
|
+
float fi = float(i);
|
|
104
|
+
vec2 offset = vec2(
|
|
105
|
+
sin(time * 0.15 + fi * 2.1) * 0.6,
|
|
106
|
+
cos(time * 0.12 + fi * 1.7) * 0.5
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
vec2 flowOffset = totalFlow * (0.3 + fi * 0.1);
|
|
110
|
+
vec2 pos = offset + flowOffset;
|
|
111
|
+
|
|
112
|
+
float dist = length(distorted - pos);
|
|
113
|
+
float size = 0.4 + sin(time * 0.2 + fi) * 0.15;
|
|
114
|
+
liquid += smoothstep(size, 0.0, dist) * (1.0 - fi * 0.15);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
float turbulence = fbm(distorted * 2.0 + totalFlow + time * 0.05, 4);
|
|
118
|
+
liquid += turbulence * 0.15;
|
|
119
|
+
|
|
120
|
+
float tendrils = 0.0;
|
|
121
|
+
for(int i = 0; i < 2; i++) {
|
|
122
|
+
float fi = float(i);
|
|
123
|
+
vec2 tendrilFlow = distorted * 2.5 + totalFlow * 1.5 + time * (0.08 + fi * 0.03);
|
|
124
|
+
float t = sin(tendrilFlow.x * 2.0 + cos(tendrilFlow.y * 1.5)) *
|
|
125
|
+
cos(tendrilFlow.y * 2.0 + sin(tendrilFlow.x * 1.5));
|
|
126
|
+
tendrils += t * (0.1 - fi * 0.03);
|
|
127
|
+
}
|
|
128
|
+
liquid += tendrils;
|
|
129
|
+
|
|
130
|
+
float edgeDetail = fbm(distorted * 5.0 + totalFlow * 2.0 + time * 0.1, 3);
|
|
131
|
+
liquid += edgeDetail * 0.08 * smoothstep(0.3, 0.8, liquid);
|
|
132
|
+
|
|
133
|
+
float grain1 = random(uv * 800.0 + time * 15.0) * 0.08;
|
|
134
|
+
float grain2 = random(uv * 400.0 + sin(time * 8.0)) * 0.05;
|
|
135
|
+
liquid += grain1 + grain2;
|
|
136
|
+
|
|
137
|
+
vec3 dark = vec3(0.8, 0.2, 0.0);
|
|
138
|
+
vec3 mid = vec3(1.0, 0.4, 0.05);
|
|
139
|
+
vec3 bright = vec3(1.0, 0.6, 0.15);
|
|
140
|
+
vec3 highlight = vec3(1.0, 0.75, 0.3);
|
|
141
|
+
|
|
142
|
+
float colorFlow = fbm(distorted * 2.0 + time * 0.06, 4);
|
|
143
|
+
vec3 color = mix(dark, mid, liquid * 0.6);
|
|
144
|
+
color = mix(color, bright, liquid * liquid * 0.8);
|
|
145
|
+
color = mix(color, highlight, pow(liquid, 3.0) * 0.5);
|
|
146
|
+
color = mix(color, color * 1.2, colorFlow * 0.3);
|
|
147
|
+
|
|
148
|
+
float alpha = liquid * 0.8;
|
|
149
|
+
alpha = smoothstep(0.15, 0.7, alpha);
|
|
150
|
+
alpha *= 1.0 - smoothstep(0.9, 1.2, liquid) * 0.5;
|
|
151
|
+
|
|
152
|
+
gl_FragColor = vec4(color * alpha, alpha);
|
|
153
|
+
}
|
|
154
|
+
`;
|
|
155
|
+
|
|
156
|
+
const createShader = (gl: WebGLRenderingContext, type: number, source: string) => {
|
|
157
|
+
const shader = gl.createShader(type);
|
|
158
|
+
if (!shader) return null;
|
|
159
|
+
|
|
160
|
+
gl.shaderSource(shader, source);
|
|
161
|
+
gl.compileShader(shader);
|
|
162
|
+
|
|
163
|
+
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
|
|
164
|
+
console.error('Shader compile error:', gl.getShaderInfoLog(shader));
|
|
165
|
+
gl.deleteShader(shader);
|
|
166
|
+
return null;
|
|
167
|
+
}
|
|
168
|
+
return shader;
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
const vertexShader = createShader(gl, gl.VERTEX_SHADER, vertexShaderSource);
|
|
172
|
+
const fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fragmentShaderSource);
|
|
173
|
+
|
|
174
|
+
if (!vertexShader || !fragmentShader) return;
|
|
175
|
+
|
|
176
|
+
const program = gl.createProgram();
|
|
177
|
+
if (!program) return;
|
|
178
|
+
|
|
179
|
+
gl.attachShader(program, vertexShader);
|
|
180
|
+
gl.attachShader(program, fragmentShader);
|
|
181
|
+
gl.linkProgram(program);
|
|
182
|
+
|
|
183
|
+
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
|
|
184
|
+
console.error('Program link error:', gl.getProgramInfoLog(program));
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
programRef.current = program;
|
|
189
|
+
|
|
190
|
+
const positionBuffer = gl.createBuffer();
|
|
191
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
|
|
192
|
+
const positions = new Float32Array([
|
|
193
|
+
-1, -1,
|
|
194
|
+
1, -1,
|
|
195
|
+
-1, 1,
|
|
196
|
+
1, 1
|
|
197
|
+
]);
|
|
198
|
+
gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW);
|
|
199
|
+
|
|
200
|
+
const positionLocation = gl.getAttribLocation(program, 'position');
|
|
201
|
+
const resolutionLocation = gl.getUniformLocation(program, 'resolution');
|
|
202
|
+
const timeLocation = gl.getUniformLocation(program, 'time');
|
|
203
|
+
|
|
204
|
+
gl.enable(gl.BLEND);
|
|
205
|
+
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
|
|
206
|
+
|
|
207
|
+
const render = (time: number) => {
|
|
208
|
+
time *= 0.001;
|
|
209
|
+
|
|
210
|
+
gl.clearColor(0, 0, 0, 0);
|
|
211
|
+
gl.clear(gl.COLOR_BUFFER_BIT);
|
|
212
|
+
gl.useProgram(program);
|
|
213
|
+
|
|
214
|
+
gl.enableVertexAttribArray(positionLocation);
|
|
215
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
|
|
216
|
+
gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
|
|
217
|
+
|
|
218
|
+
gl.uniform2f(resolutionLocation, canvas.width, canvas.height);
|
|
219
|
+
gl.uniform1f(timeLocation, time);
|
|
220
|
+
|
|
221
|
+
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
|
|
222
|
+
|
|
223
|
+
animationRef.current = requestAnimationFrame(render);
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
animationRef.current = requestAnimationFrame(render);
|
|
227
|
+
|
|
228
|
+
return () => {
|
|
229
|
+
window.removeEventListener('resize', resizeCanvas);
|
|
230
|
+
if (animationRef.current) {
|
|
231
|
+
cancelAnimationFrame(animationRef.current);
|
|
232
|
+
}
|
|
233
|
+
};
|
|
234
|
+
}, []);
|
|
235
|
+
|
|
236
|
+
return (
|
|
237
|
+
<div className="absolute inset-0 w-full h-full bg-black overflow-hidden">
|
|
238
|
+
<canvas
|
|
239
|
+
ref={canvasRef}
|
|
240
|
+
className="absolute top-0 left-0 w-full h-full pointer-events-none opacity-95"
|
|
241
|
+
/>
|
|
242
|
+
</div>
|
|
243
|
+
);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
export default LiquidOrangeShader;
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import React, { useRef, useEffect } from 'react';
|
|
3
|
+
|
|
4
|
+
const NeonFluidShader: 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
|
+
// Hash for noise
|
|
40
|
+
float hash(vec2 p) {
|
|
41
|
+
p = fract(p * vec2(123.34, 456.21));
|
|
42
|
+
p += dot(p, p + 45.32);
|
|
43
|
+
return fract(p.x * p.y);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Smooth noise
|
|
47
|
+
float noise(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 = hash(i);
|
|
53
|
+
float b = hash(i + vec2(1.0, 0.0));
|
|
54
|
+
float c = hash(i + vec2(0.0, 1.0));
|
|
55
|
+
float d = hash(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
|
+
// Fractal Brownian Motion
|
|
61
|
+
float fbm(vec2 p) {
|
|
62
|
+
float value = 0.0;
|
|
63
|
+
float amplitude = 0.5;
|
|
64
|
+
float frequency = 1.0;
|
|
65
|
+
|
|
66
|
+
for(int i = 0; i < 6; i++) {
|
|
67
|
+
value += amplitude * noise(p * frequency);
|
|
68
|
+
frequency *= 2.0;
|
|
69
|
+
amplitude *= 0.5;
|
|
70
|
+
}
|
|
71
|
+
return value;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Turbulent fbm for flames
|
|
75
|
+
float turbulence(vec2 p) {
|
|
76
|
+
float value = 0.0;
|
|
77
|
+
float amplitude = 1.0;
|
|
78
|
+
float frequency = 1.0;
|
|
79
|
+
|
|
80
|
+
for(int i = 0; i < 5; i++) {
|
|
81
|
+
value += amplitude * abs(noise(p * frequency) * 2.0 - 1.0);
|
|
82
|
+
frequency *= 2.0;
|
|
83
|
+
amplitude *= 0.5;
|
|
84
|
+
}
|
|
85
|
+
return value;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
void main() {
|
|
89
|
+
vec2 uv = gl_FragCoord.xy / resolution.xy;
|
|
90
|
+
vec2 p = (uv - 0.5) * 2.0;
|
|
91
|
+
p.x *= resolution.x / resolution.y;
|
|
92
|
+
|
|
93
|
+
float t = time * 0.3;
|
|
94
|
+
|
|
95
|
+
// Create upward flowing motion
|
|
96
|
+
vec2 flowDir = vec2(0.0, -1.0);
|
|
97
|
+
vec2 flowPos = p + flowDir * t;
|
|
98
|
+
|
|
99
|
+
// Add turbulent distortion
|
|
100
|
+
float turbulent = turbulence(flowPos * 1.5 + vec2(t * 0.2, 0.0));
|
|
101
|
+
float displacement = fbm(flowPos * 2.0 + vec2(t * 0.3, -t * 0.5)) * 2.0 - 1.0;
|
|
102
|
+
|
|
103
|
+
vec2 distorted = p;
|
|
104
|
+
distorted.x += displacement * 0.4;
|
|
105
|
+
distorted.y += turbulent * 0.3;
|
|
106
|
+
|
|
107
|
+
// Flame shape - stronger at bottom, thinner at top
|
|
108
|
+
float flameShape = 1.0 - abs(distorted.x) * (1.0 + distorted.y * 0.8);
|
|
109
|
+
flameShape = smoothstep(0.0, 0.8, flameShape);
|
|
110
|
+
|
|
111
|
+
// Multiple flame layers
|
|
112
|
+
float flame1 = fbm(distorted * 2.0 + vec2(t * 0.4, -t * 0.8));
|
|
113
|
+
float flame2 = fbm(distorted * 3.0 + vec2(-t * 0.3, -t * 0.6));
|
|
114
|
+
float flame3 = fbm(distorted * 4.0 + vec2(t * 0.5, -t * 1.0));
|
|
115
|
+
|
|
116
|
+
// Combine flames with different intensities
|
|
117
|
+
float flames = flame1 * 0.5 + flame2 * 0.3 + flame3 * 0.2;
|
|
118
|
+
flames = pow(flames, 1.5);
|
|
119
|
+
|
|
120
|
+
// Add turbulent wisps
|
|
121
|
+
float wisps = turbulence(distorted * 3.0 + vec2(t * 0.2, -t * 0.7));
|
|
122
|
+
wisps = pow(wisps, 2.0) * 0.3;
|
|
123
|
+
|
|
124
|
+
// Combine all elements
|
|
125
|
+
float intensity = flames + wisps;
|
|
126
|
+
intensity *= flameShape;
|
|
127
|
+
|
|
128
|
+
// Fire color gradient - from dark red to orange to yellow to white
|
|
129
|
+
vec3 color1 = vec3(0.1, 0.0, 0.0); // Dark red/black
|
|
130
|
+
vec3 color2 = vec3(0.8, 0.1, 0.0); // Deep red
|
|
131
|
+
vec3 color3 = vec3(1.0, 0.3, 0.0); // Red-orange
|
|
132
|
+
vec3 color4 = vec3(1.0, 0.6, 0.0); // Orange
|
|
133
|
+
vec3 color5 = vec3(1.0, 0.9, 0.2); // Yellow
|
|
134
|
+
vec3 color6 = vec3(1.0, 1.0, 0.8); // White-yellow
|
|
135
|
+
|
|
136
|
+
// Color mapping based on intensity
|
|
137
|
+
vec3 fireColor = color1;
|
|
138
|
+
fireColor = mix(fireColor, color2, smoothstep(0.0, 0.2, intensity));
|
|
139
|
+
fireColor = mix(fireColor, color3, smoothstep(0.2, 0.4, intensity));
|
|
140
|
+
fireColor = mix(fireColor, color4, smoothstep(0.4, 0.6, intensity));
|
|
141
|
+
fireColor = mix(fireColor, color5, smoothstep(0.6, 0.8, intensity));
|
|
142
|
+
fireColor = mix(fireColor, color6, smoothstep(0.8, 1.0, intensity));
|
|
143
|
+
|
|
144
|
+
// Add hot spots
|
|
145
|
+
float hotSpots = pow(flame3, 3.0) * flameShape;
|
|
146
|
+
fireColor += vec3(1.0, 1.0, 0.5) * hotSpots * 0.5;
|
|
147
|
+
|
|
148
|
+
// Enhance edges with bright orange
|
|
149
|
+
float edge = smoothstep(0.3, 0.5, intensity) * (1.0 - smoothstep(0.7, 0.9, intensity));
|
|
150
|
+
fireColor += vec3(1.0, 0.5, 0.0) * edge * 0.3;
|
|
151
|
+
|
|
152
|
+
// Darken based on position (darker at edges)
|
|
153
|
+
float vignette = 1.0 - length(vec2(distorted.x, distorted.y * 0.5)) * 0.3;
|
|
154
|
+
fireColor *= vignette;
|
|
155
|
+
|
|
156
|
+
// Boost overall brightness
|
|
157
|
+
fireColor *= 1.2;
|
|
158
|
+
|
|
159
|
+
gl_FragColor = vec4(fireColor, 1.0);
|
|
160
|
+
}
|
|
161
|
+
`;
|
|
162
|
+
|
|
163
|
+
const createShader = (gl: WebGLRenderingContext, type: number, source: string) => {
|
|
164
|
+
const shader = gl.createShader(type);
|
|
165
|
+
if (!shader) return null;
|
|
166
|
+
|
|
167
|
+
gl.shaderSource(shader, source);
|
|
168
|
+
gl.compileShader(shader);
|
|
169
|
+
|
|
170
|
+
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
|
|
171
|
+
console.error('Shader compile error:', gl.getShaderInfoLog(shader));
|
|
172
|
+
gl.deleteShader(shader);
|
|
173
|
+
return null;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return shader;
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
const vertexShader = createShader(gl, gl.VERTEX_SHADER, vertexShaderSource);
|
|
180
|
+
const fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fragmentShaderSource);
|
|
181
|
+
|
|
182
|
+
if (!vertexShader || !fragmentShader) return;
|
|
183
|
+
|
|
184
|
+
const program = gl.createProgram();
|
|
185
|
+
if (!program) return;
|
|
186
|
+
|
|
187
|
+
gl.attachShader(program, vertexShader);
|
|
188
|
+
gl.attachShader(program, fragmentShader);
|
|
189
|
+
gl.linkProgram(program);
|
|
190
|
+
|
|
191
|
+
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
|
|
192
|
+
console.error('Program link error:', gl.getProgramInfoLog(program));
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const positionBuffer = gl.createBuffer();
|
|
197
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
|
|
198
|
+
gl.bufferData(
|
|
199
|
+
gl.ARRAY_BUFFER,
|
|
200
|
+
new Float32Array([
|
|
201
|
+
-1, -1,
|
|
202
|
+
1, -1,
|
|
203
|
+
-1, 1,
|
|
204
|
+
1, 1,
|
|
205
|
+
]),
|
|
206
|
+
gl.STATIC_DRAW
|
|
207
|
+
);
|
|
208
|
+
|
|
209
|
+
const positionLocation = gl.getAttribLocation(program, 'position');
|
|
210
|
+
const resolutionLocation = gl.getUniformLocation(program, 'resolution');
|
|
211
|
+
const timeLocation = gl.getUniformLocation(program, 'time');
|
|
212
|
+
|
|
213
|
+
let startTime = Date.now();
|
|
214
|
+
|
|
215
|
+
const render = () => {
|
|
216
|
+
if (!gl || !canvas) return;
|
|
217
|
+
|
|
218
|
+
gl.clearColor(0, 0, 0, 1);
|
|
219
|
+
gl.clear(gl.COLOR_BUFFER_BIT);
|
|
220
|
+
|
|
221
|
+
gl.useProgram(program);
|
|
222
|
+
|
|
223
|
+
gl.enableVertexAttribArray(positionLocation);
|
|
224
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
|
|
225
|
+
gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
|
|
226
|
+
|
|
227
|
+
gl.uniform2f(resolutionLocation, canvas.width, canvas.height);
|
|
228
|
+
gl.uniform1f(timeLocation, (Date.now() - startTime) / 1000);
|
|
229
|
+
|
|
230
|
+
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
|
|
231
|
+
|
|
232
|
+
animationRef.current = requestAnimationFrame(render);
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
render();
|
|
236
|
+
|
|
237
|
+
return () => {
|
|
238
|
+
window.removeEventListener('resize', resizeCanvas);
|
|
239
|
+
if (animationRef.current) {
|
|
240
|
+
cancelAnimationFrame(animationRef.current);
|
|
241
|
+
}
|
|
242
|
+
gl.deleteProgram(program);
|
|
243
|
+
gl.deleteShader(vertexShader);
|
|
244
|
+
gl.deleteShader(fragmentShader);
|
|
245
|
+
gl.deleteBuffer(positionBuffer);
|
|
246
|
+
};
|
|
247
|
+
}, []);
|
|
248
|
+
|
|
249
|
+
return (
|
|
250
|
+
<div ref={containerRef} className="absolute inset-0 w-full h-full bg-black overflow-hidden">
|
|
251
|
+
<canvas
|
|
252
|
+
ref={canvasRef}
|
|
253
|
+
className="absolute top-0 left-0 w-full h-full"
|
|
254
|
+
/>
|
|
255
|
+
</div>
|
|
256
|
+
);
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
export default NeonFluidShader;
|