From 182cbf3cc3b323ffcfa56ec6436ae3c8d38e8401 Mon Sep 17 00:00:00 2001 From: Orkuncakilkaya Date: Sat, 19 Sep 2026 16:41:33 +0300 Subject: [PATCH] fix(render3d): a leaf is not matte Foliage roughness was pinned to 1.0 and grazing Fresnel switched off, so nothing green in the game had a highlight anywhere. The glint off waxy leaves and wet needles is most of what makes a stand look alive rather than painted. The reason it was off is real: a crown is card quads, and at a grazing angle the card's normal is a lie, so a specular lobe frosted whole crowns white against the sky. The sheen returns as its own term gated on exactly that - it fades as the card turns edge-on, which is where its normal stops meaning anything. A tight lobe for the glint, a weak wide one for the waxy rim, nothing at the angles that frosted. Translucency reaches 260 m rather than 140, and a dense crown passes 0.45 of it rather than 0.3, so a backlit stand glows for as far as you can see it. 400 frames: GL 7.1 s, VK 7.4 s. Backends agree to 0.14/255. Co-Authored-By: Claude Opus 5 --- changes/a-leaf-is-not-matte.md | 17 ++++++++ packages/ludic.render3d/shaders/model.frag | 40 ++++++++++++++++-- .../shaders/spv/1ce5c5d1.frag.spv | Bin 43736 -> 45008 bytes .../shaders/spv/3b183d8f.frag.spv | Bin 42504 -> 43760 bytes .../shaders/spv/4dda406a.frag.spv | Bin 42908 -> 44212 bytes .../shaders/spv/7ed7de52.frag.spv | Bin 46824 -> 48064 bytes .../shaders/spv/7f096322.frag.spv | Bin 46824 -> 48064 bytes .../shaders/spv/b4587cd4.frag.spv | Bin 49628 -> 50884 bytes .../shaders/spv/d48d6a48.frag.spv | Bin 46824 -> 48064 bytes .../shaders/spv/f4a2b586.frag.spv | Bin 46824 -> 48064 bytes 10 files changed, 54 insertions(+), 3 deletions(-) create mode 100644 changes/a-leaf-is-not-matte.md diff --git a/changes/a-leaf-is-not-matte.md b/changes/a-leaf-is-not-matte.md new file mode 100644 index 00000000..6eec66c4 --- /dev/null +++ b/changes/a-leaf-is-not-matte.md @@ -0,0 +1,17 @@ +bump: patch +type: fix +A leaf is not matte. + +Foliage roughness was pinned to 1.0 and grazing Fresnel switched off, so nothing green in +the game had a highlight anywhere: the glint off waxy leaves and wet needles, which is +most of what makes a real stand look alive rather than painted, was simply absent. + +The reason it was switched off is real. A crown is card quads, and at a grazing angle the +card's normal is a lie, so a plain specular lobe frosted whole crowns white against the +sky. So the sheen comes back as its own term gated on exactly that: it fades out as the +card turns edge-on, which is where its normal stops meaning anything. A tight lobe for the +glint, a weak wide one for the waxy rim, and nothing at all at the angles that frosted. + +Thin-leaf translucency reaches further with it - a backlit stand glows for as far as you +can see it, not 140 m - and a dense crown passes 0.45 of it rather than 0.3. The distance +cap that stops a two-pixel clump card becoming a lime disc stays. diff --git a/packages/ludic.render3d/shaders/model.frag b/packages/ludic.render3d/shaders/model.frag index e1925fef..6a2e58dc 100644 --- a/packages/ludic.render3d/shaders/model.frag +++ b/packages/ludic.render3d/shaders/model.frag @@ -147,6 +147,17 @@ void main() { if (u_model_h > 2.0) arm.r *= mix(0.5, 1.0, v_hull); #endif alb *= u_tint * (0.85 + 0.3 * fract(v_seed * 7.13)); +#if defined(FOLIAGE) && !defined(BLADE) + // Per-plant HUE, not only per-plant brightness. The line above varies value by +-15% and + // nothing else, so a stand of one species was one colour at fifteen different exposures - + // and a real stand is not: neighbouring trees of the same species sit anywhere from + // yellow-green to blue-green depending on age, aspect and how dry the ground under them is. + // Trading red against blue is a hue rotation about green, which is the axis that matters for + // foliage and costs two multiplies. Blades are excluded: they get their own patchy field + // and regionTint already, and doubling up on them reads as noise rather than variety. + float hj = fract(v_seed * 3.71) - 0.5; + alb *= vec3(1.0 + 0.20 * hj, 1.0, 1.0 - 0.20 * hj); +#endif #ifdef CARD // a card is its own caster: look up the shadow a little above and in front of it, and let // light bleed through the thin clump as real grass does @@ -173,11 +184,34 @@ void main() { // Only a thin leaf a few metres away is translucent. A clump card at 200 m is a whole // bush in two pixels, and giving it the leaf's glow toward the sun painted the // backlit hillsides with lime discs. The term fades out with distance. - float thin = 1.0 - smoothstep(30.0, 140.0, dist); + // A backlit stand glows for as far as you can see it, not 140 m. The cap below is what + // keeps a two-pixel clump card at distance from becoming a lime disc. + float thin = 1.0 - smoothstep(60.0, 260.0, dist); // a dense crown of cards is not a thin leaf: much less light comes through it - if (u_model_h > 2.0) thin *= 0.3; - col += alb * u_sun_color * (0.14 * back + 0.05 * wrap) * thin * shadow * cloudShadow(v_wpos); + if (u_model_h > 2.0) thin *= 0.45; + col += alb * u_sun_color * (0.24 * back + 0.07 * wrap) * thin * shadow * cloudShadow(v_wpos); col += alb * skyIrradiance(vec3(0, 1, 0)) * 0.12 * arm.r; + // ---- leaf sheen --------------------------------------------------------------------- + // A LEAF IS NOT MATTE. Foliage roughness is pinned to 1.0 just above and grazing Fresnel + // is switched off, so nothing green in this game has ever had a highlight - and the glint + // off wet needles and waxy leaves is most of what makes a real stand look alive rather + // than painted. The reason it was switched off is real, though: a crown is card quads, and + // at a grazing angle the card's normal is a lie, so a plain specular lobe frosted whole + // crowns white against the sky. + // + // So the sheen is added back as its own term and gated on exactly that: it fades out as + // the card turns edge-on (NoV), which is where its normal stops meaning anything. A tight + // lobe for the glint, a weak wide one for the waxy rim, and nothing at all at the angles + // that used to frost. + { + vec3 vv = normalize(u_cam_pos - v_wpos); + vec3 hh = normalize(vv + u_sun_dir); + float NoH = max(dot(n, hh), 0.0); + float NoV = max(dot(n, vv), 0.0); + float sheen = pow(NoH, 26.0) * 0.55 + pow(1.0 - NoV, 4.0) * 0.05; + sheen *= smoothstep(0.10, 0.42, NoV); + col += u_sun_color * sheen * shadow * cloudShadow(v_wpos) * 0.4 * (0.45 + 0.55 * arm.r); + } #endif col += alb * u_emissive; if (any(isnan(col))) col = vec3(0.0); diff --git a/packages/ludic.render3d/shaders/spv/1ce5c5d1.frag.spv b/packages/ludic.render3d/shaders/spv/1ce5c5d1.frag.spv index 72e2e685202aed787f81c9bae196041cb6078832..ab98010678593bf2757b3cef8336aba569ef8bb2 100644 GIT binary patch delta 2969 zcmZ9ONo-bC7={mjDJ`W`X=M-*nM6hv9FexrB7!0m9O?jC5o%kZEmBI|nCpNGHxg1~ zj2fd0H)>+AJ9fgtjSE9Gnz(S|l8r`WaD1NI`{^w=x&L|J_j}LxoqNtb=l(x@SNZ0r z%Jizr;+m8urD>@)y?A2!f$Dzf$jO&JpP9`79ol<%Z+}XuI!#X3PT*B(5&Y;;V^wJx zxVLv4-wfU{xHZ>r%W-Gk|8DS5Z+G`Vk#9S^oWFY_?@T=th17Qicc_D1`vwmV9?lC4 zOo&&fqwwLbBST%i-F-d1M{>`|*vVNNs@K?cKI8D(!n0{z2(ByfMFzQykBlvxH{7uP zQ2R>A6vW^CNw01SUQL4Ir-xgE*TR?YIo`4gcO%X@qaUqk>b6rWk(6HE7|wQhOWpT* zS7gfFFCK&6A=D)8If>kIDC)#T@B9lDpig90OCmsaeq{}KH%_$=*vv@Og9P5Of$^;390fd2_rJ5xzn zxr>a`!!&~D;cWxGiemx!@(V>iLYofL`;#!D_mlcLjXie@pWh|22 z4lXCZ1MZVW@;kwL)a#4fcY!0f@yP9&ml-s;xn>` z;E7tt;M(DO!oL}8j`5m{Q@RhLXvZj^7B9<|(O;UIvaKjvxFgN|VAI5&cmS+NJv`gM z<@4e}xIT3~zS29uab9c(%j3N001x3o&;Bd46VXHlqxdec`hF(NVY`tZf?v6jA4XG; z`F;fK0;%EWI{V?h`|_(0)yZvJ^+@-x8M+X7CF?T=nLR&6x~(qx5IFKUZD-uY#JBcSRo!M fj1{zt- delta 1717 zcmZ9MNo-9~9LCRk^;*>dt*W7hnrRI+7mtWwVS%+pBO(Y=T9)!6SlCEJBBqFq6^Zb6 zUFyQdLWD$OVPk0}hMN6;&;2LQyUDrd`@a8p&pG$Jc$a_kI^R;4pWl#DE;Xg`Y3tRN z}%b#5#489SRke0I}|zTMynReYXC9__{Q>8`V_P0c%cQ+;Yf z?6}t5)4sD2G?&mt&bX}x*MYAyHfP*YYVr@vmhzGBt!Ht{H+v4`_q#e;l-rqIooept zRC8A+coY7vPH^MbmhZ3F+gkcUg?>ge`@xjH^2#yyF|L9?GxWK@>M*4-UQaW>t@Ql~ zuC^WfTl8<>m&~`Bi?qs=zVlKa!RIdg2Uu-1pGxT~Nq#|mz7wGFCqr#4gThT$AgX4cI98aQfh=)RAVHY0QOrmr_==3Pd$8^!J}0^ zEpYwR_3>jSg1hi>eLO?*{;Ziv;9>TlVu(VM5ly5qlC*)Jlf`hMnAIyQPW?0uVvxpYWI9;=AGOhr};qgL|8|c@s0()~9=jBr2{5I}!AN(S-`Ha&Z&1j3_>%h-TSrYGOg*Yhd z!Ba`19z{2R_u#)jJHZ>_`uT*`pJ`rWp>4{vJlZ~*tX#lo#&;N?gURjE8+miNao4kB Fe*w!@rOp5V diff --git a/packages/ludic.render3d/shaders/spv/3b183d8f.frag.spv b/packages/ludic.render3d/shaders/spv/3b183d8f.frag.spv index b7f68ede92f53eede96bef26ba57e649d4c1cf52..7862a9acb74f08a212d9c9d442fa6de70a29883b 100644 GIT binary patch delta 2882 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 F{U5`8LfilV delta 1694 zcmZ9M$!|?z7{aszl1S<)DK_enGNTp>dCxVqkB27pTv9J;w zxmIFhNNkKX);y0j)ja$CuJdvAB;R?T_j#XpINv$nIozAsb1qX?nVC2!go;oT28V|2 zb*r*9xP?O-wic4dxe(Sa?OeJhgpdtY;ne`Y5@mTjJHQ__z_)To+p><;9i63mwg1Jl zVI=fIZIAHX^FVeukfn9fv=JqLttsteL5AV>$gktc|_&7m!;hE^lniG`Uws zr1Mz!s@!+4%KdEjv8ki8Dr<|a({|P*XGxgOs^vXk!Z~gg*d|sx_zX+UCUylA&U5ow z)U`0-X{=l;@f+|Lz&+S5>}-a@gdT44N6_rWzX+B)kqJroM3KulUSCNtc!?$VIb=#W z7Pg1#5A$T_{Y94{r$*b{Uo`7et+%3PIM4~yJ<|()fk%3s`8Q4O`Vv*A9)sKGd2|qNj~5 zs=&6ez5fUO7|VfYq%r@i`o^HbVDR)b=08@ynYMlc)!@xo>zQYq`bnvg!PuY<9Gm+i z)=$5MFcjQC5&7737<8pxru65UhqQnR3M}p} N__NIyU(P+%{u>%T+?oIY diff --git a/packages/ludic.render3d/shaders/spv/7ed7de52.frag.spv b/packages/ludic.render3d/shaders/spv/7ed7de52.frag.spv index 53a1b9ba208a2c3d2a446d3fafcbd3bf601cbfb0..7fe5d81d4aa2c674029b2387bff6b800875da947 100644 GIT binary patch delta 2821 zcmZ9OS!`BS6opU!QfdS=*u*p@C<-weLk##}h(ZJ;nqZ7PXks)r6~Q(LB`uKnVE#DZ zh}2RX7{mbwzyYx+j>zDEqJoNovw{o?sHoWDlizoHcfIvbPVQQ3@3Zz9_Py=?L#6ed zr2&LxYa(A1n2 zIM^#*mHvXys+-wT*D$&9`i7ZV&JncPb#mN9 z+UlyZwN7S^zl)IdJ{8=rz)v&C&$xBW?~`-um|x_@eCrzTo9D|5@e@*4o>0mYx;hpP z8aA*zW9(UYI%^gQDcv$oJ`i+c?hx@g@Yap9MhCwWe&*G!qk^x8k2-co7I*$m_~|?c zdBk?ES#ELuurBhQT#B*fD5C`S{Y_zU@oL zOs~J4&?Lqjy2op|L(EVg#g^3W0#8-XbM6Lv9qKpd+C5i93iIZIZ)%Mm3vI?N0`8H z*_u88Rx?>+V`@?KLGb3hs5e{7u&8qvf`{`|_h{4>ft_P47P7dyraN#BFT)rTy_F^H zZR2VhA0}-{A$2KuF+)9;@CdlL>tBG_Zz0WO-afxDG3 ze+g`kdYHcq4sYY(?OSThh4;1fUV&IJCcX-G;z~xT55z}cPf0xreGJyfSZG_p>bp1&H{q+g%~!Sj3FS5W=imtp=gGeStC{OD8dHn>9k#fiA(wP5=lLZ;&vj>> zO}E%Ltuo4+KL`=ntM{2rpXh@F(+KY-ObS@kZS z{{A7R{v$)JE0r=F1cskHr{^;)lRd{2*8!AN%sJkRg~Dh@!uNH&Aq9{=WSVH_u;ahjWch j_h^6Qn*SVK#v+Fj{1eUj<{Q`BMIPWutfO=M;Tit|=SzJz delta 1586 zcmZ9MOGuPa7>3XMIvSXS4b;$8Gunhi1(9?CwTNny?OF|HnPNFMYS5y7g6w|Nv5S{V zyW3P|WB06)HtpKAZ&yLk&Ip9wXF5mz=7+<1-}igZ_kHKWpPBT&{q&0Syz)Y0LZ-;f zFcV$H&Ed4@_~?<(bNq`!t#ygI1IC!J$v0PWcq)fK&fz0DoXqi9eSCjBk+hUD-oAx!fugRLmB_4_;;_bGH_gT}PriO-pO?}AnmV|3Eldg>xhJ9)+(h|Nc z>YdzW!bJhTgrGj+l);~GIc4w%dC*T;;sw?Z55#Aij5T=HkV$`ESW)8ts#>m`tm=@# zn3*;35~0I3gX4qZ%cPeo{!BPecxlL(b(XV3GRr@^qXXnFRrXmib(Q zO#17JX0Mi8CxTq6nOhHXZav7kb;qOfbL)NPrbxtziY z;eNG=zCjhyP6{^%Jg0=$>mtyP*>$IdSxfAKw8dI?M)vfW?}Vq_#oQh2)t?ZSJ6(eRR&7@G56 z7XDOVc4mW|cvOR;EWRqdQZqW|hIUPuMZ{bqy~60L_2#PN<2L$|2RB9heyM!{=YTM7 zbl1Xl;b6-*#L@7&meHL5rm$MhR@$u_91`E9dC+>e$y>tG{pl8OOJE@Zm*|dgu<%`R zG`y}bn)BZi#y_C5u28}I!f1Wsp7;Y{v`L-wL(Rs_HRg|k1co&be;dyXC%d*r${b^M zB_0V=g7T^4(B_Em9t5xbiFLBWr@|xlvGDTYm3sf&(Wr2b=9FgE5qBFR=BhmxcGdVN z!rk4!5H9D<5nusDUkXnM)asJquf#F(IqkL8bRg5(8>{h8+My=-oxGLCdi=!6O_Tgy Qh)wAyveu-(Z2Qsr7u{mfQ2+n{ diff --git a/packages/ludic.render3d/shaders/spv/7f096322.frag.spv b/packages/ludic.render3d/shaders/spv/7f096322.frag.spv index 53a1b9ba208a2c3d2a446d3fafcbd3bf601cbfb0..7fe5d81d4aa2c674029b2387bff6b800875da947 100644 GIT binary patch delta 2821 zcmZ9OS!`BS6opU!QfdS=*u*p@C<-weLk##}h(ZJ;nqZ7PXks)r6~Q(LB`uKnVE#DZ zh}2RX7{mbwzyYx+j>zDEqJoNovw{o?sHoWDlizoHcfIvbPVQQ3@3Zz9_Py=?L#6ed zr2&LxYa(A1n2 zIM^#*mHvXys+-wT*D$&9`i7ZV&JncPb#mN9 z+UlyZwN7S^zl)IdJ{8=rz)v&C&$xBW?~`-um|x_@eCrzTo9D|5@e@*4o>0mYx;hpP z8aA*zW9(UYI%^gQDcv$oJ`i+c?hx@g@Yap9MhCwWe&*G!qk^x8k2-co7I*$m_~|?c zdBk?ES#ELuurBhQT#B*fD5C`S{Y_zU@oL zOs~J4&?Lqjy2op|L(EVg#g^3W0#8-XbM6Lv9qKpd+C5i93iIZIZ)%Mm3vI?N0`8H z*_u88Rx?>+V`@?KLGb3hs5e{7u&8qvf`{`|_h{4>ft_P47P7dyraN#BFT)rTy_F^H zZR2VhA0}-{A$2KuF+)9;@CdlL>tBG_Zz0WO-afxDG3 ze+g`kdYHcq4sYY(?OSThh4;1fUV&IJCcX-G;z~xT55z}cPf0xreGJyfSZG_p>bp1&H{q+g%~!Sj3FS5W=imtp=gGeStC{OD8dHn>9k#fiA(wP5=lLZ;&vj>> zO}E%Ltuo4+KL`=ntM{2rpXh@F(+KY-ObS@kZS z{{A7R{v$)JE0r=F1cskHr{^;)lRd{2*8!AN%sJkRg~Dh@!uNH&Aq9{=WSVH_u;ahjWch j_h^6Qn*SVK#v+Fj{1eUj<{Q`BMIPWutfO=M;Tit|=SzJz delta 1586 zcmZ9MOGuPa7>3XMIvSXS4b;$8Gunhi1(9?CwTNny?OF|HnPNFMYS5y7g6w|Nv5S{V zyW3P|WB06)HtpKAZ&yLk&Ip9wXF5mz=7+<1-}igZ_kHKWpPBT&{q&0Syz)Y0LZ-;f zFcV$H&Ed4@_~?<(bNq`!t#ygI1IC!J$v0PWcq)fK&fz0DoXqi9eSCjBk+hUD-oAx!fugRLmB_4_;;_bGH_gT}PriO-pO?}AnmV|3Eldg>xhJ9)+(h|Nc z>YdzW!bJhTgrGj+l);~GIc4w%dC*T;;sw?Z55#Aij5T=HkV$`ESW)8ts#>m`tm=@# zn3*;35~0I3gX4qZ%cPeo{!BPecxlL(b(XV3GRr@^qXXnFRrXmib(Q zO#17JX0Mi8CxTq6nOhHXZav7kb;qOfbL)NPrbxtziY z;eNG=zCjhyP6{^%Jg0=$>mtyP*>$IdSxfAKw8dI?M)vfW?}Vq_#oQh2)t?ZSJ6(eRR&7@G56 z7XDOVc4mW|cvOR;EWRqdQZqW|hIUPuMZ{bqy~60L_2#PN<2L$|2RB9heyM!{=YTM7 zbl1Xl;b6-*#L@7&meHL5rm$MhR@$u_91`E9dC+>e$y>tG{pl8OOJE@Zm*|dgu<%`R zG`y}bn)BZi#y_C5u28}I!f1Wsp7;Y{v`L-wL(Rs_HRg|k1co&be;dyXC%d*r${b^M zB_0V=g7T^4(B_Em9t5xbiFLBWr@|xlvGDTYm3sf&(Wr2b=9FgE5qBFR=BhmxcGdVN z!rk4!5H9D<5nusDUkXnM)asJquf#F(IqkL8bRg5(8>{h8+My=-oxGLCdi=!6O_Tgy Qh)wAyveu-(Z2Qsr7u{mfQ2+n{ diff --git a/packages/ludic.render3d/shaders/spv/b4587cd4.frag.spv b/packages/ludic.render3d/shaders/spv/b4587cd4.frag.spv index 039ec70fca0c95c59bd9d11cb369ba3f225d0de4..cb196a62d9f9ce93697fca76684b9f336e980466 100644 GIT binary patch delta 2798 zcmZ9N-BVUo6vj8aQiNcUnx-ir`IVMpWuajq8JcFIrI{p>LL}-dphzEd1gGh$oW^FH zX}ajXtA@XzrkU<*CU5Gt%kKJ|>Eri%Ig5@kb2!iQtY@#a_g;IS_s46cZ$2zFl&V|r zOsOg@OtoqH__S+)vvv46Dxd`hV%El3aE#!Km8`1rW7QhF3TG&GCb z!3WEGEBdYqK3W;y1s)q37`Tw-0rD|XDRr%g6BTC9aW#o zXS@vTSYsc}*2~gzh(!%XvirdhTg=O<9(i_vN7%vOJK=ghVG$nfr@(sD>ocyMVh6;; z8*;-}yiM-SHx|jB1{ae*19xSSyc4WPy*|&q8yvZfM{d6&nL(2~iD$tUjDdT=4qTDP zR&q|_Q}7%yzQMf*JJs#gz!128}2A0S7;y8F34|+CN=v71$8I0m5!0Jmm zc23)i+zX$7kzYeokNrLgwt?!g-+kaZ9%GUJR4wQVR&u;T_Z{zal8aIx^4a5c?QSS|c#!TLXA%ePW+7_Rmy zyas*_toA#TzQEhRzlf=y=TXc2X_G{O9Srnm&!92iQSc(zf_lqfdBjG+(eDwk+?cDs z1YVnu8w1PRxx}24EM11&#AHGTYKX}givn+fqxb|^9=`=|gE{d@J&L{qUPIBI{Jy;l t*XLholeJy5B8#3A-^-1B6w)DPxt^eHGQlK|ag#gAE4;Irsr@I${{s{1{t^HH delta 1561 zcmZ9MO-qzf7>18CPFXsYscB#4M`~r3W@&0_X6m$aXjvo@Ey}{Ma2G>Gv?_=eLP+!% zVtzpov@dW~e<51+1x432z9)tm7|wm)&*yp0Iq&;t>CcbT*>tLNYY0`LHq?YmFS1LS ziJB1R-afe3T-XP?ySTD=Cxnm*)uFwD4^{B96@0XUn|XQR`tt4Nl~TUR3i}*cwRXB3t8EH!ZIRRl_@2 zBkzTG!`rZrgVW%isu0E!ZUbuaKP0pP%q6}G$QA4Rmr{A>#Vkv58>{mw=gzB~JFm!F z@ONI38$Vg>8~oIm-;Vqj{Ru;JH??rJbt+HLmo~Lu3}74U&tx5^+e7%*p{?OFMf^2l zvM=+whQbciEPu1=wG*tTx{+4e1^!xAxgP$S4C>KmH~4#*PXqiZC!_u_>9`SWthVQy z^$MX0Vo{AT*&eXlNt{A6TtD^b(*pib=Cc>BpSnJ7W$mnKgA&g<6t1RkA?`pi1{?#&n{tD5J`TUi z>idX=`@rh4@CmSS>M_qraCzZ=xSGC6-9k`|0RvzI);Yg^3Z88-f@$=w~LXyO9phCPTQ{YaLsK=rgz#aJKxeq?eX}Es=qINOS*z|vG fCei#Z&eNoyv{^Lcn+(vwthzDEqJoNovw{o?sHoWDlizoHcfIvbPVQQ3@3Zz9_Py=?L#6ed zr2&LxYa(A1n2 zIM^#*mHvXys+-wT*D$&9`i7ZV&JncPb#mN9 z+UlyZwN7S^zl)IdJ{8=rz)v&C&$xBW?~`-um|x_@eCrzTo9D|5@e@*4o>0mYx;hpP z8aA*zW9(UYI%^gQDcv$oJ`i+c?hx@g@Yap9MhCwWe&*G!qk^x8k2-co7I*$m_~|?c zdBk?ES#ELuurBhQT#B*fD5C`S{Y_zU@oL zOs~J4&?Lqjy2op|L(EVg#g^3W0#8-XbM6Lv9qKpd+C5i93iIZIZ)%Mm3vI?N0`8H z*_u88Rx?>+V`@?KLGb3hs5e{7u&8qvf`{`|_h{4>ft_P47P7dyraN#BFT)rTy_F^H zZR2VhA0}-{A$2KuF+)9;@CdlL>tBG_Zz0WO-afxDG3 ze+g`kdYHcq4sYY(?OSThh4;1fUV&IJCcX-G;z~xT55z}cPf0xreGJyfSZG_p>bp1&H{q+g%~!Sj3FS5W=imtp=gGeStC{OD8dHn>9k#fiA(wP5=lLZ;&vj>> zO}E%Ltuo4+KL`=ntM{2rpXh@F(+KY-ObS@kZS z{{A7R{v$)JE0r=F1cskHr{^;)lRd{2*8!AN%sJkRg~Dh@!uNH&Aq9{=WSVH_u;ahjWch j_h^6Qn*SVK#v+Fj{1eUj<{Q`BMIPWutfO=M;Tit|=SzJz delta 1586 zcmZ9MOGuPa7>3XMIvSXS4b;$8Gunhi1(9?CwTNny?OF|HnPNFMYS5y7g6w|Nv5S{V zyW3P|WB06)HtpKAZ&yLk&Ip9wXF5mz=7+<1-}igZ_kHKWpPBT&{q&0Syz)Y0LZ-;f zFcV$H&Ed4@_~?<(bNq`!t#ygI1IC!J$v0PWcq)fK&fz0DoXqi9eSCjBk+hUD-oAx!fugRLmB_4_;;_bGH_gT}PriO-pO?}AnmV|3Eldg>xhJ9)+(h|Nc z>YdzW!bJhTgrGj+l);~GIc4w%dC*T;;sw?Z55#Aij5T=HkV$`ESW)8ts#>m`tm=@# zn3*;35~0I3gX4qZ%cPeo{!BPecxlL(b(XV3GRr@^qXXnFRrXmib(Q zO#17JX0Mi8CxTq6nOhHXZav7kb;qOfbL)NPrbxtziY z;eNG=zCjhyP6{^%Jg0=$>mtyP*>$IdSxfAKw8dI?M)vfW?}Vq_#oQh2)t?ZSJ6(eRR&7@G56 z7XDOVc4mW|cvOR;EWRqdQZqW|hIUPuMZ{bqy~60L_2#PN<2L$|2RB9heyM!{=YTM7 zbl1Xl;b6-*#L@7&meHL5rm$MhR@$u_91`E9dC+>e$y>tG{pl8OOJE@Zm*|dgu<%`R zG`y}bn)BZi#y_C5u28}I!f1Wsp7;Y{v`L-wL(Rs_HRg|k1co&be;dyXC%d*r${b^M zB_0V=g7T^4(B_Em9t5xbiFLBWr@|xlvGDTYm3sf&(Wr2b=9FgE5qBFR=BhmxcGdVN z!rk4!5H9D<5nusDUkXnM)asJquf#F(IqkL8bRg5(8>{h8+My=-oxGLCdi=!6O_Tgy Qh)wAyveu-(Z2Qsr7u{mfQ2+n{ diff --git a/packages/ludic.render3d/shaders/spv/f4a2b586.frag.spv b/packages/ludic.render3d/shaders/spv/f4a2b586.frag.spv index 1bbfe6adc798c65c8520334f0bf7a3c3a0a6f12d..b52469090c41ec7bc3f77b64c867bc700b275bab 100644 GIT binary patch delta 2821 zcmZ9OS!`BS6on6eTWSzA*u*p@C<-weLk##}h(ZJ;nqZ7PXks)r6~Q(LB`uKnVE#DZ zh+ruW4B~(T;DA^ZM`Un7Q9(t)SwRM+sHoWDlizoHcfIvbPVQQ3@3Zz9_PyzX_kF2?A+ll2?(2V-#`e`Yps#KXC>cuP4WANFtja8(l z!3_=F_&M;UO&4eSiy6Kmo4*d+(lB}Q^xSVNyy(BDH!r8Xy@j;@f83=u)lF);p{Y45 z(9tVimHvXys+-wT*D$&9`i7ZV&JncPb#p`b zwbfN+Yn{v-e-|O^eJZ$LfuClOpK0mYy4n{F z8aA*zW9(UYI%^gQDcv$oJ`i+c?hx@g@cEl&jShY%{LHK8j|#p4KC0i&EbjbW@Y8t? z#_tz!1$bzml+Me!CsJ_B@Fdg!Xx0~k{^YunHx)1)A=eI_;K)+JddmT&trMp@C$1$_w;Vf z7y2Yw%Nb8F`g6-J{wc6;z<7pn-ECj>Sj`HsT2x!vHt~X*@+y>-j5yY3z-Fq4`?KJ1 zH?F%cgL=3>4<1zDtHI_hWYu=!3t)59`_t8GYrJ2L&&U(j^VHloi(rp=JGf}R7VcKU z{3WnC>S6veIJ}LAw{NL27v9&_dj(>_nD{E#i6=3P)pGao{=5b^zBa!hckw!0Q}0Lq z26#oThv%DU;c0vaU+2t|`e{9a3Fjkv4Vt_Kx0o?c#BYgO_o`Fc2)1Z{{+gP<32Xto zdM-X2;O0dBX0RTyk+;EWWhX`f#psTA!1iKep%>ppQxDVkz?#PEvIT68dgQzhE`A?A zfSadI&NW&4eh7&V#8$98J`f*)Jtg%h^f6c?W1(#WtMBGG+=Q>{c3;)vg+MD z{ry8s{YQpcS1M&V3Y^Qlqi7NH83lg=ThQD+Jmp!;%i9Nu3*HNs8+7*vz^egH+z*zI zOx4-({tRA7j)u-N9*Z#+#Sej_cn4S>AN%sJkRg~Dh@!uNH&S$B{=WSVH_u;ahjWch j_h^6Qn*SVK#v+Fj{1eUj<{Q`BMIPiyti5ym;Tit|-Hm-V delta 1586 zcmZ9MOGuPa7>19(jz%S6LP=N6XcH0@MA8M+BC1W(uGL_cDN~M(8nmdNAiLkjv5S{V zyPK8S*gb2cO}lpO+f@*>GXkOana&yh=7+<1-}igZ_kHKWpPux-{q&0Sy~-kE^2`h~ z%}gYUqoI`O_~?<(bNq`!t@ZKx1ICz;DKJ-Z_;3z?oWngCJdr;iY-Tth1aQqQTpwvqL`kM-^a4lCyDkWEKPjTIO>J z(y6a2qF$I=CxTqAnOhHXZav7kb;rx(=hhu({a9*hl|Ret$x9xsZSgx5YS8G^<#Gxq zg!|Mc`UX`*J1HCucuong*F~Tov+GU^vzFKeX^XY)jPP*{VlLNN;WiEHnP<+4U?kv7 z=Y?yN^>y=o(p(bjQXd<2yc`C++LlcS`|_Z>vRz;eVq_#oQh2)t?ZUf-(eRR&7@G56 z7XDOdc4mT{c$o%8S$tJ^rDk-_4egpRi-@^KdW6we>&?~3$8Gc`4{j>;`=s^;oc+SM z(OnDIg@Y~M5J$u7T1IpJo5E^2Q)#zua8P`g<^k*BCT|H#_p>eDmcT*+F3}y~VBx#s zXn0*=H0Qr3jK5!JU8REeh0%J&J@E&^Xp=hUhnkI+{YX@{ERck)&m>+usOH%;<; QAvUF-$Xb*7vh7FfU)UzmPyhe`