diff --git a/frontend/src/assets/shader.glsl b/frontend/src/assets/shader.glsl index cb66698..ec5e54b 100644 --- a/frontend/src/assets/shader.glsl +++ b/frontend/src/assets/shader.glsl @@ -1,16 +1,12 @@ #version 300 es -precision lowp float; -uniform vec3 iResolution; // viewport resolution (in pixels) -uniform float iTime; // shader playback time (in seconds) +precision mediump float; +uniform vec3 iResolution; +uniform float iTime; uniform sampler2D iChannel1; out vec4 FragColor; -const float pi = 3.1415927f; - -float sdSphere(vec3 p, float s) { - return length(p) - s; -} +const float PI = 3.1415927f; float sdTorus(vec3 p, vec2 t) { vec2 q = vec2(length(p.xz) - t.x, p.y); @@ -18,66 +14,87 @@ float sdTorus(vec3 p, vec2 t) { } void mainImage(out vec4 fragColor, in vec2 fragCoord) { - vec2 pp = fragCoord.xy / iResolution.xy; - pp = -1.0f + 2.0f * pp; - pp.x *= iResolution.x / iResolution.y; + 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)); //camera position + 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, -0.1f)), front)); + 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 * pp.x + up * pp.y); // rect vector + vec3 rd = normalize(front * 1.5f + left * uv.x + up * uv.y); - vec3 bh = vec3(0.0f, 0.0f, 0.0f); - float bhr = 0.1f; - float bhmass = 5.0f; - bhmass *= 0.001f; // premul G + // 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; - float dt = 0.02f; - + 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); - for(float t = 0.0f; t < 1.0f; t += 0.005f) { - p += pv * dt * noncaptured; + // 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; - // gravity + p += pv * (dt * noncaptured); + + // gravity: compute bhv and reuse vec3 bhv = bh - p; - float r = dot(bhv, bhv); - pv += normalize(bhv) * ((bhmass) / r); + 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)); - noncaptured = smoothstep(0.0f, 0.666f, sdSphere(p - bh, bhr)); + // capture factor (reuse distance) + float dist = sqrt(r2); + noncaptured = smoothstep(0.0f, 0.666f, dist - bhr); - // Texture for the accretion disc + // accretion disc texture float dr = length(bhv.xz); - float da = atan(bhv.x, bhv.z); - vec2 ra = vec2(dr, da * (0.01f + (dr - bhr) * 0.002f) + 2.0f * pi + iTime * 0.005f); - ra *= vec2(10.0f, 20.0f); + 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); - vec3 dcol = mix(c2, c1, pow(length(bhv) - bhr, 2.0f)) * max(0.0f, texture(iChannel1, ra * vec2(0.1f, 0.5f)).r + 0.05f) * (4.0f / ((0.001f + (length(bhv) - bhr) * 50.0f))); + // color mix: avoid pow by squaring + float sq = (dr - bhr); + sq = sq * sq; + vec3 dcol = mix(c2, c1, sq) * radial * falloff; - col += max(vec3(0.0f), dcol * smoothstep(0.0f, 1.0f, -sdTorus((p * vec3(1.0f, 25.0f, 1.0f)) - bh, vec2(0.8f, 0.99f))) * noncaptured); + // 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 += vec3(1.0f, 0.9f, 0.85f) * (1.0f / vec3(dot(bhv, bhv))) * 0.0033f * noncaptured; + 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() { - // gl_FragCoord.xy is pixel coordinates (1..width, 1..height) mainImage(FragColor, gl_FragCoord.xy); } \ No newline at end of file