aboutsummaryrefslogtreecommitdiff
path: root/assets/shaders/geodesic.slang
diff options
context:
space:
mode:
Diffstat (limited to 'assets/shaders/geodesic.slang')
-rw-r--r--assets/shaders/geodesic.slang64
1 files changed, 57 insertions, 7 deletions
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<Camera> 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);
}