render: march sun shafts at quarter res with reach-proportional steps

This commit is contained in:
mchughalex 2026-07-21 13:01:08 +01:00
parent 4d616e4d45
commit ceb98c34ec
4 changed files with 621 additions and 589 deletions

View file

@ -52,7 +52,8 @@ cbuffer C : register(b0) {
// strength / phase g, p1.xy = proj m22 / m32, vp/vs0/vs1/vs2 = the // strength / phase g, p1.xy = proj m22 / m32, vp/vs0/vs1/vs2 = the
// row-vector inverse view-projection (world = clip * M). // row-vector inverse view-projection (world = clip * M).
float4 vp; // tonemap/linearize: x = |m00|, y = |m11|, z = m22, w = m32 float4 vp; // tonemap/linearize: x = |m00|, y = |m11|, z = m22, w = m32
float4 vs0; // tonemap: w = shaft intensity (xyz unused) float4 vs0; // tonemap: x = shaft plane resolution divisor (>= 2),
// w = shaft intensity (yz unused)
float4 vs1; // tonemap: xyz = haze tint (xe space), w = haze intensity float4 vs1; // tonemap: xyz = haze tint (xe space), w = haze intensity
float4 vs2; // tonemap: xyz = sun direction in AO view space (toward the float4 vs2; // tonemap: xyz = sun direction in AO view space (toward the
// sun), w = haze density (1 / view unit) // sun), w = haze density (1 / view unit)
@ -248,7 +249,14 @@ float4 ps_vol_shafts(VSOut i) : SV_Target {
float dist = length(ray); float dist = length(ray);
float3 rdir = ray / max(dist, 1e-4); float3 rdir = ray / max(dist, 1e-4);
float reach = min(dist, src.w); float reach = min(dist, src.w);
float steps = clamp(p0.x, 8.0, 64.0); // Constant per-meter sample density: a short ray to near geometry needs
// proportionally fewer steps for the same marched quality (the fixed
// count oversampled a 5 m ray 8x versus the full-reach case), and the
// path-fraction scale keeps near-surface terms small enough that the
// extra variance stays under the tent blur.
float steps_max = clamp(p0.x, 8.0, 64.0);
float steps = clamp(ceil(steps_max * reach / max(src.w, 1.0)), 8.0,
steps_max);
float jitter = Ign(i.pos.xy); float jitter = Ign(i.pos.xy);
float lit = 0.0; float lit = 0.0;
float tot = 0.0; float tot = 0.0;
@ -400,7 +408,8 @@ float4 ps_tonemap(VSOut i) : SV_Target {
bool sc_vol = sc_mask < 0 || (sc_mask & 64) != 0; bool sc_vol = sc_mask < 0 || (sc_mask & 64) != 0;
if (vs0.w > 0.0 && sc_vol) { if (vs0.w > 0.0 && sc_vol) {
float dpix = lin_depth.SampleLevel(smp_point, i.uv, 0); float dpix = lin_depth.SampleLevel(smp_point, i.uv, 0);
float2 vt = size.zw * 2.0; // one shaft-plane texel in uv // One shaft-plane texel in uv (vs0.x = the march resolution divisor).
float2 vt = size.zw * max(vs0.x, 2.0);
float2 base = (floor(i.uv / vt - 0.5) + 0.5) * vt; float2 base = (floor(i.uv / vt - 0.5) + 0.5) * vt;
float2 f = saturate((i.uv - base) / vt); float2 f = saturate((i.uv - base) / vt);
float acc = 0.0; float acc = 0.0;

File diff suppressed because it is too large Load diff

View file

@ -249,10 +249,20 @@ REXCVAR_DEFINE_DOUBLE(skate3_native_render_scene_shafts_reach, 40.0, "Skate 3",
.range(5.0, 300.0) .range(5.0, 300.0)
.lifecycle(rex::cvar::Lifecycle::kHotReload); .lifecycle(rex::cvar::Lifecycle::kHotReload);
REXCVAR_DEFINE_INT32(skate3_native_render_scene_shafts_steps, 64, "Skate 3", REXCVAR_DEFINE_INT32(skate3_native_render_scene_shafts_steps, 64, "Skate 3",
"Sun-shaft march steps per pixel (half-res pass). More steps " "Sun-shaft march step ceiling per pixel; rays shorter than "
"resolve thinner shadow volumes at proportional GPU cost.") "the full reach take proportionally fewer steps at the same "
"per-meter density. More steps resolve thinner shadow "
"volumes at proportional GPU cost.")
.range(8, 64) .range(8, 64)
.lifecycle(rex::cvar::Lifecycle::kHotReload); .lifecycle(rex::cvar::Lifecycle::kHotReload);
REXCVAR_DEFINE_INT32(skate3_native_render_scene_shafts_res, 4, "Skate 3",
"Sun-shaft march resolution divisor (2 = half res, 4 = "
"quarter res). The march output is low frequency (jittered "
"average under a tent blur) and upsamples depth-aware, so "
"higher divisors trade little quality for a proportional "
"GPU saving.")
.range(2, 8)
.lifecycle(rex::cvar::Lifecycle::kHotReload);
REXCVAR_DEFINE_BOOL(skate3_native_render_scene_sun_override, false, "Skate 3", REXCVAR_DEFINE_BOOL(skate3_native_render_scene_sun_override, false, "Skate 3",
"Lighting lab: replace the captured sun direction with the " "Lighting lab: replace the captured sun direction with the "
"azimuth/elevation cvars. Every per-frame light transform is " "azimuth/elevation cvars. Every per-frame light transform is "

View file

@ -71,6 +71,7 @@ REXCVAR_DECLARE(bool, skate3_native_render_scene_shafts);
REXCVAR_DECLARE(double, skate3_native_render_scene_shafts_intensity); REXCVAR_DECLARE(double, skate3_native_render_scene_shafts_intensity);
REXCVAR_DECLARE(double, skate3_native_render_scene_shafts_reach); REXCVAR_DECLARE(double, skate3_native_render_scene_shafts_reach);
REXCVAR_DECLARE(int32_t, skate3_native_render_scene_shafts_steps); REXCVAR_DECLARE(int32_t, skate3_native_render_scene_shafts_steps);
REXCVAR_DECLARE(int32_t, skate3_native_render_scene_shafts_res);
REXCVAR_DECLARE(bool, skate3_native_render_scene_ssao_debug); REXCVAR_DECLARE(bool, skate3_native_render_scene_ssao_debug);
REXCVAR_DECLARE(bool, skate3_native_render_scene_ssao_full_res); REXCVAR_DECLARE(bool, skate3_native_render_scene_ssao_full_res);
REXCVAR_DECLARE(double, skate3_native_render_scene_ssao_intensity); REXCVAR_DECLARE(double, skate3_native_render_scene_ssao_intensity);
@ -1087,9 +1088,13 @@ bool ApplyVolumetricPass(const NativeGuestOutputRenderContext& context,
} }
} }
// ---- Shaft planes (half res, march + blurred), rebuilt on resize. // ---- Shaft planes (march + blurred), rebuilt on resize or divisor
const uint32_t vw = std::max(1u, (width + 1) / 2); // change; the tonemap upsample reads the divisor from vs0.x.
const uint32_t vh = std::max(1u, (height + 1) / 2); const uint32_t vol_div = uint32_t(
std::clamp<int32_t>(REXCVAR_GET(skate3_native_render_scene_shafts_res),
2, 8));
const uint32_t vw = std::max(1u, (width + vol_div - 1) / vol_div);
const uint32_t vh = std::max(1u, (height + vol_div - 1) / vol_div);
if (shafts && if (shafts &&
(g_r.vol_width != vw || g_r.vol_height != vh || g_r.vol_tex == nullptr || (g_r.vol_width != vw || g_r.vol_height != vh || g_r.vol_tex == nullptr ||
g_r.vol_tex_b == nullptr)) { g_r.vol_tex_b == nullptr)) {
@ -1272,7 +1277,7 @@ bool ApplyVolumetricPass(const NativeGuestOutputRenderContext& context,
g_r.vol_rows[1] = std::fabs(pr[5]); g_r.vol_rows[1] = std::fabs(pr[5]);
g_r.vol_rows[2] = pr[10]; g_r.vol_rows[2] = pr[10];
g_r.vol_rows[3] = pr[14]; g_r.vol_rows[3] = pr[14];
g_r.vol_rows[4] = 0.0f; g_r.vol_rows[4] = float(vol_div);
g_r.vol_rows[5] = 0.0f; g_r.vol_rows[5] = 0.0f;
g_r.vol_rows[6] = 0.0f; g_r.vol_rows[6] = 0.0f;
g_r.vol_rows[7] = shaft_plane ? shaft_int : 0.0f; g_r.vol_rows[7] = shaft_plane ? shaft_int : 0.0f;