From 59b7c407550d63e997ed1e7bed286be5c332c285 Mon Sep 17 00:00:00 2001 From: hachem Date: Mon, 24 Aug 2026 03:57:40 +0200 Subject: [feat]: work on render path exporting --- assets/shaders/geodesic.slang | 64 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 57 insertions(+), 7 deletions(-) (limited to 'assets') diff --git a/assets/shaders/geodesic.slang b/assets/shaders/geodesic.slang index 7acbd78..eee62da 100644 --- a/assets/shaders/geodesic.slang +++ b/assets/shaders/geodesic.slang @@ -19,8 +19,19 @@ struct Camera float tanHalfFov; float aspect; bool moving; - int _pad4; + int outputChannel; // 0 = colour; 1 = redshift g; 2 = emission T; 3 = impact parameter + int rawOutput; // 0 = display (false-colour / tone-mapped); 1 = raw float value }; + +// 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) +{ + t = clamp(t, 0.0, 1.0); + return clamp(float3(1.5 - abs(4.0 * t - 3.0), + 1.5 - abs(4.0 * t - 2.0), + 1.5 - abs(4.0 * t - 1.0)), 0.0, 1.0); +} ConstantBuffer cam; struct Disk @@ -148,8 +159,9 @@ float3 Blackbody(float T) // 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 // the redshifted blackbody at T_obs = g * T_emit. -float3 DiskEmission(float3 P, float3 rayDir) +float3 DiskEmission(float3 P, float3 rayDir, out float outG, out float outTemit) { + outG = 0.0; outTemit = 0.0; float rc = length(float2(P.x, P.z)); // cylindrical radius (disk axis = +Y) float rin = max(disk.disk_r1, R_ISCO); float rout = disk.disk_r2; @@ -172,6 +184,7 @@ float3 DiskEmission(float3 P, float3 rayDir) float3 nhat = -normalize(rayDir); // photon direction toward the observer float g = sqrt(max(1.0 - 1.5 * SagA_rs / rc, 0.0)) / max(1.0 - dot(beta, nhat), 1e-3); + outG = g; outTemit = Temit; // surfaced for the observable export channels float Tobs = g * Temit; float3 colour = Blackbody(Tobs); @@ -317,8 +330,9 @@ float3 ACESFilm(float3 x) // 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) +float3 TracePixel(float2 texCoord, out float outG, out float outTemit, out bool outHitDisk) { + outG = 0.0; outTemit = 0.0; outHitDisk = false; float u = (2.0 * texCoord.x - 1.0) * cam.aspect * cam.tanHalfFov; float v = (1.0 - 2.0 * texCoord.y) * cam.tanHalfFov; float3 dir = normalize(u * cam.camRight - v * cam.camUp + cam.camForward); @@ -367,8 +381,9 @@ float3 TracePixel(float2 texCoord) float rc = length(float2(hitP.x, hitP.z)); if (rc >= max(disk.disk_r1, R_ISCO) && rc <= disk.disk_r2) { - diskColor = DiskEmission(hitP, newPos - prevPos); - hitDisk = true; + diskColor = DiskEmission(hitP, newPos - prevPos, outG, outTemit); + hitDisk = true; + outHitDisk = true; break; } } @@ -422,8 +437,43 @@ float4 fragmentMain(VSOutput input) : SV_Target // 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 // resolution-correct per-pixel UV footprint. + // Observable export channels: the chosen scalar quantity, either as a raw + // float (rawOutput: value in RGB, validity mask in A) or false-coloured. + // Channel 0 is the normal colour image. + if (cam.outputChannel != 0) + { + float g, Temit; bool hitDisk; + TracePixel(input.texCoord, g, Temit, hitDisk); + + float value; float valid = 1.0; + if (cam.outputChannel == 3) // impact parameter: a per-ray geometric quantity (r_s) + { + float uu = (2.0 * input.texCoord.x - 1.0) * cam.aspect * cam.tanHalfFov; + float vv = (1.0 - 2.0 * input.texCoord.y) * cam.tanHalfFov; + float3 dir = normalize(uu * cam.camRight - vv * cam.camUp + cam.camForward); + value = length(cross(cam.camPos, dir)) / SagA_rs; + } + else // g / T are disk-only + { + valid = hitDisk ? 1.0 : 0.0; + value = (cam.outputChannel == 1) ? g : Temit; + } + + if (cam.rawOutput != 0) + return float4(value, value, value, valid); // raw: physical value + validity + + if (valid < 0.5) return float4(0.0, 0.0, 0.0, 1.0); + float norm = (cam.outputChannel == 1) ? value / 1.5 + : (cam.outputChannel == 2) ? value / 15000.0 + : value / 30.0; + return float4(Falsecolor(norm), 1.0); + } + + float _g, _t; bool _hd; + if (cam.rawOutput != 0) // raw colour: linear pre-tone-map radiance (HDR) + return float4(TracePixel(input.texCoord, _g, _t, _hd), 1.0); if (cam.moving) - return float4(ACESFilm(TracePixel(input.texCoord)), 1.0); + return float4(ACESFilm(TracePixel(input.texCoord, _g, _t, _hd)), 1.0); float2 dUV = float2(ddx(input.texCoord.x), ddy(input.texCoord.y)); float2 offs[4] = { @@ -432,7 +482,7 @@ float4 fragmentMain(VSOutput input) : SV_Target }; float3 sum = float3(0.0); for (int i = 0; i < 4; ++i) - sum += TracePixel(input.texCoord + offs[i] * dUV); + sum += TracePixel(input.texCoord + offs[i] * dUV, _g, _t, _hd); return float4(ACESFilm(sum * 0.25), 1.0); } -- cgit v1.3