From 2454af1c89fad7635a03ff6712e159ca95d9f02f Mon Sep 17 00:00:00 2001 From: emil28092005 Date: Thu, 18 Jun 2026 21:40:43 +0300 Subject: [PATCH] =?UTF-8?q?fix:=20balanced=20shadow=20bias=20=E2=80=94=20n?= =?UTF-8?q?o=20acne,=20no=20peter-panning?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fragment bias: 0.08 + 0.15 * (1 - NdotL) — smaller base, moderate slope (was 0.3 + 0.5 — too much → peter-panning, shadows detached from objects) - vkCmdSetDepthBias: 1.75/0/2.5 (was 2.5/0/3.5 — too aggressive) - Removed early exit logic — was causing hard shadow edges - Always run 16-tap PCF for consistent soft shadow quality - Smaller sample radius: clamp(0.015 * dist/20, 0.003, 0.05) --- .../Shaders/triangle.frag | 21 +++++------------- .../Shaders/triangle.frag.spv | Bin 13820 -> 13124 bytes src/Engine.Graphics.Vulkan/VulkanRenderer.cs | 2 +- 3 files changed, 6 insertions(+), 17 deletions(-) diff --git a/src/Engine.Graphics.Vulkan/Shaders/triangle.frag b/src/Engine.Graphics.Vulkan/Shaders/triangle.frag index ab18a64..8793544 100644 --- a/src/Engine.Graphics.Vulkan/Shaders/triangle.frag +++ b/src/Engine.Graphics.Vulkan/Shaders/triangle.frag @@ -23,7 +23,6 @@ const vec3 AMBIENT = vec3(0.01, 0.01, 0.02); const float PI = 3.14159265359; const float FAR_PLANE = 60.0; -// 16-tap Poisson disk samples (normalized offsets on unit sphere tangent) const vec3 POISSON_DISK[16] = vec3[16]( vec3( 0.0000, 0.0000, 0.0000), vec3( 0.1376, 0.0000, 0.0000), @@ -84,7 +83,6 @@ vec3 acesTonemap(vec3 color) return clamp((color * (a * color + b)) / (color * (c * color + d) + e), 0.0, 1.0); } -// Build a tangent-space basis for the sampling direction mat3 buildTangentBasis(vec3 dir) { vec3 absDir = abs(dir); @@ -100,23 +98,14 @@ float calcShadow(vec3 worldPos, vec3 lightPos, vec3 N, vec3 L) float dist = length(dir); vec3 dirNorm = normalize(dir); - // Base shadow check (center sample) - float closestDepth = texture(shadowCube, dirNorm).r; - float mappedDepth = closestDepth * FAR_PLANE; - float bias = 0.3 + 0.5 * (1.0 - max(dot(N, L), 0.0)); - float baseResult = dist - bias < mappedDepth ? 1.0 : 0.0; + // Slope-dependent bias: small base + larger at grazing angles + float NdotL = max(dot(N, L), 0.0); + float bias = 0.08 + 0.15 * (1.0 - NdotL); - // If clearly lit or clearly shadowed, skip PCF - if (dist - bias * 3.0 < mappedDepth) return 1.0; - if (dist + bias * 3.0 > mappedDepth && baseResult > 0.5) return 1.0; - - // Poisson disk PCF — 16 samples + // 16-tap Poisson disk PCF float shadow = 0.0; mat3 basis = buildTangentBasis(dirNorm); - - // Sample radius scales with distance — softer shadows further from light - float sampleRadius = 0.02 * (dist / 20.0); - sampleRadius = clamp(sampleRadius, 0.005, 0.08); + float sampleRadius = clamp(0.015 * (dist / 20.0), 0.003, 0.05); for (int i = 0; i < 16; i++) { diff --git a/src/Engine.Graphics.Vulkan/Shaders/triangle.frag.spv b/src/Engine.Graphics.Vulkan/Shaders/triangle.frag.spv index e6771f0b3c5f2320b6c5a4871a51e248f8135bd6..715eb7e0b10ab71e5b48a0a0dad4f55498e7043a 100644 GIT binary patch literal 13124 zcmZ9S3A~)+*~Z^wW=<9%2x6B=5n|sX64^*pL~7THX3ja2%>3r|T-SBq&;8ub_Rh?S($ERRl4MA- zS~5IYZ)lRf)=Gw=Bxo&ZJ7?Y@^S1BpYu|p?-F7r!jii*u88bQ=k(6n*Wz~)@9f#pK z3Ofc{ot$eDub+WHC9)^U{rD=V#eNlXt%0w-qqnc8qqV=Uquw=V&XE(3U%bl{ZShW1 z=u@JuB$Q;BBx7io)auJ>eLW{0(zdj-qwP3TlFDpZ z4QtWPZ?E?qp7KhvHtpa%Ba;o$8hMs=^et^v6_sW6tY@w?PbnEoo8>Ge>x1W|dL`L_ zc5wdT$!2JaduqL1wa&auYc#icE6FDK+v>fCF0J)df$7{grQMGmsj#vWz}2=|@1gas z+OleQT3r@bPBsU$)jR7wv=#PwB5iAbM`!z?)vhJAuD+So-j3e9hikeO{`QU@Vn-x9 z*tgo*cF59ddwuz!NvFkG+azL^*LynK7t~#LluEKKZD+@lrF{*plx&9P_HN>x#pPr&*nP?Nx18(~JoT57S@5p@Wohib;PzTqeHpEJT=#6grQ|5^aj9NT zI%o&>x}0?7JgcRgIDzhJPc_R^PWqep2~GT@LA;cl4Uf5;3vR`%=X~0>9A8M=p5u#Y zYbh=#Kc=m=_RePM(>}Mr`@6HXSOxCOwhLb+c@W;(5lR;S6k4`#<>c8E_x8~cT~1!e zeDmu)*?PSUcO7Ehufbb8s`>tvlegf#>7Hl(-_h?mp*7#Tv;*tZl6+u%by;_3?ci#A zM}MA&yUsm#&8>M9IEzWAm}=YV?`p4|P;KqZ)}fN@IuKXu?pvD1?Wva5GpoOqzzWZ% zX^pr8tKHo-mlVB}9D<&mvr76bTHI4z;>4?+ng2-q*}ZH@pHupe7<7Ki$wFePovpR@ zdRDv7GUH|xv_)ux?{Ot*MdM*p?c_<5)l*5@(VFkVVzdPu;1((t`&w3SuTdgeOR6as z`-SfFGu3Bhm%4@fZ;n#vhj-MLFX*Ws3wJ%1;_Idw*K8R$?Yn+P6z&0 zT9WlGCm*8?d}frB3WsiBPx!2b*ZceO(_-FAG6ro{)mjgmc@T+O*jqJU$EVM+QnGH& z=iu{BkH<&D+Gc!ngL%pQ9^q$P`29{H_od%EDL3UD=Ib30&&3yas*>`7JLY$gHJh{v za9?wogZ+4?pQqCcnt6>|kZM_e?^`jyyg9#`{T5-??79uip_Hyq)EsLSIfEB-hGtG* z=5$TRXSBgL&-oZu#Qft~A8Qe>2Wp`mhmEDb`Q`e@!H3hTY4Q;{HfB7LBWcCPY5K>r z60WPdJky3H>*iX-7UMU@=NQJSk7q3Bp(bA+zD3ThP5+j(YFYfSWE*g?7IkeBvFcMX zU%}_5Y{fpoeO5g+djILc7pDGXM({IIeoe!F4*bdM#x?kP@N@5fZ|{h|0KVDj8#VkF z!KaQK+ZgW>_|`{{p0+Oa{RBJ?^JRbettlI*cO`uJy(jOp4m)=Tcw1)W`rL&%7w4yi zes|8p=lUD~`W$&ZAGnZ-VYv5g+4iigwfUXEo-(8C)&Ki@0%U zL!O!0h#L=A(~9G7p7Y2N{U@N)zcH^*!PTOF#BG5_J&m|6;c8J&%x5b!`)kGVC&JA+ zppQwZutG2lt;d*BuN_2q&E1Yk32Hf=WPvajn=m*mHK%Yn!4CJmeBg# z*6+CTrMafRBggh}T<>rfI6hR!Rmh4Y#o{JZdxPflpu z*&E<$VG;Lu+HXap-=E-PZ+~LXIBPG#eO`t|zfaJ~-+1QzC*{}e9sMlU7)@XF8-c#V zov$|T(nz>knCmkN?pnA$uAkiR25Ot~Uq9z#t=9qbPBmlYe&(y~&gZVJ`{m32AENjj z!r0-o{vL|+B0sW;`&%dc?&+c??r)m#+rPcQjrTW2(ce?x`j;2D{ZA@z{a-0?{ihVT z{xb_)zrP`3JpGpzxc_(K!NLju!%oh;KuuVBI-5%=_dYs6MwP5?eFi2V*fXr zxW6Teet%2Ir*b}R56AB_Y*hZ;<_IwFWZ&F3jZ^IzR*ycP1^b*kInVJquv+9e8q7P* zq0bzid-cfidGIm0&pRMD&hzgxQta9H`8b0c|BC>3G9BH$~w6QUj`eaZvT^L)gu06u$uejd{=;Z zCo?g9_VKx(9(}$FwvXICr-F?c#fb9L!MxMIZ|XY@TY$%Uz6RDGc4l7Z8EEF!@0dP& z)MAXYz>X38>tNR+#{3^J?{rLkj%jY|`CqX8jd5Mn&IZrTeWN%pYUiNw=JO--`+dil z7~{O$@86(}^>ff#&!p8C>vI9vy^UHg1bcSQ$>(|z*ckPQxfpDoh`9u8oVsK9tW}FK zE(g2M`YxlDhwnu#_- z_Viw`XW6mLX3V*#;C`A`5BmZdMEXJpUnLLSYPCN z2<$%SdyrN>nN@Y~eh&8EWbC8QnEPneV~)Q7yOzO!3AX;z^V)s|Hby?5#o z>JjrV@TFM9{2OeXy6gHTty+vbgfBG3xeoOU4yGP4C9wUX{xaM+b#s1g>E8tN%_Iv*7 zn7=JY)B4*|&40UfuEBR};**=W|At$PpVh?u&adCKvA&%##|^$q&f|HuYaXNSJgrA9 z;&%tD1>Ym*(SJ|4G3xfWel_E*V=v702=4zg;F`pR7Of3TY8BgXqQ*naxr zxqSdwJJj6=1r=lbHx%R4tzDm5#D50te1abgHYUDT^G{mb zfh1A?g!3pbp6M}gU;h2rGwnHDtAN)AdydzkjdSb22fN?FkIH$}a0J|bu`Wk~)nZ-j zuNLv21*--BT+U;ij)ohf9`iZ|tiBL)U93SZa(o`_Jc2K3;#IhPVvk$F)~6ohwt*L6 z`XWy|*nTm$8rV4XsBJOW@%6dp^5U9r2=}!v7SECO3V3V*ub*T0WdqE;*oZdPz5`s` zi>@Z#+r;~u_z6w?q?|in^B#*?Tkzw+))#lW6Ksrnd>$_Y55Xp4`r}SptG>up2N!d9 z!<}#B{sPz-^~l`=j@mm2MYzysj%sXKpebMJc zuw&TAHIs+$i{QxT`pF~Tm%!$;kG?otUj{pte$R+J&WP*28RlzaEbiqdIUY{CDdt%i zM{6DSS%IksKPBh!UFNHB&v=+;-+rfJ`ize`od$NixF4s3?WZ2U#lHr2FD7C7r((v$ zUY!Zn?|C=&3@pB{oCS7VW8(Y9*TE|=eXfbTxF!?ezQ$v5Z#DW`F&jGuPm1%o_B?nBN2!Yq|#Re8PWi?vI+j1y{QUi<+(j zJMXCJ+hDb@sOdXs_SYXZT@SVfeUb0GVAn44-2he#i+net*2Z+8<-X3jCHP zzN(2onDf|&o8ab+zrWlJw#G81&ok*5>ND8y5wy2qYPVvswzuc{N@DH+--)TM#9Wtq zXz#_;!)`>k_WN?*U9@*&j_Ewqqt2g#)q>v-&g#y-+y4x1-Lo-c^~anZ0PEWqGtT_0 zXw@U%Ltyg-|2cRS7QY=H21kBl^_$1_)EE7J0bW($zXTf-{$GI|Cw?jV= zIbLXw<=W7K_Bh!5z9R0|V6|f3CvvUW_cv(P;w$?87OYn6`@3ATZ=CPngRR>-tt<3D zfUU_I95eJM!H#Es^Qmt^`$x=N_A|zNpceO_1-t|1Ya1-?!K4D-wt%-Q;O)WQl^tp0 zt~>=EmUGYXvjy&5dZEDezud%MYvOMe_}k!j3fz3}7P$F7DDWXH@=z@5F@98mPXcdS z;P&6OiBHRUoUNycaczSC8SMJRXYey%W7Ivnu7_I0KL=Kee*<_P%n<2soabom{Q zpBKT=$GN1ed^M3`KcOs_WXRf*3BYo!b zy!;JQi|-VF2hYv*_+IoT{0&T>&wTs+1A86QXI}SOP3%59Uex;@*g5HQkLCVNVhh^$ zF>6%U@0n1G`u+)a4dZ+KzrboAVNu_|!5?D!;(O44z|PZ1B}4TaZ~;rUoF>0(tbpYyn>m_+~Qc5)xoZd+_hK( z?))S7HgI!Wr)PU>%+HkJ#B2vvGbZwF4_24EM>~L>YkX(e5pIn7`Q-8Z?F4pQeerp| zGuYbn#b?4UVBYCD)o)GSKXu33opv&|XU@&N7uc917|Xxy?2Q?t9?#IJxlVYR(>tpc zbJz#07JEGdtTr992Jf_5#LonKUV_hpdp6^I&xRYL9x?lZ%@d!+`+<#9_cPbEoC8+( zZp7JEpNr;sk9)DdF_`hzr%%oE<5}_y*gCcg`f1EPUcnm4=jGY~wD#AhRy?1crKo*A b*fHXM90XT85R38EB7Onbct1PDes=s9^i}F- literal 13820 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 N?*i-ZnHKv_^Z#v$Hp~D3 diff --git a/src/Engine.Graphics.Vulkan/VulkanRenderer.cs b/src/Engine.Graphics.Vulkan/VulkanRenderer.cs index 402b1ab..fd8552d 100644 --- a/src/Engine.Graphics.Vulkan/VulkanRenderer.cs +++ b/src/Engine.Graphics.Vulkan/VulkanRenderer.cs @@ -255,7 +255,7 @@ internal sealed unsafe class VulkanRenderer : IRenderer, Engine.Graphics.IScreen }; Vk.vkCmdSetScissor(cmd, 0, 1, &shadowScissor); - Vk.vkCmdSetDepthBias(cmd, 2.5f, 0.0f, 3.5f); + Vk.vkCmdSetDepthBias(cmd, 1.75f, 0.0f, 2.5f); foreach (var dc in drawCalls) {