feat(render3d): the air and the light are the hour's, not one constant apiece
Aerial perspective is a curve now. A low sun shines through far more air than a high one and shines ALONG the ground rather than down onto it, so density, height falloff and forward scatter all ride the sun's elevation; overcast thickens the air and flattens the scatter, because a grey sky has no disc to scatter from. `lowsun` falls away BELOW the horizon as well as above it, or the middle of the night gets a dawn's haze with no dawn to justify it. The term that was missing entirely is distance DESATURATION. Blending a saturated green ridge toward a saturated blue noon sky leaves a saturated ridge - which is why the same valley read as a photograph at dusk, where the fog colour happened to be a warm grey, and as a toy at one o'clock. A surface is now pulled toward its own luminance faster than the fog itself arrives. Measured far/near saturation at the camp: 07:00 1.11 -> 0.89, 09:00 1.04 -> 0.93, 13:00 0.98 -> 0.89. The grade is the hour's too - nine literals bound at the draw, written by daylight_set now. Noon is the case worth naming: direct sun is warm-white and the only thing filling a midday shadow is a blue sky, so noon gets a cool balance over a blue-lifted shadow with hard contrast, and dawn and dusk the reverse. Ground R-B, lit vs shadowed: 07:00 +42.8/+14.2 -> +48.9/+15.1, 13:00 +32.2/+14.8 -> +25.2/+2.9. Gain is left alone deliberately: the grade is `c * gain + lift * (1 - c)`, so warming it warms the whole frame, and warming it at noon made one o'clock yellower than seven in the morning - the opposite of the point. The visible sky is relit. Turning a photograph on its axis does not change what colour it was taken at, so every sunset had a mid-morning blue overhead. An analytic sky supplies the chroma and the photograph keeps the luminance: the cloud stays where it is and goes orange at dusk, the zenith goes deep blue at noon, and no second sky is shipped. It fades out under the horizon and eases off under cloud. The ground bounce follows the ground, crossing meadow to rock at the map's treeline instead of being one green constant everywhere including above the scree. R3D_NOAIR=1 restores all of it, so a before-and-after comes from one binary at one hour; it joins R3D_NOCLOUD / R3D_NOSHADOW / R3D_NOGI. Verified on macOS OpenGL, macOS Vulkan (MoltenVK) and Windows Vulkan (RTX 3070 Ti). Backends agree: mean difference 0.15-0.88/255 within a machine. Across machines the ORIGINAL renderer already differed by 5.02/255 at 19:12 and this build differs by 2.80, so cross-platform variance is pre-existing and did not grow. 400 frames: GL 7.4 s before and after, VK 7.0 s before and after. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
f094acfdb5
commit
d34fb5bc63
59 changed files with 1560 additions and 1035 deletions
|
|
@ -27,6 +27,23 @@ var post_lum: words = null
|
|||
var post_mips: int = 0
|
||||
var post_adapt: int = 0 # smoothed exposure (float bits)
|
||||
var post_exposure_max: int = 0x41A00000 # 20: the ceiling auto-exposure may reach (night lowers it)
|
||||
# ---- the grade ------------------------------------------------------------------------------
|
||||
# White balance, the shadows' floor and the highlights' gain. These were nine literals bound at
|
||||
# the draw, so the game had the same colour at seven in the morning as at one in the afternoon -
|
||||
# every knob a grade needs, and no hand on any of them. daylight.ludic owns them now and writes
|
||||
# them from the sun's own elevation; the values here are what they used to be hard-coded to, so a
|
||||
# program that never starts a day looks exactly as it did.
|
||||
# They are plain ints rather than a v3 on purpose: a grade is written by daylight_set, which can
|
||||
# run before post_init has allocated anything, and nine ints cannot be null.
|
||||
var post_wb_r: int = 0x3F828F5C # 1.02
|
||||
var post_wb_g: int = 0x3F800000 # 1.0
|
||||
var post_wb_b: int = 0x3F7851EC # 0.97
|
||||
var post_lift_r: int = 0x3B83126F # 0.004
|
||||
var post_lift_g: int = 0x3B83126F # 0.004
|
||||
var post_lift_b: int = 0x3C449BA6 # 0.012
|
||||
var post_gain_r: int = 0x3F7D70A4 # 0.99
|
||||
var post_gain_g: int = 0x3F7EB852 # 0.995
|
||||
var post_gain_b: int = 0x3F800000 # 1.0
|
||||
|
||||
var post_ao: Target = null
|
||||
var post_ao_blur: Target = null
|
||||
|
|
@ -324,9 +341,9 @@ function post_tonemap(color_tex: int) -> void {
|
|||
u_f(gpu_uniform(prog, "u_vignette"), post_vignette)
|
||||
u_f(gpu_uniform(prog, "u_saturation"), post_saturation)
|
||||
u_f(gpu_uniform(prog, "u_contrast"), post_contrast)
|
||||
u_f3(gpu_uniform(prog, "u_wb"), fl(1.02), F_ONE, fl(0.97))
|
||||
u_f3(gpu_uniform(prog, "u_lift"), fl(0.004), fl(0.004), fl(0.012))
|
||||
u_f3(gpu_uniform(prog, "u_gain"), fl(0.99), fl(0.995), fl(1.0))
|
||||
u_f3(gpu_uniform(prog, "u_wb"), post_wb_r, post_wb_g, post_wb_b)
|
||||
u_f3(gpu_uniform(prog, "u_lift"), post_lift_r, post_lift_g, post_lift_b)
|
||||
u_f3(gpu_uniform(prog, "u_gain"), post_gain_r, post_gain_g, post_gain_b)
|
||||
# the HDR10 variant's display calibration (the SDR program has none of these, and -1 sets nothing)
|
||||
u_f(gpu_uniform(prog, "u_hdr_peak"), r3d_hdr_peak_nits())
|
||||
u_f(gpu_uniform(prog, "u_hdr_paper"), r3d_hdr_paper_nits())
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue