-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathshader.js
More file actions
154 lines (126 loc) · 5.36 KB
/
shader.js
File metadata and controls
154 lines (126 loc) · 5.36 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
const meshVertexShader = `
attribute vec2 position;
varying vec2 vUv;
void main() {
vUv = position * 0.5 + 0.5;
gl_Position = vec4(position, 0.0, 1.0);
}
`;
const meshFragmentShader = `
precision lowp float;
varying vec2 vUv;
uniform float u_time;
uniform vec3 u_colorBg;
uniform vec3 u_colorAccent;
uniform vec2 u_resolution;
// Simple noise for a "data-stream" feel
float hash(vec2 p) {
return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453123);
}
void main() {
vec2 uv = vUv;
float t = u_time * 0.2;
// 1. Technical Grid with varying thickness
vec2 aspect = vec2(u_resolution.x / u_resolution.y, 1.0);
vec2 gridUv = uv * aspect * 40.0;
vec2 grid = abs(fract(gridUv - 0.5) - 0.5);
float line = smoothstep(0.04, 0.0, min(grid.x, grid.y));
// Secondary subtle grid
vec2 gridUv2 = uv * aspect * 8.0;
vec2 grid2 = abs(fract(gridUv2 - 0.5) - 0.5);
float line2 = smoothstep(0.02, 0.0, min(grid2.x, grid2.y));
// 2. Data "Nodes" (The Hotspots)
vec2 p1 = vec2(0.5 + 0.3 * sin(t * 1.5), 0.5 + 0.2 * cos(t * 1.8));
vec2 p2 = vec2(0.5 + 0.3 * cos(t * 1.2), 0.5 + 0.3 * sin(t * 0.9));
float d1 = 0.015 / length(uv - p1);
float d2 = 0.015 / length(uv - p2);
// 3. Digital Grain / Dithering (Prevents color banding)
float noise = hash(uv + t) * 0.03;
// 4. Composition
vec3 color = u_colorBg;
// Layering
color = mix(color, u_colorAccent, line * 0.15); // Fine grid
color = mix(color, u_colorAccent, line2 * 0.08); // Coarse grid
color += u_colorAccent * (d1 + d2) * 0.6; // Dynamic nodes
color += noise; // System grain
gl_FragColor = vec4(color, 1.0);
}
`;
function initLightShader() {
const canvas = document.getElementById('bg-canvas') || document.createElement('canvas');
if (!canvas.id) {
canvas.id = 'bg-canvas';
document.body.prepend(canvas);
}
const gl = canvas.getContext('webgl', { antialias: false, depth: false });
if (!gl) return;
// --- SHADER SETUP ---
function createShader(gl, type, source) {
const s = gl.createShader(type);
gl.shaderSource(s, source);
gl.compileShader(s);
return s;
}
const program = gl.createProgram();
gl.attachShader(program, createShader(gl, gl.VERTEX_SHADER, meshVertexShader));
gl.attachShader(program, createShader(gl, gl.FRAGMENT_SHADER, meshFragmentShader));
gl.linkProgram(program);
gl.useProgram(program);
const vertices = new Float32Array([-1, -1, 1, -1, -1, 1, 1, 1]);
gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer());
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
const pos = gl.getAttribLocation(program, "position");
gl.enableVertexAttribArray(pos);
gl.vertexAttribPointer(pos, 2, gl.FLOAT, false, 0, 0);
const uniforms = {
time: gl.getUniformLocation(program, "u_time"),
bg: gl.getUniformLocation(program, "u_colorBg"),
acc: gl.getUniformLocation(program, "u_colorAccent"),
res: gl.getUniformLocation(program, "u_resolution")
};
// --- CACHED VALUES (Optimization) ---
// We only update these when the theme changes, not every frame.
let cachedBg = [0, 0, 0];
let cachedAcc = [0, 1, 0];
function parseColor(color) {
if (color.startsWith('rgb')) {
const vals = color.match(/\d+/g).map(Number);
return [vals[0] / 255, vals[1] / 255, vals[2] / 255];
}
const hex = color.replace('#', '');
const b = parseInt(hex, 16);
return [((b >> 16) & 255) / 255, ((b >> 8) & 255) / 255, (b & 255) / 255];
}
function updateColors() {
const style = getComputedStyle(document.documentElement);
cachedBg = parseColor(style.getPropertyValue('--bg').trim() || "#000000");
cachedAcc = parseColor(style.getPropertyValue('--accent').trim() || "#22c55e");
}
// Update colors initially and whenever the theme/mode changes
updateColors();
const observer = new MutationObserver(updateColors);
observer.observe(document.documentElement, { attributes: true, attributeFilter: ['data-theme', 'data-mode'] });
function render(time) {
if (document.documentElement.getAttribute('data-effects') === 'off') {
requestAnimationFrame(render);
return;
}
// Only resize if needed
const quality = window.innerWidth < 950 ? 0.5 : 1.0;
const displayWidth = Math.floor(window.innerWidth * quality);
const displayHeight = Math.floor(window.innerHeight * quality);
if (canvas.width !== displayWidth || canvas.height !== displayHeight) {
canvas.width = displayWidth;
canvas.height = displayHeight;
gl.viewport(0, 0, canvas.width, canvas.height);
}
gl.uniform1f(uniforms.time, time * 0.001);
gl.uniform2f(uniforms.res, canvas.width, canvas.height);
gl.uniform3fv(uniforms.bg, cachedBg);
gl.uniform3fv(uniforms.acc, cachedAcc);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
}
document.addEventListener('DOMContentLoaded', initLightShader);