The volumetric pass marched 800 m in a fixed number of steps whatever the wall: it now stops at the wall with steps in proportion (at least 8) - 0.57 -> 0.33 ms at 30 m by the pass timers. SSGI skipped only past 900 m; it now also skips past 0.85 of a wall, where the fog is solid (u_ao_far). applyFog samples the fog's colour only where its weight is above zero. A card layer whose casters inside the wall are 80% or more of it (a kilometre's wall) casts from its static list and is not refiltered and re-uploaded every 4 m of camera motion. Fog off: the frame unchanged (0 pixels over 8). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
47 lines
2 KiB
Text
47 lines
2 KiB
Text
# fog_casters.ludic — under a fog wall, a layer's cards cast shadows only from where they can be seen.
|
|
# The impostor shadow list is every instance on the map; past the wall that is a vertex bill for
|
|
# nothing, so the casters inside the wall (and a shadow's throw past it) go into a list of their own.
|
|
|
|
const FOG_CAST_THROW: float = 30.0 # the longest shadow a tree throws back toward the camera
|
|
const FOG_CAST_STEP: float = 4.0 # the camera moves this far before the list is rebuilt
|
|
|
|
# the buffer and count the card shadows draw from: the whole list, or the wall's share of it
|
|
function fog_casters(render3d_st: mut Render3dState, l: Layer) -> int {
|
|
let w = render3d_st.r3d_fog_wall
|
|
if w <= 0.0 { return l.sh_buf }
|
|
let dx = render3d_st.cam_pos[0] - l.fog_x
|
|
let dz = render3d_st.cam_pos[2] - l.fog_z
|
|
if l.fog_wall == w and l.fog_src == l.n_sh and (l.fog_all or dx * dx + dz * dz < FOG_CAST_STEP * FOG_CAST_STEP) {
|
|
if l.fog_all { return l.sh_buf }
|
|
return l.fog_buf
|
|
}
|
|
l.fog_wall = w; l.fog_src = l.n_sh
|
|
l.fog_x = render3d_st.cam_pos[0]; l.fog_z = render3d_st.cam_pos[2]
|
|
let far = w + R3D_FOG_MARGIN + FOG_CAST_THROW + FOG_CAST_STEP
|
|
let far2 = far * far
|
|
let src = l.inst
|
|
let dst = l.scratch
|
|
var n = 0
|
|
let total = Math.min(l.n_sh, l.count)
|
|
for i in 0 .. total {
|
|
let o = i * INST_FLOATS
|
|
let ex = src[o] - l.fog_x
|
|
let ez = src[o + 2] - l.fog_z
|
|
if ex * ex + ez * ez > far2 { continue }
|
|
let q = n * INST_FLOATS
|
|
for k in 0 .. INST_FLOATS { dst[q + k] = src[o + k] }
|
|
n += 1
|
|
}
|
|
l.fog_n = n
|
|
# a wall that keeps nearly the whole layer (a kilometre): the static list, and no rebuild as the camera
|
|
# moves - copying and uploading 60k cards every 4 m bought nothing there
|
|
l.fog_all = n * 5 >= total * 4
|
|
if l.fog_all { return l.sh_buf }
|
|
if n > 0 { gpu_buffer_upload(render3d_st, l.fog_buf, n * INST_FLOATS * 4, data_of(dst), GPU_DYNAMIC) }
|
|
return l.fog_buf
|
|
}
|
|
|
|
function fog_casters_n(render3d_st: Render3dState, l: Layer) -> int {
|
|
if render3d_st.r3d_fog_wall <= 0.0 or l.fog_all { return l.n_sh }
|
|
return l.fog_n
|
|
}
|