From 2f7430e3b1845f86ddddd88beca318c6090e1080 Mon Sep 17 00:00:00 2001 From: emil28092005 Date: Thu, 18 Jun 2026 21:53:15 +0300 Subject: [PATCH] =?UTF-8?q?fix:=20flip=20normals=20when=20facing=20away=20?= =?UTF-8?q?from=20light/view=20=E2=80=94=20fixes=20cube=20lighting?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Root cause: cube.obj has inconsistent winding order. Many faces have normals pointing inward (e.g. back face normal = +Z instead of -Z). With cullMode=None, both sides render, but inward normals cause: - No diffuse lighting (NdotL < 0 → max() = 0) - Wrong shadow bias (slope-dependent bias uses wrong NdotL) - Inconsistent shadow edges on cube faces Fix: in fragment shader, flip normal if dot(N,L) < 0 or dot(N,V) < 0 This is the standard approach for double-sided rendering with non-watertight geometry or inconsistent winding order. --- imgui.ini | 6 +++--- .../Shaders/triangle.frag | 14 +++++++++++++- .../Shaders/triangle.frag.spv | Bin 13472 -> 13808 bytes 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/imgui.ini b/imgui.ini index b286810..da274ca 100644 --- a/imgui.ini +++ b/imgui.ini @@ -19,12 +19,12 @@ Size=300,425 Collapsed=0 [Window][Cortex Engine Debug] -Pos=60,60 +Pos=1,1 Size=226,196 Collapsed=0 [Window][Shadow Parameters] -Pos=60,60 -Size=318,263 +Pos=772,456 +Size=507,263 Collapsed=0 diff --git a/src/Engine.Graphics.Vulkan/Shaders/triangle.frag b/src/Engine.Graphics.Vulkan/Shaders/triangle.frag index dd9681d..d0ceb73 100644 --- a/src/Engine.Graphics.Vulkan/Shaders/triangle.frag +++ b/src/Engine.Graphics.Vulkan/Shaders/triangle.frag @@ -17,7 +17,7 @@ layout(push_constant) uniform PC { vec4 lightPos; vec4 lightColor; mat4 lightViewProj; - vec4 shadowParams; // x=bias, y=sampleRadius, z=farPlane, w=unused + vec4 shadowParams; } pc; const vec3 AMBIENT = vec3(0.01, 0.01, 0.02); @@ -138,6 +138,18 @@ void main() float dist = length(toLight); vec3 L = toLight / max(dist, 0.001); + // Flip normal if it faces away from light (fixes inward normals from bad winding) + if (dot(N, L) < 0.0) + { + N = -N; + } + + // Also flip for view direction if needed + if (dot(N, V) < 0.0) + { + N = -N; + } + float attenuation = pow(clamp(1.0 - dist / max(lightRange, 0.001), 0.0, 1.0), 2.0); vec3 radiance = lightColor * lightIntensity * attenuation; diff --git a/src/Engine.Graphics.Vulkan/Shaders/triangle.frag.spv b/src/Engine.Graphics.Vulkan/Shaders/triangle.frag.spv index 5751d5be87d3802dabf8eff71a86b949c4793a95..d349aecb67c68d27ea260aafb36bcea0d80adac5 100644 GIT binary patch delta 3777 zcmZ9OX>1i$7>3X7?Y)#$%B><)N^2F1Y}LfXMoJ_g)Wjf$=x=Y@3Js;LrG+2<7>Syg zn2?x@`vM4p3ofNveLAr^*OATy>k`hjX6qWVO||t-zm&^&>!mH6m#u4F z9Pv)^mTYTwfw#Zhhj;bi8+-Aj>lUvGW0P*H@Qh0GCf&OU7r0r|XM{!H>lKvmw)f#5 z_TowRiTF%Ep&h+!`EF+)zN-)4-NS=uCVM2z@X7Y}7^Bti>%~*f!g{Gw+lIGm)R~tVjKD=wkl@@n4H??qhFhWI?ZL)cd4Ed_b z=2a|?4+ER|T_ikd(}5}GcdZVPYT}? z<7IJvB+m%zQpY-nofYC^ zMV|Hck<#$RnaQu#9UGG!#q)knB=7=>pBKJOV`;8zL?*B`y(opyrJ52^9l|eYpp%w= zM>N~NmxLR=jXs8(?G@og-bPQu&9*_9eJ5uZ|9S{!+$7AaiE*Ru@G8F_n}sK7ViY5( zfzr=bBu2hChrozxYva7*}@w#NZZYdUzecEU88{miMw@2w?uCVw|LH8 zZWC_TnC)}CEsSP^he_^tr0H@KH8}9egYT3;MYamyU`5^+wk_g_gPZLGVJ5}ow(AbZ z#$-McUg&Kceef#(N`5RnL_Xvr4m&vl&Qy$6;8S5M&c_7YY@Z3Utpx7W9d5SIh3(Pp z5{BEvz7V#F?G}dn$9lQ@QiOMyVk9LPCyH=jW49t-33uoA7f%bY?iKqsX5BB$USiv) zJG{z2nD2x~s|qpL@Y$m~x=r&3VYb8gkHXC0F5jg;3F9L@=$X>b5=dlyb3hmkXNvGo zfM!cLEX@8I|3#QRwL|V#VSLc7)sG14aupibt5v^eR=-I&q=AzqJt`b8^1C<>$BO(R zj1RgM`BPX|SOh!gjjjAIaq8eh52ZQS{uaS-OoNpk)E&(xaYC5Y!+AYV3gctFa!MG@ z1_va)@{crPv6J_>?r4_xU*Ui)$9P(I1T*Ty4mD0zk|o5?%8qUm$rGk&?etBGbF!k_ zW6T#O78`ZW$REwusdZ_Q_!jpQk8g2-I6mmM#r=hu3(pZdTTF8P+_Bjr1`3}^M6*8IXVsXP$j)7SzrTHsh=0&h?u sw(C-1G=8Ry^BM_F8}Bt-XR9Qi*AqmD#~(YdBQ;ZeZ%^nPTe`UDKm1UxrvLx| delta 3430 zcmZ9OX>3$g6vyv$W=eq;DFc?$+9myBq7m_fX(J^X5NbjYLj0@~TA+5d3x4p!leomh zgoM1hgR;1xxL{S5x)c=^R}dA!6}O@w;DQT^`v1MTr@VGDH}Aikd(M6L-ScKnw5)3` zO6Jv!aW3J;y8>6ex@d7?>Xz)4`H8-8OMWI;lW?vp+t{3KU()3CNxPKWAgz0Owz1>- z9N#G3neEE9dHEl1Ecth zBY4sciqG>4+Bw2YxmQQ=U8DFL!#v2XMb6PyCLLi!Hs}o4lytryz9U7yIGT;~G&NuPL21t2c&YL4#7nmBt+jc_!^?_Gg0U*1Y%?utT*Nod zw5ajo#6)qrTHIve={pY3vbZVYXf~>}iW2dp*6^9eC7!cUXcf6ls&Jo+5L_uk-)MIT zhF66ri;racv~X?|wLn+;%=lPwddxUJJr?KmSe(;ialR=$G^uO~GwCRE$h<9_k*Tbv zec~$Z6X%2BqD+8q*YZeivVbCcKZ5pM8ap&l)9_e#@O=gc|$dR-M03>Uf22Tb$9M9ku}a199}p%6-EL@|FnKIyy)bd>5_-8&grkROMIIDx)xhJ}!OeSEc!l>k zf^hR56<+E+jw9T>$AnjTkJmrEZoc21$3>=V^%Ns1lL=x0^Z~vldP2A*#-9`>@yhVJ z(o9gO?PedImZGn_T;nMLGVv>eZke7H?)03!d``GmW1-LSyfB wxcBO4HX}puua3 zJjAL-5H%YCR%C~;?a6jwxOp!KvnUovJHqi;%uB-E-lH|)bt;edvhW05$;Ui=a%|&) zVp@S$gsnI~fpGI)6J{ToNQcACdtKNb7TpfFg}o_k3#0Slb^bZ-4n#P@6eB5<38KiL zuDdm?$lJo)z43R1Sx}$vqj!agLAPFfPgvim7xC#Ma`TG83oO}Q5#D>4{653${B!+4 zxKb0DghveTEOfie4~6ybW0-#=%njV)7x%F+G3Zuvf5Kf9C9+2QM5aVXFh%z1ie?i& z6hDP$vdPgn&tgTm@UUUs;hV~ zY@U#S$P1NZKMNn%K(~ebBHXKCFWp~-dFi6t_Wvf#Ts#UL(-qAY@w@PujaBRXAtAoA ze~J@?Zae#zu)fjG;@b&4DU4^y{}!g8@qdInHFyf#)M;U2(4*a~mH4j+fADPBcK#=w z3HX+fzZGoj%5Zf>NkGaxDQl!yiKKW!1D#pCI#R+c)%GYYP8_<${^U%VzWBojF#eGMKnXh1iN|!f1R