From b12228c6625fcdb85ba096c72af0e5a8b689bb09 Mon Sep 17 00:00:00 2001 From: Orkuncakilkaya Date: Sat, 19 Sep 2026 16:06:16 +0300 Subject: [PATCH] feat(render3d): light you can see - sun shafts and valley mist Everything before this made the air a COLOUR APPLIED TO A SURFACE. Nothing put light in the space between surfaces, so the basin had no shafts, no pooled mist and no rays off a ridge at any hour, whatever was done to the fog. A half-resolution march from the camera to the depth buffer, asking the same shadow the rest of the frame asks - the cascades, the baked height-field shadow and the cloud mask - so a shaft is cast by the actual trees and the actual ridge and a passing cloud dims its own rays. Henyey-Greenstein scattering, because real air throws light forward. Density and a separate ground-hugging mist layer ride the sun's elevation, so mist forms in the cold at either end of the day and burns off by mid-morning. Composited with the bloom pyramid's own tent upsample under ONE/ONE - what was wanted and already there - and before bloom, so a shaft blooms. Into post_hdr, not post_scene: post_scene is what the water refracts and shafts added there would sit under the lake. The tuning that mattered was the sky term, which is added at every step: at 0.06 it accumulated into a flat grey wash lifting lit and shadowed air equally, which is the contrast a shaft is made of, and the valley came out one pale sheet. At 0.012 the sun dominates and there is light rather than fog. I cut the density and mist three times before the frame looked like air instead of paint. R3D_NOVOL=1 for an A/B; Off in Settings skips the pass whole. 400 frames at 07:00: GL 7.1 -> 7.3 s, VK 7.2 -> 7.4 s. Backends agree to 0.08/255. Co-Authored-By: Claude Opus 5 --- changes/light-you-can-see.md | 32 ++++++ packages/ludic.render3d/daylight.ludic | 12 +++ packages/ludic.render3d/post.ludic | 59 ++++++++++- packages/ludic.render3d/render.ludic | 3 + .../shaders/spv/1ce5c5d1.frag.spv | Bin 43416 -> 43736 bytes .../shaders/spv/3b183d8f.frag.spv | Bin 42184 -> 42504 bytes .../shaders/spv/4dda406a.frag.spv | Bin 42588 -> 42908 bytes .../shaders/spv/74258189.frag.spv | Bin 0 -> 18136 bytes .../shaders/spv/74258189.vert.spv | Bin 0 -> 1612 bytes .../shaders/spv/7ed7de52.vert.spv | Bin 23664 -> 23644 bytes .../shaders/spv/7f096322.vert.spv | Bin 23996 -> 23976 bytes .../shaders/spv/b4587cd4.frag.spv | Bin 49308 -> 49628 bytes .../ludic.render3d/shaders/spv/manifest.txt | 51 ++++++++++ packages/ludic.render3d/shaders/variants.list | 1 + .../ludic.render3d/shaders/volumetric.frag | 95 ++++++++++++++++++ 15 files changed, 252 insertions(+), 1 deletion(-) create mode 100644 changes/light-you-can-see.md create mode 100644 packages/ludic.render3d/shaders/spv/74258189.frag.spv create mode 100644 packages/ludic.render3d/shaders/spv/74258189.vert.spv create mode 100644 packages/ludic.render3d/shaders/volumetric.frag diff --git a/changes/light-you-can-see.md b/changes/light-you-can-see.md new file mode 100644 index 00000000..8c8b83fb --- /dev/null +++ b/changes/light-you-can-see.md @@ -0,0 +1,32 @@ +bump: minor +type: feat +Light you can see: sun shafts, and mist that lies in the valley. + +Everything before this made the air a COLOUR APPLIED TO A SURFACE. Nothing put light in +the space between surfaces, so a basin at dawn had no shafts, no pooled mist and no rays +off a ridge at any hour, however the fog was tuned. + +A half-resolution march from the camera to the depth buffer, asking the SAME shadow the +rest of the frame asks - the cascades, the baked height-field shadow and the cloud mask - +so a shaft is cast by the actual trees and the actual ridge, and a passing cloud dims its +own rays. Henyey-Greenstein scattering, because real air throws light forward, which is +why a low sun fills a valley when you look into it and does almost nothing when you look +away. Density and a separate ground-hugging MIST layer ride the sun's elevation from +daylight.ludic, so the mist forms in the cold at either end of the day and burns off by +mid-morning. + +It composites into the HDR scene with the bloom pyramid's own tent upsample under ONE/ONE +blending - which is exactly what was wanted and already existed - and before bloom, so a +shaft blooms like the bright thing in the air it is. Into post_hdr and not post_scene: +post_scene is the copy the water refracts, and shafts added there would sit under the lake. + +The tuning that mattered was the SKY term. It is added at every step, so at 0.06 it +accumulated into a flat grey wash that lifted lit and shadowed air by the same amount - +which is the contrast a shaft is made of. The valley came out as one pale sheet with no +rays in it. At 0.012 the sun's term dominates and there is light to see rather than fog. + +R3D_NOVOL=1 switches it off for an A/B. Off in Settings skips the pass whole rather than +marching once, because a feature is off when it costs nothing. + +Measured, 400 frames at 07:00: OpenGL 7.1 -> 7.3 s, Vulkan 7.2 -> 7.4 s. Backends agree +to 0.08/255. diff --git a/packages/ludic.render3d/daylight.ludic b/packages/ludic.render3d/daylight.ludic index 5e08bc28..5f5b015b 100644 --- a/packages/ludic.render3d/daylight.ludic +++ b/packages/ludic.render3d/daylight.ludic @@ -201,6 +201,17 @@ function daylight_set(hours: int) -> void { # zenith onto a grey day. r3d_sky_relight = f_mul(smoothf(f_rad(fi(-10)), f_rad(fi(1)), el), f_sub(F_ONE, f_mul(fl(0.75), oc))) + # ---- what is IN the air ------------------------------------------------------------- + # The volumetric march (post.ludic) needs the same story the analytic fog tells, or the + # shafts and the haze disagree. Density rides the sun's elevation like everything else; the + # MIST is separate and is the thing a valley actually does - a shallow layer that forms in + # the cold at either end of the day, lies ON the ground rather than filling the basin, and + # burns off by mid-morning. Cloud thickens the air and kills the shafts, because a shaft + # needs a disc to come from. + post_vol_density = f_mul(f_add(fl(0.00030), f_mul(fl(0.00070), lowsun)), f_sub(F_ONE, f_mul(fl(0.55), oc))) + post_vol_mist = f_mul(f_mul(fl(0.40), lowsun), f_sub(F_ONE, f_mul(fl(0.4), oc))) + post_vol_falloff = f_add(fl(0.006), f_mul(fl(0.004), lowsun)) + # R3D_NOAIR=1: the air as it was before any of the above - one density, one falloff, the # inscatter the shader used to hard-code, and no distance desaturation at all. It is here # so a before-and-after can be shot from ONE binary at one hour, which is the only kind of @@ -215,6 +226,7 @@ function daylight_set(hours: int) -> void { post_gain_r = fl(0.99); post_gain_g = fl(0.995); post_gain_b = F_ONE post_contrast = fl(1.12); post_saturation = fl(1.04) r3d_sky_relight = F_ZERO + post_vol_density = F_ZERO; post_vol_mist = F_ZERO } # exposure: auto-exposure must not turn the night into day # the ceiling has to move with the moon or auto-exposure eats the difference between a diff --git a/packages/ludic.render3d/post.ludic b/packages/ludic.render3d/post.ludic index a398a668..49905b68 100644 --- a/packages/ludic.render3d/post.ludic +++ b/packages/ludic.render3d/post.ludic @@ -56,6 +56,19 @@ var post_gi_strength: int = 0x3ECCCCCD # 0.4 var post_no_gi: bool = false var post_ldr: Target = null var post_depth_copy: Target = null +# ---- volumetric light ---------------------------------------------------------------------- +# Half resolution on purpose: in-scattered light is smooth, a shaft has no sharp edge, and the +# march is the whole cost of the pass. post_vol_steps is the quality dial; 0 switches it off +# and the pass is skipped entirely rather than run at one step. +var post_vol: Target = null +var post_p_vol: int = 0 +var post_vol_steps: int = 0x41C00000 # 24 +var post_vol_density: int = 0x3B03126F # 0.002 +var post_vol_falloff: int = 0x3BA3D70A # 0.005 +var post_vol_far: int = 0x44480000 # 800 m +var post_vol_g: int = 0x3F19999A # 0.6: air throws light forward +var post_vol_mist: int = 0 # the day sets these two +var post_vol_mist_h: int = 0x42200000 # 40 m var post_prev: Target = null # last frame's scene colour, for the SSGI bounce only var post_scene: Target = null # this frame's scene colour before the water, for refraction var post_frame: int = 0 @@ -75,7 +88,7 @@ function post_free() -> void { if post_hdr == null { return } if post_ms_fbo != 0 { gpu_fb_free(post_ms_fbo); post_ms_fbo = 0 } target_free(post_hdr); target_free(post_ao); target_free(post_ao_blur); target_free(post_ldr) - target_free(post_depth_copy); target_free(post_prev); target_free(post_scene) + target_free(post_depth_copy); target_free(post_prev); target_free(post_scene); target_free(post_vol) for i in 0 .. len(post_bloom) { target_free(post_bloom[i]) } post_hdr = null } @@ -136,6 +149,8 @@ function post_init(w: int, h: int) -> void { post_ldr = target_new(w, h, post_ldr_fmt(), GL_RGBA, GL_UNSIGNED_BYTE, false, GL_LINEAR) post_ldr_hdr = gpu_hdr_active() post_depth_copy = target_new(w, h, GL_R8, GL_RED, GL_UNSIGNED_BYTE, true, GL_NEAREST) + post_vol = target_new(max(w / 2, 1), max(h / 2, 1), GL_RGBA16F, GL_RGBA, GL_HALF_FLOAT, false, GL_LINEAR) + if post_p_vol == 0 { post_p_vol = r3d_program("fullscreen.vert", "volumetric.frag", "") } post_prev = target_new(w, h, GL_RGBA16F, GL_RGBA, GL_HALF_FLOAT, false, GL_LINEAR) post_scene = target_new(w, h, GL_RGBA16F, GL_RGBA, GL_HALF_FLOAT, false, GL_LINEAR) if post_p_sharp == 0 { post_p_sharp = r3d_program("fullscreen.vert", "sharpen.frag", "") } @@ -278,6 +293,48 @@ function post_ssao_pass() -> void { mesh_draw(post_fs) } +# The march, then the composite. It is added to the scene BEFORE bloom on purpose: a shaft of +# light is a bright thing in the air and should bloom like one, and compositing it after the +# bloom pyramid would give hard-edged rays with no glow at all. +function post_volumetric_pass() -> void { + if post_vol_steps <= 0 { return } + if r3d_env_has("R3D_NOVOL") { return } + gpu_depth_test(false) + gpu_blend(false) + target_bind(post_vol) + gpu_use_program(post_p_vol) + r3d_bind_2d(post_p_vol, "u_depth", 0, post_hdr.depth) + shadow_bind(post_p_vol) + sky_bind_lighting(post_p_vol) + sky_bind_rot(post_p_vol) + fog_bind(post_p_vol) + u_mat4(gpu_uniform(post_p_vol, "u_inv_vp"), cam_inv_vp) + u_v3(gpu_uniform(post_p_vol, "u_cam_pos"), cam_pos) + u_v3(gpu_uniform(post_p_vol, "u_sun_dir"), sun_dir) + u_v3(gpu_uniform(post_p_vol, "u_sun_color"), sun_color) + u_f(gpu_uniform(post_p_vol, "u_vol_steps"), post_vol_steps) + u_f(gpu_uniform(post_p_vol, "u_vol_density"), post_vol_density) + u_f(gpu_uniform(post_p_vol, "u_vol_falloff"), post_vol_falloff) + u_f(gpu_uniform(post_p_vol, "u_vol_far"), post_vol_far) + u_f(gpu_uniform(post_p_vol, "u_vol_g"), post_vol_g) + u_f(gpu_uniform(post_p_vol, "u_vol_mist"), post_vol_mist) + u_f(gpu_uniform(post_p_vol, "u_vol_mist_h"), post_vol_mist_h) + mesh_draw(post_fs) + # Composite into the HDR scene with the bloom pyramid's own upsample - a 3x3 tent under + # ONE/ONE blending, which is exactly what is wanted here and already exists, rather than a + # blit shader written for one caller. Into post_hdr, not post_scene: post_scene is the copy + # the water refracts, so adding shafts there would put them UNDER the lake. + target_bind(post_hdr) + gpu_blend(true) + gpu_blend_func(GL_ONE, GL_ONE) + gpu_use_program(post_p_up) + r3d_bind_2d(post_p_up, "u_src", 0, post_vol.color) + u_f2(gpu_uniform(post_p_up, "u_texel"), fr(1, post_vol.w), fr(1, post_vol.h)) + u_f(gpu_uniform(post_p_up, "u_radius"), F_ONE) + mesh_draw(post_fs) + gpu_blend(false) +} + function post_bloom_pass() -> void { gpu_depth_test(false) gpu_blend(false) diff --git a/packages/ludic.render3d/render.ludic b/packages/ludic.render3d/render.ludic index c847f5f2..88a551b9 100644 --- a/packages/ludic.render3d/render.ludic +++ b/packages/ludic.render3d/render.ludic @@ -285,6 +285,9 @@ function r3d_frame(time: int) -> void { post_color = post_hdr.color; post_color_w = post_w; post_color_h = post_h # DLSS super resolution: the lit frame up to the display's size, before anything reads it if gsl_dlss_live() { prof_begin("DLSS"); post_color = gsl_dlss_eval(); prof_end() } + # Before bloom and before the tonemap: a shaft of light is a bright thing in the air and + # should bloom like one. + prof_begin("volumetric"); post_volumetric_pass(); prof_end() if not post_no_gi { prof_begin("SSAO/GI"); post_ssao_pass(); prof_end() } if r3d_debug_max { tex_max(post_hdr.color, post_hdr.w, post_hdr.h, "hdr") } prof_begin("bloom") diff --git a/packages/ludic.render3d/shaders/spv/1ce5c5d1.frag.spv b/packages/ludic.render3d/shaders/spv/1ce5c5d1.frag.spv index 5f014a4ae53601544c8dc7486c34766e78b3cf8d..72e2e685202aed787f81c9bae196041cb6078832 100644 GIT binary patch delta 3343 zcmZ9OS!`8R6oz+O+A_4LbP$<~2na>3qH=+PL(~T!&}Sb=MPm`MSXAPJo*40g7ZP%f zA!>{!8WZ$I>xei2Dmb7r35YnMB8uVw3OIh>y?2G%dy{ka|Nm>)>+HSuxo7qE>fJl5 z8*8f1tWT*b4M_vjoK=mBYA=9xK703y=HWHz`MQ+4Z%(N;4M-coOS`*n=vkIu*)lQP z3|-RI+qF0q&F{l|d+xZY`$~CD`T)M5=axd~mI~jGD)`n4zP*C)tl*#2rL5oNdnz3E z_Tx3_bNHR5LcalD-?d;7v{2k3__E%vCBcutZ|`1sYw)40NBM)c7Wj21pL}YC!?X&1 zZUuLNd*-t?J-vkjruVbgrrG&btxZ*(eNVQ|t?kfxjHU3qid~ouZvzi5_r~pi6x6KnTiBVN4=$n%iCeYKrN(M!?=OVQNKZq|#7)i3Za`6cnHw8W=nw7pm3&_TYg>nH z4qNESR_kP67}8p9YLWX@@CXvA-#|~im1|UtfVaV;xU|__@~~|JPb%5$G+f>S)R958+XWZ#;R}J_ax6GS0W3JZ#&*K9n|_SRO_0 z06R~cMqi5xVqb|K+)G&|+h$k(wi!*?Zq!{R4?ABTsXhf;zR{indDuP!$69zI!tvafZ=2ba%^Ps?3BSbr1y?-rS72Al{&0$~!TPA% z$ZGq+Hu4oEyJdSg)g#ZhV708&JKurTSd_Z7gTMp%$8$~D_ecl09w?oKgJ50N8%jI= z1K8;%b7^;N!}epzHUdq*W**IQd^c!|a()8aZSIM){|w%iQvRu2V|ExUj~adf>#rVX z?pLt7XOuHn=)~XP>S|HX@9?NqyR}-6?Gf=7g~t|AEzpQE?QbcAU6~cZ}8dt+^T=t6v4T*flm{bVxO#hIqTwfDKfSLI!}{ z+9;$J-b@kd;Zp~8xx=R(zLq~m_1Lv<*hg796|xj34UyzDu!*!ql3`%~WPJ*gHo?{OHL*6e z@NWjY?ArXV^UK0XjXfPafw8ro{f{Ldj%bj^DC7*V`ZFxE`{x^N1bhXFp99A#j6_q9 zxKUtZ)uW)%;PM9;10S7n(>b0%F#^VdqjBu=HuyHiaU~yWNaMlkF|psw#;Hf13E=XH z&w{Jzo5jS65ik*Kz$5J6V@xs$uJ#bzqcj<;<}sSW=+9H6em0kyr$`+6pT@@%5;jNX z%L@C^4sk-A{SJ{wG3S7zdHg1kYxC*P1N$N|&hL`~=h(Q%9q{Fh<}*%v6r-(}pZ&iO z5|jJME00FG2;52%^_cWx@YPIuZLx#X47h&&Ld%sjUuvPvENQ%d_}Hi~e!jZ*CNMw; QlUt?Vv$OiP&fT=|f7FYH+W-In delta 3046 zcmZ9O*>6=<6vlUFpe;&UieiC+2#BQyUraG-h*2JVK>mWJqM;4yC8fZVo)GbY7ZY-g zA!>}iqAysS5Kut`N0dnvC!BD=d0=qB-|zOUa(i!b?*6`St+m%Wd#!WsJ$9se^P%eI zn(E6NQmRUgXm; z{KX2s13p+-v!eeN{cF-r`0~PCrM-4l_`XuXcaI+xiqj|0$@Y!Xk=$=q@VCeCnsflZ zHm~yt`1Zc#E1{*_pTO4*_N@y3HGHUl;GW>;;pO+)(2%k*LI;Ob?l8TAcUEu@wb0A0 z7Y0iOTrtL8mlnX+^bW1=?Z0ziMgLIAryFf;?_gnws+OeTUG3M@Ep(shR>@}x4KZ#e zctRPUrIC;J?%|moy%Q&W!~LYxbW>NVPc5MO;*;~IES^O099Sog4nYn0VXo_PZZb9b zDOQvDkuPP(?%}iZudT{jksJ)In&b{f@J6_UQO+HV;QBil<=o*Y=MG0X-+h6%FuEzd zc0ovu#bsTs)0~$G(ZhKe<;C?~tyKq#d%I?2&cdjUpR*9$(wv2I&Mb`KjzYQJQ3!6{ z&NA+PwqE;UnmnyO4G%9kF+Q^?&ylEu>jn({l3wD=DR)2PwCm@CtxZfXva}ngNcJ*@D8xp{!}cn81DAm|MIN?2;LW+s2Faty zy`^Z)?tF)10Q5vT6^Js-$zrA`#%Ww-0mmFrX2$7 zquxY2)DDBISZ~hl-h^)|_1NcV{ftx+v%LEsU_8nd#T+Z1Sk#(*h;oeUA=0s=kNDD4 zy(#biaj@y;<@PPCVf#3@O-0kMja#!EUzXaUoKL~dUS$6a%uy6iE^5v`2g{>|6JY(- zAIR(a0<6A+JnM7qOSrmP)bkZQYSnJ7*5d=JEqZ(s+{4wB#$}}&{}y84Lat3<-*!6L z2bT05U(UCFZs-%P<_#T3{~l~_A7zasKY-P@>7TYi{KM8zI2W|P>g_!!7(^ac?3$g6vuC8+L=mQOdKUfqqS<#g!Dthr`p(n2K6I;@dIC)kqT23rkNH<5c@2P z1s4zo#j=Rn;sT;##igx^8?qEv7Pq3Ph`51>EUl>j-rC?A{r~^x+;h&o_uSWR z-w|2V7a19iTore&z?HaSR~%n9a$0O4=3HTXbIaVs#Zfm$BsI;sB3I}Z3pb^b_37E! zc^8iMmWVbcGs)>LXI>_rNza^`x(Ob2%f% zd`8BOBOdSCdC`GFk7Sp#6#Z`2DH6OwctnU_iBUi7B*MQi;6;Idn1}tzOFX6V=fgwx zgo_6bk-$NPU3Y2A`QBX3=xm%mZj4efxrdiKHr{xz&r5HFpC`*!aRM7ZEIwNN3fa#H zM}?Q_sR=j>M}r^I$in%-qcJzuUui)g;J`}x*#uS+<|=&Bm{A24B@(#{nDI?$yg$sd z1(k`*xE|)g46cXqM7F9jQN^VgQslv<2y-rlaU!&5CsdYuYW-m9soOIl^ey>qIoTd-X!I*sa0|1wwCiMV{|8PoziA z7;WNw;qb%-;!H%hVV59rs)!KRu?qR+7z!8smOV}zB(8w6Uv?!~xWGlq2 z5SkTkwnv5Q^e~Ucg`4d$VHSmrCWc#)tAxo@fsxNV0(iB?4kaJ2ZK7?~W|OrE?{TSX z0}nbMZlRtKrhI0v(+D@)Q^K|v+z5EJdWr++5N^@Pk|>=Lse}s1K`iDduIK+1V*+LE z)}-~qyENXS@1d>tX=&)jpAlx?=oS(_D~=C3ZH@MvFl{{{u(JrRD!Rql7%R`+UoU8- z=Vjce$BNmMt*A+OFG$&>XNA&fa4+hGCwggc{x1oW?%Kf4PMd9WV7o*beq~x3)7v_L`b9Z;LS8YIs!`e{{QnuL+}bV_Sn-Ul&J5vwGeTw_34NEA{ZD z!bUwB+?#qGQ9Y%uICq=hlEB0&Jw3v`dT_E^`?fIsjUNHKHKMVgJ<{J1=4`iWv?T8e zqj$?^rEKpBqxI^~*(t^NKv*9h=LS?2#`pCgo^}x*TcB;yNNBt9p)gvBDz;+K&S;*^ zXSChlE?ln1c7KO3#dgq$)*w42Vz6(`E@393TOobI9IX|yTewU`pqtM}!fdzsd@S6h zKc?t*be{+_7n`?4+e2G_Dne3>7VI-&R&G9@3*(1wK6`{0>oK3b!uX-%L+t&+_Bq-o z46n}hk>|b;X_6BL3-YBffv{PSuY{NDVHE`ZS{MyqjsY8*`F|tKwqxtpI7tslnR`I^ zYK{G|vFEn@TZv4Q>`{MYEf+Lh!F zVYF4^+?}JsXxy7WH4fby49D~!a&N$vpi^$#39~U;!oP$`i0^TYaI^g_Y~6A~7>-SY zJEa$2j#^I>Pl_P$r;<}WExbS@0hx&1ios?H{t>q1gTip@jI+X{6a(Fo{wqAFq_^f; v*8L}b8;<-;R1FS|&zNZifyO`36Xn1^xe;juz#luoDRMw#B;K`cT%`Vg^!?GD delta 3224 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 a%KS%a2F(EaYd5$>`q7%{oO|KMmj3}4(WE&5 delta 3032 zcmZ9OTWnQT5QbOpD8&TYU?PbTHAD%fpb0_6-w?$E zs1*?vR0=9uuZTBL@q(fVqJp5}4aGZh6V&fJ>}gNWN!DKf|Ie&7v)9bp?b0okyEa!2 zs;WGzE~SbzFbzm`3kS`rIaZre?bA2SXw0h9d~iog^R;dBQmRSS=??hp=Jw|6(+PfO z8DCh&7s1=x=FDuFqJLFd44>Y1U2(4^Wxn^8@vf74g~HLs5!r*i^hE53%lL|Zyed5g zpPT3T1o)cf>9e54*w4Y|wKvZW{tCRarS*p3JK&}7xu-T|{e%N$4hPHl;WF-_woT*J z+uDl>9PekZNj219&a}>sX)QBaXSQ?}eQMF>PHS)LB&(^Zr)%i>HDlc8bhG3)m4di# zIQZleex^o#+G~4`4{JJQ@T)9Kss7?osWuG(?Jit4^3=)uP)ESd!0{h&6?h3_Le34Q zCg0C&Fn{tX*lK%Lj;yQ58jx%Zt(xREM(}}f8>5ul7{T?oF-p13QOa$OQr^|ar_j17 zE$tJEd|4T{dtw9q?VeJ;qA+;Wu)?LI8Z&#N)M9T0H&uJ1l+zpixUErYw>5$r+13c| zezt;qB1cDUdYaBVw=T0t3owjkOri*$uRM23cY%lHHqTnFjVayDV=TjUp1XWJA(&Eu zhh6V}uG6lc7hRi}+GJ@1P7&-L43UV1k%#R*@YM_#S|oYc?g!7#Z5B)(Nj?C!30!CG zHVa&LO>}iy>j73CaO3(1nQ@uVfcn{cU z=j8To=CD1R+fGB%uaS2#8^4y?BAe&IHd{o00nED>E*v)~dl4*;99{zJuYP-;*E+EJ zdg9!YYcIpq)gqtu@W@rWxtfnRQd<=DHEZ-Uh}R;F}_J>CXepmdZPX?%;JcsNxSacr~+ z&46(l?|{{YkZ~lVc7%1Y-Z@VHU9h((PX9fy$#&a$(ID?5YA}e6w;Ak0^+@Ccu%(Se zJ_I+CgnIaF0ejrx^AUI*e*@H`bRUCVt1VlQ2iyuVsK(gr6R>X{KHI?hsfW*Y@O(!2 zd5(Xz`x)KejF^1&iD^J zls(iVDBGut;8#)>rc?pHN=JVoRwAnT84Il{*ZAV3E7-;V&8pGdK!5FSZj#4XTivsL I;-=RB0XAx!@Bjb+ diff --git a/packages/ludic.render3d/shaders/spv/74258189.frag.spv b/packages/ludic.render3d/shaders/spv/74258189.frag.spv new file mode 100644 index 0000000000000000000000000000000000000000..08ccc2ae9bd77e1e85b86ca1b1bd3e1c9e4a3106 GIT binary patch literal 18136 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 literal 0 HcmV?d00001 diff --git a/packages/ludic.render3d/shaders/spv/74258189.vert.spv b/packages/ludic.render3d/shaders/spv/74258189.vert.spv new file mode 100644 index 0000000000000000000000000000000000000000..55a5473344437ce6ee3c76c7852439eaa953c12d GIT binary patch literal 1612 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 CSwxrs literal 23664 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

)WE~ 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 a2EpxLA9G%eV1vO6Q=7Bnmg|>)`}F_a<~GX! diff --git a/packages/ludic.render3d/shaders/spv/7f096322.vert.spv b/packages/ludic.render3d/shaders/spv/7f096322.vert.spv index 277a763b717d45e9894d701062bad6a4e8e5312e..417ebd22155b715aa7494612ae7f7ed1164070e2 100644 GIT binary patch delta 8575 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 V$vSQZiT|!Ta=DKFV{1>^{{eZ!>)u-nC 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 delta 3170 zcmZ9OS!`8R6oz-XZK=`<1*{ds7La*rQITRKA&NW@5{WU!#7HU{tEh#F<;k2>Vt6wl z*M!6n@N%2%?*ISS+H0+|_d5IBeH#l~ z`w9((VUvfaRFy`gy0mC>!4C1!JGwWdRFkUHEAX|QJ)QTZ zA-;1d(Pp7 z^f|mYFZV0(?VYRFKuab30N>ElxiM&{k}09@Egfs;7H>UQ$Q@y5<{e+I76tGWc7@#xKlybZ zcvLn!)utAtmw1o?=BKKL=2?WOg&#%d|B~Rpm3aNN@}nsI*&XE;yCb*-*&XHF?kMMU$2r{Q z2)p@hj^M^?%lQ&oV}EVxUp{}u$jsAx95jf!dlBwB zZK1skR<}DAb8eo9TEray8&}&7Fgz<6(b8)OvltpZDtQO>YkM7>F&of@+UvPbw7mhg zBjz%mVd#6NW+;x=i*{pr+}fj`@cW-%Xv zP2@V;K`!3KJb>M;_h@#*pJePV&bV$w_OXbe=SR%)i1;bk$Ixag$-}l699!nKB=2Bj zJhRWho0)wwpTJ73$V$v%EY}b>E4980Eblfp^9%5;%(t+Q@z`IYsR!Q&wrMuz4KWDU zN4=iSu!#L&&t@q@yAR=gsUC3-498P3%e(Ju@Bu~?bFjE_>6q*rl!J`NNas#}3)WM; zJ`J_WcVN>^&+P-uVf#L}jYHFKJG13@WowIa4uS2_$o?aottj5Sv?2QmERPxv>&sAo zB(Ljdu=-Kr_vP9VxVl=@^9wv`)o!iUnovHSmmE%rGZF&g9yq6U-LWn2Wwh3ZjA z6`DsIg$#o~K_K<;DS+MX@TrDx;)+v`qpJbCR$CVLVJ*a@8Y9?nuumR7b#VRE!{Guv?h4zMgzz5UWBF|r8a{v zWT;2#i^1ikj)SY|8>Om+|9CK^W@r0(0)j2#Pl-r;DOf!cw}4%!9)ViH<%uW4)%1= 0.0 && p.x <= 1.0 && p.y >= 0.0 && p.y <= 1.0 && p.z <= 1.0) { + s = shadowTap(p.xy, c, p.z - 0.0016); + } + return min(s, terrainShadow(wpos)) * cloudShadow(wpos); +} + +// how much air there is at a height: the basin's own haze, plus a shallow mist that lies on +// the ground rather than filling the valley +float volDensity(vec3 wpos) { + float y = wpos.y - u_fog_base; + float air = exp(-max(y, 0.0) * u_vol_falloff); + float mist = u_vol_mist * exp(-max(y, 0.0) / max(u_vol_mist_h, 1.0)); + return u_vol_density * (air + mist); +} + +// Henyey-Greenstein: real air throws light FORWARD, which is why a low sun makes the whole +// valley glow when you look into it and almost nothing when you look away from it. +float phaseHG(float c, float g) { + float g2 = g * g; + float d = 1.0 + g2 - 2.0 * g * c; + return (1.0 - g2) / (4.0 * PI * max(d * sqrt(max(d, 1e-4)), 1e-4)); +} + +void main() { + float d = texture(u_depth, v_uv).r; + // the world point this pixel looks at, and the ray to it + vec4 far4 = u_inv_vp * vec4(v_uv * 2.0 - 1.0, 1.0, 1.0); + vec3 farP = far4.xyz / far4.w; + vec3 dir = normalize(farP - u_cam_pos); + float march; + if (d >= 1.0) { + march = u_vol_far; // sky: march the whole way + } else { + vec4 h4 = u_inv_vp * vec4(v_uv * 2.0 - 1.0, d * 2.0 - 1.0, 1.0); + march = min(length(h4.xyz / h4.w - u_cam_pos), u_vol_far); + } + int steps = int(u_vol_steps); + float dt = march / float(steps); + // A dither per pixel, not per frame. Advancing it every frame only helps if something + // averages the frames, and there is no temporal resolve here (post.ludic); it would just + // be noise that changes, which is what the old TAA attempt was removed for. + float jitter = ign(gl_FragCoord.xy); + float ph = phaseHG(dot(dir, u_sun_dir), u_vol_g); + vec3 acc = vec3(0.0); + float trans = 1.0; + for (int i = 0; i < 64; i++) { + if (i >= steps) break; + float t = (float(i) + jitter) * dt; + vec3 p = u_cam_pos + dir * t; + float dens = volDensity(p); + if (dens > 1e-5) { + float vis = volShadow(p, t); + // in-scattering from the sun, and a little from the sky so shadowed air is not black + // The sky's own contribution is kept SMALL and deliberately so. It is added at every + // step, so it accumulates into a flat grey wash that lifts lit and shadowed air by the + // same amount - which is exactly the contrast a shaft is made of. At 0.06 the valley + // came out as one pale sheet with no rays in it at all; the sun's term has to dominate + // or there is no light to see, only fog. + vec3 inscat = u_sun_color * vis * ph + skyIrradiance(vec3(0.0, 1.0, 0.0)) * 0.012; + float a = dens * dt; + acc += inscat * a * trans; + trans *= exp(-a); + } + } + o_color = vec4(sane(acc), 1.0); +}