lightswind
Version:
A collection of beautifully crafted React Components, Blocks & Templates for Modern Developers. Create stunning web applications effortlessly by using our 160+ professional and animated react components.
301 lines (276 loc) • 9.69 kB
JavaScript
"use strict";
// PlasmaGlobe.tsx
"use client";
Object.defineProperty(exports, "__esModule", { value: true });
exports.default = PlasmaGlobe;
const jsx_runtime_1 = require("react/jsx-runtime");
const react_1 = require("react");
const ogl_1 = require("ogl");
const VERTEX_SHADER = `#version 300 es
in vec2 position;
void main() {
gl_Position = vec4(position, 0.0, 1.0);
}
`;
/*
Adapted & simplified plasma globe fragment shader (from Shadertoy-style code).
Replaces texture-based noise with small procedural noise functions so it
runs without external textures. Uses uniforms:
- uTime (float)
- uResolution (vec2)
- uMouse (vec2)
- uSpeed (float)
- uIntensity (float)
NOTE: keep an eye on precision and performance on low-end GPUs.
*/
const FRAGMENT_SHADER = `#version 300 es
precision highp float;
out vec4 fragColor;
uniform float uTime;
uniform vec2 uResolution;
uniform vec2 uMouse;
uniform float uSpeed;
uniform float uIntensity;
#define NUM_RAYS 13.0
#define VOLUMETRIC_STEPS 19
#define MAX_ITER 35
#define FAR 6.0
// small 2x2 rotation matrix
mat2 mm2(float a){
float c = cos(a), s = sin(a);
return mat2(c, -s, s, c);
}
// simple hash-based random
float hash1(float n){ return fract(sin(n)*43758.5453); }
float hash2(vec2 p){ return fract(sin(dot(p, vec2(127.1,311.7)))*43758.5453); }
// value noise from vec3 -> float (cheap, not high quality but ok)
float noise3(vec3 p){
// grid cell
vec3 ip = floor(p);
vec3 fp = fract(p);
fp = fp*fp*(3.0-2.0*fp);
float n000 = hash2(ip.xy + ip.z*7.0);
float n100 = hash2(ip.xy + vec2(1.0,0.0) + ip.z*7.0);
float n010 = hash2(ip.xy + vec2(0.0,1.0) + ip.z*7.0);
float n110 = hash2(ip.xy + vec2(1.0,1.0) + ip.z*7.0);
float nx0 = mix(n000, n100, fp.x);
float nx1 = mix(n010, n110, fp.x);
float nxy = mix(nx0, nx1, fp.y);
// incorporate z as small modulation using hash
float nz = mix(nxy, hash1(ip.z + 1.0), fp.z);
return nz;
}
// light-weight fractal noise (based on noise3)
float flow(vec3 p, float t){
float rz = 0.0;
vec3 bp = p;
float z = 2.0;
// a few octaves
for (int i = 1; i < 5; i++){
p += t * 0.1;
rz += (sin(noise3(p + t*0.8) * 6.0) * 0.5 + 0.5) / z;
p = mix(bp, p, 0.6);
z *= 2.0;
p *= 2.01;
p *= mat3(
0.00, 0.80, 0.60,
-0.80, 0.36, -0.48,
-0.60, -0.48, 0.64
);
}
return rz;
}
// helper used to create wavy variations (low-frequency)
float sins(float x, float t){
float rz = 0.0;
float z = 2.0;
for (int i = 0; i < 3; i++){
rz += abs(fract(x * 1.4) - 0.5) / z;
x *= 1.3;
z *= 1.15;
x -= t * 0.65 * z;
}
return rz;
}
float segm(vec3 p, vec3 a, vec3 b){
vec3 pa = p - a;
vec3 ba = b - a;
float h = clamp(dot(pa, ba) / dot(ba, ba), 0.0, 1.0);
return length(pa - ba * h) * 0.5;
}
vec3 path(float i, float d, float t){
// produce a pseudo-random endpoint on unit sphere influenced by i and d
float sns2 = sins(d + i * 0.5, t) * 0.22;
float sns = sins(d + i * 0.6, t) * 0.21;
float a1 = (hash1(i * 10.569) - 0.5) * 6.2 + sns2;
float a2 = (hash1(i * 4.732) - 0.5) * 6.2 + sns;
vec3 en = vec3(0.0, 0.0, 1.0);
en.xz *= mat2(cos(a1), -sin(a1), sin(a1), cos(a1));
en.xy *= mat2(cos(a2), -sin(a2), sin(a2), cos(a2));
return en;
}
vec2 map(vec3 p, float i, float t){
float lp = length(p);
vec3 bg = vec3(0.0);
vec3 en = path(i, lp, t);
float ins = smoothstep(0.11, 0.46, lp);
float outs = 0.15 + smoothstep(0.0, 0.15, abs(lp - 1.0));
p *= ins * outs;
float id = ins * outs;
float rz = segm(p, bg, en) - 0.011;
return vec2(rz, id);
}
// sphere-ray intersection helper
vec2 iSphere2(vec3 ro, vec3 rd){
vec3 oc = ro;
float b = dot(oc, rd);
float c = dot(oc, oc) - 1.0;
float h = b*b - c;
if (h < 0.0) return vec2(-1.0);
return vec2((-b - sqrt(h)), (-b + sqrt(h)));
}
// volumetric march (accumulates light along a ray)
vec3 vmarch(vec3 ro, vec3 rd, float j, vec3 orig, float t){
vec3 p = ro;
vec3 sum = vec3(0.0);
for (int i = 0; i < VOLUMETRIC_STEPS; i++){
vec2 r = map(p, j, t);
p += rd * 0.03;
float lp = length(p);
// create color base per-step
vec3 col = sin(vec3(1.05, 2.5, 1.52) * 3.94 + r.y) * 0.85 + 0.4;
col *= smoothstep(0.0, 0.015, -r.x);
col *= smoothstep(0.04, 0.2, abs(lp - 1.1));
col *= smoothstep(0.1, 0.34, lp);
// noise modulation
float n = noise3(vec3(lp * 2.0 + j * 13.0 + t * 5.0));
// attenuate with distance from origin and add
float denom = max(0.0001, log(max(0.0001, distance(p, orig) - 2.0)) + 0.75);
sum += abs(col) * 5.0 * (1.2 - n * 1.1) / denom;
}
return sum;
}
// ray-marching distance estimator to sphere-like structures
float march(vec3 ro, vec3 rd, float startf, float maxd, float j, float t){
float precis = 0.001;
float h = 0.5;
float d = startf;
for (int i = 0; i < MAX_ITER; i++){
if (abs(h) < precis || d > maxd) break;
d += h * 1.2;
float res = map(ro + rd * d, j, t).x;
h = res;
}
return d;
}
void main(){
// Normalized coords (-0.5..0.5)
vec2 uv = (gl_FragCoord.xy / uResolution.xy) - 0.5;
uv.x *= uResolution.x / uResolution.y;
vec2 um = (uMouse.xy / uResolution.xy) - 0.5;
// camera setup
vec3 ro = vec3(0.0, 0.0, 5.0);
vec3 rd = normalize(vec3(uv * 0.7, -1.5));
mat2 mx = mm2(uTime * 0.4 + um.x * 6.0);
mat2 my = mm2(uTime * 0.3 + um.y * 6.0);
ro.xz *= mx; rd.xz *= mx;
ro.xy *= my; rd.xy *= my;
vec3 bro = ro;
vec3 brd = rd;
vec3 col = vec3(0.0125, 0.0, 0.025);
// multiple rays to create many filaments
for (float j = 1.0; j < NUM_RAYS + 1.0; j++){
ro = bro;
rd = brd;
mat2 mm = mm2((uTime * 0.1 + ((j + 1.0) * 5.1)) * j * 0.25);
ro.xy *= mm; rd.xy *= mm;
ro.xz *= mm; rd.xz *= mm;
float rz = march(ro, rd, 2.5, FAR, j, uTime);
if (rz >= FAR) continue;
vec3 pos = ro + rz * rd;
col = max(col, vmarch(pos, rd, j, bro, uTime));
}
ro = bro;
rd = brd;
vec2 sph = iSphere2(ro, rd);
if (sph.x > 0.0){
vec3 pos = ro + rd * sph.x;
vec3 pos2 = ro + rd * sph.y;
vec3 rf = reflect(rd, normalize(pos));
vec3 rf2 = reflect(rd, normalize(pos2));
float nz = (-log(abs(flow(rf * 1.2, uTime) - 0.01) + 0.00001));
float nz2 = (-log(abs(flow(rf2 * 1.2, -uTime) - 0.01) + 0.00001));
col += (0.1 * nz * nz * vec3(0.12, 0.12, 0.5) + 0.05 * nz2 * nz2 * vec3(0.55, 0.2, 0.55)) * 0.8;
}
// final tone mapping & intensity
col *= (1.0 + uIntensity * 0.6);
col = pow(clamp(col, 0.0, 10.0), vec3(1.5));
fragColor = vec4(col * 1.3, 1.0);
}
`;
function PlasmaGlobe({ speed = 1.0, intensity = 1.0, }) {
const containerRef = (0, react_1.useRef)(null);
const mouseRef = (0, react_1.useRef)({ x: 0, y: 0 });
(0, react_1.useEffect)(() => {
const container = containerRef.current;
if (!container)
return;
// create renderer
const renderer = new ogl_1.Renderer({ alpha: true, antialias: true });
const gl = renderer.gl;
gl.clearColor(0, 0, 0, 0);
gl.enable(gl.BLEND);
gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
// geometry
const geometry = new ogl_1.Triangle(gl);
// program
const program = new ogl_1.Program(gl, {
vertex: VERTEX_SHADER,
fragment: FRAGMENT_SHADER,
uniforms: {
uTime: { value: 0 },
uResolution: { value: [container.offsetWidth, container.offsetHeight] },
uMouse: { value: [0, 0] },
uSpeed: { value: speed },
uIntensity: { value: intensity },
},
});
const mesh = new ogl_1.Mesh(gl, { geometry, program });
container.appendChild(gl.canvas);
// resize
const resize = () => {
const width = container.offsetWidth;
const height = container.offsetHeight;
renderer.setSize(width, height);
program.uniforms.uResolution.value = [width, height];
};
window.addEventListener("resize", resize);
resize();
// mouse smoothing
const onMouse = (e) => {
mouseRef.current.x += (e.clientX - mouseRef.current.x) * 0.08;
mouseRef.current.y += (e.clientY - mouseRef.current.y) * 0.08;
};
window.addEventListener("mousemove", onMouse);
let rafId = 0;
const loop = (t) => {
rafId = requestAnimationFrame(loop);
// uTime passed in seconds, multiplied by speed
program.uniforms.uTime.value = (t * 0.001) * speed;
program.uniforms.uMouse.value = [mouseRef.current.x, mouseRef.current.y];
program.uniforms.uIntensity.value = intensity;
renderer.render({ scene: mesh });
};
rafId = requestAnimationFrame(loop);
return () => {
cancelAnimationFrame(rafId);
window.removeEventListener("resize", resize);
window.removeEventListener("mousemove", onMouse);
if (gl.canvas.parentNode === container)
container.removeChild(gl.canvas);
gl.getExtension("WEBGL_lose_context")?.loseContext();
};
}, [speed, intensity]);
return ((0, jsx_runtime_1.jsx)("div", { ref: containerRef, className: "w-full h-full absolute inset-0 pointer-events-none \r\n !rounded-xl !bg-transparent", style: { transform: "scale(0.50)", borderRadius: '50px' } }));
}
//# sourceMappingURL=plasma-globe.js.map