Compare commits
7 Commits
54bf5b38c2
...
eca63b368d
| Author | SHA1 | Date | |
|---|---|---|---|
| eca63b368d | |||
| a6a7e836cc | |||
| 8706d510db | |||
| 6128804352 | |||
| f58ff49c46 | |||
| 12592e3726 | |||
| d2a8edb004 |
BIN
frontend/public/assets/ichannel1.png
Normal file
BIN
frontend/public/assets/ichannel1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
|
||||
|
Before Width: | Height: | Size: 1.5 KiB |
@ -128,7 +128,7 @@ export function GameList() {
|
||||
};
|
||||
|
||||
const formHeaderStyles: React.CSSProperties = {
|
||||
background: "linear-gradient(135deg, var(--accent-color) 0%, #0a4f8c 100%)",
|
||||
background: "linear-gradient(135deg, var(--accent-color) 0%, var(--secondary-accent) 100%)",
|
||||
padding: "1.5rem 2rem",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
@ -195,7 +195,7 @@ export function GameList() {
|
||||
const submitButtonStyles: React.CSSProperties = {
|
||||
width: "100%",
|
||||
padding: "1rem",
|
||||
background: "linear-gradient(135deg, var(--accent-color) 0%, #0a4f8c 100%)",
|
||||
background: "linear-gradient(135deg, var(--accent-color) 0%, var(--secondary-accent) 100%)",
|
||||
border: "none",
|
||||
borderRadius: "12px",
|
||||
color: "white",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
100
frontend/src/assets/shader.glsl
Normal file
100
frontend/src/assets/shader.glsl
Normal file
@ -0,0 +1,100 @@
|
||||
#version 300 es
|
||||
precision mediump float;
|
||||
uniform vec3 iResolution;
|
||||
uniform float iTime;
|
||||
uniform sampler2D iChannel1;
|
||||
|
||||
out vec4 FragColor;
|
||||
|
||||
const float PI = 3.1415927f;
|
||||
|
||||
float sdTorus(vec3 p, vec2 t) {
|
||||
vec2 q = vec2(length(p.xz) - t.x, p.y);
|
||||
return length(q) - t.y;
|
||||
}
|
||||
|
||||
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
|
||||
vec2 uv = (fragCoord.xy / iResolution.xy) * 2.0f - 1.0f;
|
||||
uv.x *= iResolution.x / iResolution.y;
|
||||
|
||||
// camera
|
||||
vec3 lookAt = vec3(0.0f, -0.1f, 0.0f);
|
||||
float eyer = 2.0f;
|
||||
float eyea = 0.0f;
|
||||
float eyea2 = -0.24f * PI * 2.0f;
|
||||
vec3 ro = vec3(eyer * cos(eyea) * sin(eyea2), eyer * cos(eyea2), eyer * sin(eyea) * sin(eyea2));
|
||||
|
||||
vec3 front = normalize(lookAt - ro);
|
||||
vec3 left = normalize(cross(normalize(vec3(0.0f, 1.0f, -0.1f)), front));
|
||||
vec3 up = normalize(cross(front, left));
|
||||
vec3 rd = normalize(front * 1.5f + left * uv.x + up * uv.y);
|
||||
|
||||
// black hole
|
||||
const vec3 bh = vec3(0.0f);
|
||||
const float bhr = 0.1f;
|
||||
float bhmass = 5.0f * 0.001f; // premul G
|
||||
|
||||
// integration
|
||||
vec3 p = ro;
|
||||
vec3 pv = rd;
|
||||
const float dt = 0.02f;
|
||||
vec3 col = vec3(0.0f);
|
||||
float noncaptured = 1.0f;
|
||||
|
||||
vec3 c1 = vec3(0.5f, 0.46f, 0.4f);
|
||||
vec3 c2 = vec3(1.0f, 0.8f, 0.6f);
|
||||
|
||||
// fixed iteration count, early-out when captured
|
||||
const int MAX_STEPS = 175; // original ~200 (1 / 0.005)
|
||||
for(int i = 0; i < MAX_STEPS; ++i) {
|
||||
if(noncaptured <= 0.0001f)
|
||||
break;
|
||||
|
||||
p += pv * (dt * noncaptured);
|
||||
|
||||
// gravity: compute bhv and reuse
|
||||
vec3 bhv = bh - p;
|
||||
float r2 = dot(bhv, bhv) + 1e-6f; // prevent div0
|
||||
float invLen = inversesqrt(r2);
|
||||
// normalize(bhv) = bhv * invLen
|
||||
// acceleration magnitude = bhmass / r2
|
||||
// combine: accel = normalize(bhv) * (bhmass / r2)
|
||||
pv += bhv * (invLen * (bhmass / r2));
|
||||
|
||||
// capture factor (reuse distance)
|
||||
float dist = sqrt(r2);
|
||||
noncaptured = smoothstep(0.0f, 0.666f, dist - bhr);
|
||||
|
||||
// accretion disc texture
|
||||
float dr = length(bhv.xz);
|
||||
float da = atan(bhv.x, bhv.z); // keep same ordering as original
|
||||
// compact texture coordinate generation:
|
||||
float angleScale = 0.01f + (dr - bhr) * 0.002f;
|
||||
vec2 ra = vec2(dr * 10.0f, (da * angleScale + 2.0f * PI + iTime * 0.005f) * 20.0f);
|
||||
// sample lower frequency (reduce texture cost / cache pressure)
|
||||
float tx = texture(iChannel1, ra * vec2(0.1f, 0.5f)).r;
|
||||
float radial = max(0.0f, tx + 0.05f);
|
||||
float falloff = 4.0f / (0.001f + (dr - bhr) * 50.0f);
|
||||
|
||||
// color mix: avoid pow by squaring
|
||||
float sq = (dr - bhr);
|
||||
sq = sq * sq;
|
||||
vec3 dcol = mix(c2, c1, sq) * radial * falloff;
|
||||
|
||||
// torus SDF: scale p once
|
||||
vec3 ps = p * vec3(1.0f, 25.0f, 1.0f) - bh;
|
||||
float tor = sdTorus(ps, vec2(0.8f, 0.99f));
|
||||
float torMask = smoothstep(0.0f, 1.0f, -tor);
|
||||
|
||||
col += max(vec3(0.0f), dcol * torMask * noncaptured);
|
||||
|
||||
// simple brightening term (reuse r2)
|
||||
col += vec3(1.0f, 0.9f, 0.85f) * (1.0f / r2) * 0.0033f * noncaptured;
|
||||
}
|
||||
|
||||
fragColor = vec4(col, 1.0f);
|
||||
}
|
||||
|
||||
void main() {
|
||||
mainImage(FragColor, gl_FragCoord.xy);
|
||||
}
|
||||
@ -4,6 +4,7 @@
|
||||
--secondary-alt-bg: #191f2e;
|
||||
--tertiary-bg: #101320;
|
||||
--accent-color: #096dc0;
|
||||
--secondary-accent: #0a4f8c;
|
||||
--text-color: #ffffff;
|
||||
--text-muted: #a0a0a0;
|
||||
--border-color: #2a3045;
|
||||
@ -101,6 +102,8 @@ ul {
|
||||
--tertiary-bg: rgba(40, 40, 40, 0.8);
|
||||
--border-color: rgba(255, 255, 255, 0.15);
|
||||
--text-color: #ffffff;
|
||||
--accent-color: #121212;
|
||||
--secondary-accent: #212121;
|
||||
}
|
||||
|
||||
.shader-theme body {
|
||||
|
||||
@ -1,93 +0,0 @@
|
||||
export function generateRandomRGBABytes(
|
||||
width: number,
|
||||
height: number
|
||||
): Uint8Array {
|
||||
if (width <= 0 || height <= 0) {
|
||||
throw new Error("width and height must be positive");
|
||||
}
|
||||
const len = width * height * 4;
|
||||
const out = new Uint8Array(len);
|
||||
|
||||
// Maximum bytes per getRandomValues call (per spec / browsers)
|
||||
const MAX_GETRANDOM_BYTES = 65536;
|
||||
|
||||
if (typeof crypto !== "undefined" && "getRandomValues" in crypto) {
|
||||
// Fill in chunks of up to MAX_GETRANDOM_BYTES
|
||||
let offset = 0;
|
||||
while (offset < len) {
|
||||
const chunkSize = Math.min(MAX_GETRANDOM_BYTES, len - offset);
|
||||
// Subarray view for the current chunk
|
||||
const chunkView = out.subarray(offset, offset + chunkSize);
|
||||
crypto.getRandomValues(chunkView);
|
||||
offset += chunkSize;
|
||||
}
|
||||
// Ensure alpha channel is fully opaque (255)
|
||||
for (let i = 3; i < len; i += 4) out[i] = 255;
|
||||
} else {
|
||||
// Fallback to Math.random for all bytes
|
||||
for (let i = 0; i < len; i++) {
|
||||
if ((i + 1) % 4 === 0) out[i] = 255;
|
||||
else out[i] = Math.floor(Math.random() * 256);
|
||||
}
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a WebGL texture from random data and bind it to texture unit 0,
|
||||
* then set the sampler2D uniform named `iChannel0` to use unit 0.
|
||||
*
|
||||
* gl: WebGLRenderingContext or WebGL2RenderingContext
|
||||
* program: compiled & linked shader program (must be in use or will be used)
|
||||
*/
|
||||
export function createAndBindRandomTextureToIChannel0(
|
||||
gl: WebGLRenderingContext | WebGL2RenderingContext,
|
||||
program: WebGLProgram,
|
||||
width: number,
|
||||
height: number
|
||||
): WebGLTexture {
|
||||
const bytes = generateRandomRGBABytes(width, height);
|
||||
|
||||
const tex = gl.createTexture();
|
||||
if (!tex) throw new Error("Failed to create texture");
|
||||
|
||||
// Activate texture unit 0 and bind the texture
|
||||
gl.activeTexture(gl.TEXTURE0);
|
||||
gl.bindTexture(gl.TEXTURE_2D, tex);
|
||||
|
||||
// Texture parameters suitable for data textures
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
||||
|
||||
// Upload pixel data as RGBA UNSIGNED_BYTE
|
||||
gl.pixelStorei(gl.UNPACK_ALIGNMENT, 1);
|
||||
gl.texImage2D(
|
||||
gl.TEXTURE_2D,
|
||||
0,
|
||||
gl.RGBA,
|
||||
width,
|
||||
height,
|
||||
0,
|
||||
gl.RGBA,
|
||||
gl.UNSIGNED_BYTE,
|
||||
bytes
|
||||
);
|
||||
|
||||
const location = gl.getUniformLocation(program, "iChannel0");
|
||||
if (location === null) {
|
||||
// Clean up binding and return but signal with thrown error if desired
|
||||
gl.bindTexture(gl.TEXTURE_2D, null);
|
||||
throw new Error("Uniform 'iChannel0' not found in program");
|
||||
}
|
||||
|
||||
// Set sampler uniform to texture unit 0
|
||||
gl.uniform1i(location, 0);
|
||||
|
||||
// Unbind the texture if you prefer (not strictly necessary)
|
||||
gl.bindTexture(gl.TEXTURE_2D, null);
|
||||
|
||||
return tex;
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user