feat(render): volumetric disk integration in the RK45 loop

Restructures the disk block in the main loop:
- Off tier: flat disk_color_flat single midplane sample (old behavior).
- Volumetric tiers: (A) in-slab per-step sampling accumulates emission ×
  arc length, reusing the RK45 adaptive step density; (B) midplane edge-
  capture weights one at-plane sample by slab depth along the ray.
Removes the Task-5 disk_color shim. physics.rs untouched; cargo test green.
This commit is contained in:
xfy 2026-07-15 13:40:35 +08:00
parent 107fbd0a74
commit e2e2469971

View File

@ -311,11 +311,6 @@ fn disk_color_flat(pos: vec3<f32>, dir: vec3<f32>) -> DiskSample {
return DiskSample(vec3<f32>(col), 0.85); return DiskSample(vec3<f32>(col), 0.85);
} }
// TEMPORARY shim removed in Task 7 when the main loop is restructured.
fn disk_color(pos: vec3<f32>, dir: vec3<f32>) -> vec3<f32> {
return disk_color_flat(pos, dir).color;
}
// Volumetric disk color: ridged filaments drive brightness, a smoothstep- // Volumetric disk color: ridged filaments drive brightness, a smoothstep-
// gated FBM drives density clumping, and a logarithmic-spiral term (riding // gated FBM drives density clumping, and a logarithmic-spiral term (riding
// the Keplerian shear `rot`) drives large-scale arm structure. The three // the Keplerian shear `rot`) drives large-scale arm structure. The three
@ -553,13 +548,41 @@ fn fragment(in: VertexOutput) -> @location(0) vec4<f32> {
break; break;
} }
// --- volumetric disk ---
if (uniforms.disk_quality == 0u) {
// Off tier: zero-thickness single midplane sample, fixed alpha.
if (disk_hit(prev, new_pos)) { if (disk_hit(prev, new_pos)) {
let ty = prev.y / (prev.y - new_pos.y); let ty = prev.y / (prev.y - new_pos.y);
let hit = mix(prev, new_pos, vec3<f32>(ty)); let hit = mix(prev, new_pos, vec3<f32>(ty));
let dc = disk_color(hit, new_dir); let s = disk_color_flat(hit, new_dir);
let a = 0.85; accum_color += (1.0 - accum_alpha) * s.color * s.density;
accum_color += (1.0 - accum_alpha) * dc * a; accum_alpha += (1.0 - accum_alpha) * s.density;
accum_alpha += (1.0 - accum_alpha) * a; if (accum_alpha > 0.99) { break; }
}
} else {
// Volumetric tier.
// (A) In-slab per-step sampling: if this step ends inside the
// thickness slab, accumulate emission × arc length. Reuses the
// RK45 adaptive step dense where light bends, sparse where straight.
let slab_r = r_of(new_pos);
if (abs(new_pos.y) < uniforms.disk_half_thickness
&& slab_r >= uniforms.disk_inner
&& slab_r <= uniforms.disk_outer) {
let s = disk_color_volumetric(new_pos, new_dir);
let step_len = length(new_pos - prev);
accum_color += (1.0 - accum_alpha) * s.color * s.density * step_len;
accum_alpha += (1.0 - accum_alpha) * s.density * step_len;
}
// (B) Midplane edge-capture: if a step straddles y=0, add one
// precise at-plane sample weighted by the slab depth along the ray.
if (disk_hit(prev, new_pos)) {
let ty = prev.y / (prev.y - new_pos.y);
let hit = mix(prev, new_pos, vec3<f32>(ty));
let s = disk_color_volumetric(hit, new_dir);
let thickness_proj = uniforms.disk_half_thickness / max(abs(new_dir.y), 1e-3);
accum_color += (1.0 - accum_alpha) * s.color * s.density * thickness_proj;
accum_alpha += (1.0 - accum_alpha) * s.density * thickness_proj;
}
if (accum_alpha > 0.99) { break; } if (accum_alpha > 0.99) { break; }
} }