diff options
| author | hachem <im@hachem.wtf> | 2026-08-24 18:28:39 +0200 |
|---|---|---|
| committer | hachem <im@hachem.wtf> | 2026-08-24 18:28:39 +0200 |
| commit | a34c49f103c8950a57687ea3d866c244d95b4414 (patch) | |
| tree | ff268f4a7f52769a252443d6f2822c9814e3bb1c /assets/shaders/geodesic.slang | |
| parent | 06398a7a176e123506de6e8851866a8bec0b3427 (diff) | |
[docs]: added a few comments cause i wont be able to read this in a week's time
Diffstat (limited to 'assets/shaders/geodesic.slang')
| -rw-r--r-- | assets/shaders/geodesic.slang | 71 |
1 files changed, 38 insertions, 33 deletions
diff --git a/assets/shaders/geodesic.slang b/assets/shaders/geodesic.slang index eb536c9..937674e 100644 --- a/assets/shaders/geodesic.slang +++ b/assets/shaders/geodesic.slang @@ -1,8 +1,8 @@ -// Ray-traced Schwarzschild black hole: back-traces one null geodesic per pixel +// ray-traced Schwarzschild black hole: back-traces one null geodesic per pixel // through curved spacetime, shading the accretion disk, the lensed HDRI -// background, and placed objects. Runs as a full-screen fragment pass. Uniforms +// background, and placed objects. runs as a full-screen fragment pass. uniforms // live in ConstantBuffers (UBOs) driven through the RHI; the HDRI is a cubemap -// sampler. See docs/physics.md for the maths. +// sampler. see docs/physics.md for the maths. struct VSInput { float2 position : POSITION; float2 texCoord : TEXCOORD0; }; struct VSOutput { float4 position : SV_Position; float2 texCoord : TEXCOORD0; }; @@ -61,7 +61,7 @@ ConstantBuffer<Simulation> sim; SamplerCube u_HDRIEnvironment; -// Schwarzschild radius of Sgr A* (metres). Geometric units with c = G = 1 are +// Schwarzschild radius of Sgr A* (metres). geometric units with c = G = 1 are // used throughout the geodesic integration; the black-hole mass is M = r_s / 2. static const float SagA_rs = 1.269e10; static const float D_LAMBDA = 1e7; @@ -69,7 +69,7 @@ static const float ESCAPE_R = 1e30; static const float R_ISCO = 3.0 * SagA_rs; // innermost stable circular orbit (6M) static const float R_PHOTON = 1.5 * SagA_rs; // photon sphere (3M) -static const float FLUX_PEAK = 0.0569; // peak of the r^-3(1-sqrt(r_in/r)) profile (at r/r_in ~ 1.36) +static const float FLUX_PEAK = 0.0569; // peak of the r^-3(1-sqrt(r_in/r)) profile (~r/r_in 1.36); worked it out once, now it's just the normaliser static const int DEFAULT_MAX_STEPS_MOVING = 12000; static const int DEFAULT_MAX_STEPS_STATIC = 8000; @@ -78,8 +78,8 @@ static const float DEFAULT_EARLY_EXIT_DISTANCE = 2e12; static const float MIN_STEP_SIZE = 1e6; static const float MAX_STEP_SIZE = 2e10; -// Display mapping for the (relative) Novikov-Thorne flux -> visible colour. -// The RADIAL PROFILE is physical; the absolute temperature scale is a display +// display mapping for the (relative) Novikov-Thorne flux -> visible colour. +// the RADIAL PROFILE is physical; the absolute temperature scale is a display // choice (a real Sgr A* disk is far cooler / redder than this). struct Hit @@ -91,6 +91,7 @@ struct Hit float hash(float3 p) { + // magic primes, don't ask — they just scramble bits into a clean 0..1 hash p = frac(p * float3(0.1031, 0.1030, 0.0973)); p += dot(p, p.yxz + 33.33); return frac((p.x + p.y) * p.z); @@ -129,8 +130,10 @@ float fbm(float3 x, int octaves) return v; } -// Planckian-locus blackbody colour (Tanner Helland approximation), T in Kelvin. -// Returns an sRGB-ish chromaticity normalised so the brightest channel ~ 1. +// blackbody colour along the Planckian locus, T in Kelvin. it's Tanner Helland's +// curve fit and the coefficients below are pure magic-number soup — idk exactly +// how they were derived, so like just trust them. returns an sRGB-ish +// chromaticity normalised so the brightest channel ~ 1. float3 Blackbody(float T) { T = clamp(T, 1000.0, 40000.0); @@ -149,11 +152,11 @@ float3 Blackbody(float T) return c; } -// Emission from the thin accretion disk at an equatorial crossing point P, seen -// along the (backward-traced) ray direction rayDir. Combines a Novikov-Thorne +// emission from the thin accretion disk at an equatorial crossing point P, seen +// along the (backward-traced) ray direction rayDir. combines a Novikov-Thorne // temperature profile with the full gravitational + Doppler redshift. // g = sqrt(1 - 3M/r) / (1 - beta . nhat) (verified: g -> sqrt(1/2) at ISCO) -// Brightness follows relativistic beaming (I_obs = g^4 I_emit); colour follows +// brightness follows relativistic beaming (I_obs = g^4 I_emit); colour follows // the redshifted blackbody at T_obs = g * T_emit. float3 DiskEmission(float3 P, float3 rayDir, out float outG, out float outTemit) { @@ -171,7 +174,7 @@ float3 DiskEmission(float3 P, float3 rayDir, out float outG, out float outTemit) float Tn = pow(flux / FLUX_PEAK, 0.25); // normalised temperature, peak ~ 1 float Temit = disk.u_temperature * Tn; - // Keplerian orbit (prograde about +Y). Locally-measured orbital speed for a + // Keplerian orbit (prograde about +Y). locally-measured orbital speed for a // Schwarzschild circular geodesic: v = sqrt( M / (r - 2M) ) = 0.5 c at ISCO. float3 rhat = normalize(float3(P.x, 0.0, P.z)); float3 phiHat = normalize(cross(float3(0.0, 1.0, 0.0), rhat)); @@ -184,17 +187,17 @@ float3 DiskEmission(float3 P, float3 rayDir, out float outG, out float outTemit) float Tobs = g * Temit; float3 colour = Blackbody(Tobs); - // Physical bolometric intensity is ~ T_emit^4 * g^4, an enormous dynamic - // range. The g^4 relativistic beaming (the physical asymmetry) is kept; the + // physical bolometric intensity is ~ T_emit^4 * g^4, an enormous dynamic + // range. the g^4 relativistic beaming (the physical asymmetry) is kept; the // radial falloff is display-compressed (Tn^2) so the colour gradient across // the disk stays visible instead of collapsing to a thin saturated ring. float bright = pow(Tn, 2.0) * pow(g, 4.0); - // Soft inner/outer edges (disks have no hard rim); also tames rim aliasing. + // soft inner/outer edges (disks have no hard rim); also tames rim aliasing. float edge = smoothstep(rin, rin * 1.12, rc) * (1.0 - smoothstep(rout * 0.88, rout, rc)); bright *= edge; - // Optional turbulence overlay (disk.u_turbulence = strength; 0 = smooth). + // optional turbulence overlay (disk.u_turbulence = strength; 0 = smooth). if (disk.u_turbulence > 0.0) { float ang = sim.u_time * 0.3 / sqrt(xr); @@ -304,13 +307,13 @@ void RK4Step(inout Ray ray, float dL) float CalculateAdaptiveStepSize(Ray ray, float baseStepSize) { - // Step proportional to the distance from the photon sphere: near-flat space + // step proportional to the distance from the photon sphere: near-flat space // far from the hole is crossed in a few huge steps, while the sharply curved - // region near the photon sphere is resolved with tiny ones. This keeps the + // region near the photon sphere is resolved with tiny ones. this keeps the // integration accurate near the hole regardless of how far the camera is. float step = 0.02 * max(ray.r - R_PHOTON, 0.0); - // Slow down when near the disk plane (within its radial extent) so the thin + // slow down when near the disk plane (within its radial extent) so the thin // slab is never stepped over -- otherwise grazing rays leak through it. float rc = length(float2(ray.x, ray.z)); if (rc < disk.u_outer_radius * 3.0 && abs(ray.y) < disk.u_thickness * 8.0) @@ -319,12 +322,14 @@ float CalculateAdaptiveStepSize(Ray ray, float baseStepSize) return clamp(step, MIN_STEP_SIZE, MAX_STEP_SIZE); } +// Narkowicz's ACES filmic tonemap. the constants are just The Numbers everyone +// copies — idk the derivation, but it squashes HDR radiance into a nice 0..1 curve. float3 ACESFilm(float3 x) { return clamp((x * (2.51 * x + 0.03)) / (x * (2.43 * x + 0.59) + 0.14), 0.0, 1.0); } -// A jet-ish false-colour ramp (blue -> cyan -> green -> yellow -> red) for the +// a jet-ish false-colour ramp (blue -> cyan -> green -> yellow -> red) for the // observable export channels. t is expected in [0, 1]. float3 Falsecolor(float t) { @@ -334,8 +339,8 @@ float3 Falsecolor(float t) 1.5 - abs(4.0 * t - 1.0)), 0.0, 1.0); } -// Trace one primary ray for the given image UV and return its linear, -// pre-tone-map radiance. Called once per sub-sample by fragmentMain. +// trace one primary ray for the given image UV and return its linear, +// pre-tone-map radiance. called once per sub-sample by fragmentMain. float3 TracePixel(float2 texCoord, out float outG, out float outTemit, out bool outHitDisk) { outG = 0.0; outTemit = 0.0; outHitDisk = false; @@ -371,9 +376,9 @@ float3 TracePixel(float2 texCoord, out float outG, out float outTemit, out bool RK4Step(ray, stepSize); float3 newPos = float3(ray.x, ray.y, ray.z); - // Opaque disk of small half-thickness H (a slab about the midplane y=0). - // The ray hits when it first crosses the midplane OR enters the slab - // while grazing along it. Real (nonzero) thickness stops the zero-height + // opaque disk of small half-thickness H (a slab about the midplane y=0). + // the ray hits when it first crosses the midplane OR enters the slab + // while grazing along it. real (nonzero) thickness stops the zero-height // edge-on "razor" from aliasing into a beam streaking across the frame. { float H = disk.u_thickness; @@ -397,13 +402,13 @@ float3 TracePixel(float2 texCoord, out float outG, out float outTemit, out bool if (i % objectCheckInterval == 0 && InterceptObject(ray, hit)) { hitObject = true; break; } - // Principled escape: once outbound in near-flat spacetime (r >> r_s) the + // principled escape: once outbound in near-flat spacetime (r >> r_s) the // ray direction no longer changes, so stop and read the background. if (ray.dr > 0.0 && ray.r > 50.0 * SagA_rs) break; } - // Escape direction + environment mip LOD from the ray's angular divergence. - // Computed UNCONDITIONALLY (before the branch) so ddx/ddy are valid; strongly + // escape direction + environment mip LOD from the ray's angular divergence. + // computed UNCONDITIONALLY (before the branch) so ddx/ddy are valid; strongly // lensed background rays diverge fast, so they read a blurred cubemap mip and // the starfield stops aliasing into a fan along the equatorial plane. float3 rayDir = normalize(float3(ray.x, ray.y, ray.z) - cam.u_cam_pos); @@ -438,14 +443,14 @@ float3 TracePixel(float2 texCoord, out float outG, out float outTemit, out bool [shader("fragment")] float4 fragmentMain(VSOutput input) : SV_Target { - // Moving frame: one sample for responsiveness. Settled frame: rotated-grid + // moving frame: one sample for responsiveness. settled frame: rotated-grid // 4x supersampling (the 4-rook pattern gives 4 distinct sub-pixel positions // on BOTH axes, far better on the near-horizontal lensed edges than an - // ordered grid). Radiance is averaged before tone-mapping; ddx/ddy give the + // ordered grid). radiance is averaged before tone-mapping; ddx/ddy give the // resolution-correct per-pixel UV footprint. - // Observable export channels: the chosen scalar quantity, either as a raw + // observable export channels: the chosen scalar quantity, either as a raw // float (u_raw_output: value in RGB, validity mask in A) or false-coloured. - // Channel 0 is the normal colour image. + // channel 0 is the normal colour image. if (cam.u_output_channel != 0) { float g, Temit; bool hitDisk; |
