From 28bc8c79e75dd0fbc60d57586b82de89523ee5f8 Mon Sep 17 00:00:00 2001 From: cosmonaut Date: Thu, 27 Aug 2020 14:46:20 -0700 Subject: [PATCH 01/15] basic directional light shadow mapping --- Effects/DeferredPBREffect.cs | 10 ++++++++ Effects/FXB/DeferredPBREffect.fxb | Bin 53400 -> 54108 bytes Effects/FXB/SimpleDepthEffect.fxb | Bin 624 -> 1008 bytes Effects/HLSL/DeferredPBREffect.fx | 37 +++++++++++++++++++++++++----- Effects/HLSL/SimpleDepthEffect.fx | 14 ++++++++++- Effects/SimpleDepthEffect.cs | 11 +++++++++ Lights/DirectionalLight.cs | 2 +- Renderer.cs | 36 +++++++++++++++++++---------- 8 files changed, 90 insertions(+), 20 deletions(-) diff --git a/Effects/DeferredPBREffect.cs b/Effects/DeferredPBREffect.cs index 97c8615..8b76aca 100644 --- a/Effects/DeferredPBREffect.cs +++ b/Effects/DeferredPBREffect.cs @@ -9,8 +9,11 @@ namespace Kav EffectParameter gAlbedoParam; EffectParameter gNormalParam; EffectParameter gMetallicRoughnessParam; + EffectParameter shadowMapParam; EffectParameter eyePositionParam; + EffectParameter lightSpaceMatrixParam; + PointLightCollection pointLightCollection; DirectionalLightCollection directionalLightCollection; @@ -18,8 +21,10 @@ namespace Kav public Texture2D GAlbedo { get; set; } public Texture2D GNormal { get; set; } public Texture2D GMetallicRoughness { get; set; } + public Texture2D ShadowMap { get; set; } public Vector3 EyePosition { get; set; } + public Matrix LightSpaceMatrix { get; set; } public int MaxPointLights { get; } = 64; @@ -95,8 +100,10 @@ namespace Kav gAlbedoParam.SetValue(GAlbedo); gNormalParam.SetValue(GNormal); gMetallicRoughnessParam.SetValue(GMetallicRoughness); + shadowMapParam.SetValue(ShadowMap); eyePositionParam.SetValue(EyePosition); + lightSpaceMatrixParam.SetValue(LightSpaceMatrix); } void CacheEffectParameters() @@ -105,8 +112,11 @@ namespace Kav gAlbedoParam = Parameters["gAlbedo"]; gNormalParam = Parameters["gNormal"]; gMetallicRoughnessParam = Parameters["gMetallicRoughness"]; + shadowMapParam = Parameters["shadowMap"]; eyePositionParam = Parameters["EyePosition"]; + + lightSpaceMatrixParam = Parameters["LightSpaceMatrix"]; } } } diff --git a/Effects/FXB/DeferredPBREffect.fxb b/Effects/FXB/DeferredPBREffect.fxb index 10298d5149531440477ff239977961028697e518..7bb87291b73782956945627126e1d62215f198ce 100644 GIT binary patch literal 54108 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 Tdst^alLzaK>Rh!xjr#upzD>+; literal 53400 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 mduF_0ZDGxdr|93;I9Z!$+J3gl!}!=6-L~Dk zaO|7Yp?hVw#*RA>oJo0#jXX(5Q4%Y_1G_n7BB}kLqa{FKG0l&Plf%8jU$CFh2jzdmP8DWd zA`wKjiuYth(JU&6a{cgV14UabL@cR?wHWtCjr~SZRGrq)9l4#cwSB&0om6TStJZC{ z?V(k6`<~MSC>3{0;_Xau-T5HZN5V}!3nV7E6(2lwk5|mv7 zq$`lbp-hJVKmcP$OipCF%h)j4o>?^lXg(0}H!v`$OkiPfSir&{x`2~`ff?wE2Mi2P cfZ|V}d?qlT5zc1;^O@j$4lth?$OqX307_0K`Tzg` diff --git a/Effects/HLSL/DeferredPBREffect.fx b/Effects/HLSL/DeferredPBREffect.fx index a49f27a..05a3df7 100644 --- a/Effects/HLSL/DeferredPBREffect.fx +++ b/Effects/HLSL/DeferredPBREffect.fx @@ -8,6 +8,7 @@ DECLARE_TEXTURE(gPosition, 0); DECLARE_TEXTURE(gAlbedo, 1); DECLARE_TEXTURE(gNormal, 2); DECLARE_TEXTURE(gMetallicRoughness, 3); +DECLARE_TEXTURE(shadowMap, 4); // TODO: one map per directional light BEGIN_CONSTANTS @@ -21,6 +22,8 @@ BEGIN_CONSTANTS MATRIX_CONSTANTS + float4x4 LightSpaceMatrix _ps(c137) _cb(c137); + END_CONSTANTS struct PixelInput @@ -71,6 +74,24 @@ float GeometrySmith(float3 N, float3 V, float3 L, float roughness) return ggx1 * ggx2; } +float ComputeShadow(float4 positionLightSpace) +{ + float bias = 0.01; + + // maps to [-1, 1] + float3 projectionCoords = positionLightSpace.xyz / positionLightSpace.w; + + // maps to [0, 1] + projectionCoords = (projectionCoords * 0.5) + 0.5; + + float closestDepth = SAMPLE_TEXTURE(shadowMap, projectionCoords.xy).r; + float currentDepth = projectionCoords.z; + + float shadow = currentDepth - bias > closestDepth ? 1.0 : 0.0; + + return shadow; +} + float3 ComputeLight( float3 lightDir, float3 radiance, @@ -79,7 +100,8 @@ float3 ComputeLight( float3 N, float3 albedo, float metallic, - float roughness + float roughness, + float shadow ) { float3 L = normalize(lightDir); float3 H = normalize(V + L); @@ -98,7 +120,7 @@ float3 ComputeLight( kD *= 1.0 - metallic; float NdotL = max(dot(N, L), 0.0); - return (kD * albedo / PI + specular) * radiance * NdotL; + return (kD * albedo / PI + specular) * radiance * NdotL * shadow; } float4 ComputeColor( @@ -108,6 +130,9 @@ float4 ComputeColor( float metallic, float roughness ) { + float4 positionLightSpace = mul(float4(worldPosition, 1.0), LightSpaceMatrix); + float shadow = ComputeShadow(positionLightSpace); + float3 V = normalize(EyePosition - worldPosition); float3 N = normalize(worldNormal); @@ -124,7 +149,7 @@ float4 ComputeColor( float attenuation = 1.0 / (distance * distance); float3 radiance = PointLightColors[i] * attenuation; - Lo += ComputeLight(lightDir, radiance, F0, V, N, albedo, metallic, roughness); + Lo += ComputeLight(lightDir, radiance, F0, V, N, albedo, metallic, roughness, 1.0); } // directional light @@ -133,7 +158,7 @@ float4 ComputeColor( float3 lightDir = DirectionalLightDirections[i]; float3 radiance = DirectionalLightColors[i]; - Lo += ComputeLight(lightDir, radiance, F0, V, N, albedo, metallic, roughness); + Lo += ComputeLight(lightDir, radiance, F0, V, N, albedo, metallic, roughness, (1.0 - shadow)); } float3 ambient = float3(0.03, 0.03, 0.03) * albedo; // * AO; @@ -148,13 +173,13 @@ float4 ComputeColor( float4 main_ps(PixelInput input) : SV_TARGET0 { - float3 fragPosition = SAMPLE_TEXTURE(gPosition, input.TexCoord).rgb; + float3 worldPosition = SAMPLE_TEXTURE(gPosition, input.TexCoord).rgb; float3 normal = SAMPLE_TEXTURE(gNormal, input.TexCoord).xyz; float3 albedo = SAMPLE_TEXTURE(gAlbedo, input.TexCoord).rgb; float2 metallicRoughness = SAMPLE_TEXTURE(gMetallicRoughness, input.TexCoord).rg; return ComputeColor( - fragPosition, + worldPosition, normal, albedo, metallicRoughness.r, diff --git a/Effects/HLSL/SimpleDepthEffect.fx b/Effects/HLSL/SimpleDepthEffect.fx index 8b19a4e..787ea2a 100644 --- a/Effects/HLSL/SimpleDepthEffect.fx +++ b/Effects/HLSL/SimpleDepthEffect.fx @@ -3,6 +3,8 @@ BEGIN_CONSTANTS float4x4 ModelViewProjection _vs(c0) _cb(c0); + float near _vs(c4) _cb(c4); + float far _vs(c5) _cb(c5); MATRIX_CONSTANTS @@ -16,21 +18,31 @@ struct VertexShaderInput struct VertexShaderOutput { float4 Position : SV_Position; + float Depth : TEXCOORD0; }; VertexShaderOutput main_vs(VertexShaderInput input) { VertexShaderOutput output; - output.Position = mul(float4(input.Position.xyz, 1.0), ModelViewProjection); + output.Position = mul(input.Position, ModelViewProjection); + + output.Depth = output.Position.z / output.Position.w; + output.Depth = (output.Depth * 0.5) + 0.5; return output; } +float4 main_ps(VertexShaderOutput input) : SV_TARGET0 +{ + return float4(input.Depth, 0.0, 0.0, 0.0); +} + Technique SimpleDepth { Pass { VertexShader = compile vs_3_0 main_vs(); + PixelShader = compile ps_3_0 main_ps(); } } diff --git a/Effects/SimpleDepthEffect.cs b/Effects/SimpleDepthEffect.cs index f68f513..c821a6a 100644 --- a/Effects/SimpleDepthEffect.cs +++ b/Effects/SimpleDepthEffect.cs @@ -6,6 +6,8 @@ namespace Kav public class SimpleDepthEffect : Effect { EffectParameter modelViewProjectionParam; + EffectParameter nearParam; + EffectParameter farParam; Matrix model; Matrix view; @@ -42,6 +44,10 @@ namespace Kav dirtyFlags |= EffectDirtyFlags.WorldViewProj; } } + + public float Near { get; set; } + public float Far { get; set; } + public SimpleDepthEffect(GraphicsDevice graphicsDevice) : base(graphicsDevice, Resources.SimpleDepthEffect) { CacheEffectParameters(); @@ -58,11 +64,16 @@ namespace Kav dirtyFlags &= ~EffectDirtyFlags.WorldViewProj; } + + nearParam.SetValue(Near); + farParam.SetValue(Far); } private void CacheEffectParameters() { modelViewProjectionParam = Parameters["ModelViewProjection"]; + nearParam = Parameters["near"]; + farParam = Parameters["far"]; } } } diff --git a/Lights/DirectionalLight.cs b/Lights/DirectionalLight.cs index 31ca4fd..a22ab33 100644 --- a/Lights/DirectionalLight.cs +++ b/Lights/DirectionalLight.cs @@ -12,7 +12,7 @@ namespace Kav { get { - return Matrix.CreateLookAt(-Direction * 100f, Vector3.Zero, Vector3.Up); + return Matrix.CreateLookAt(Direction * 100f, Vector3.Zero, Vector3.Up); } } diff --git a/Renderer.cs b/Renderer.cs index c53f855..35f4b7d 100644 --- a/Renderer.cs +++ b/Renderer.cs @@ -36,7 +36,7 @@ namespace Kav renderDimensionsX, renderDimensionsY, false, - SurfaceFormat.HalfSingle, // unused + SurfaceFormat.Single, DepthFormat.Depth24 ); @@ -105,6 +105,13 @@ namespace Kav IEnumerable pointLights, IEnumerable directionalLights ) { + foreach (var directionalLight in directionalLights) + { + DepthRender(modelTransforms, directionalLight); + DeferredPBREffect.LightSpaceMatrix = directionalLight.View * directionalLight.Projection; + break; // FIXME: this is a kludge + } + GraphicsDevice.SetRenderTargets(GBuffer); GraphicsDevice.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, Color.Black, 1f, 0); GraphicsDevice.DepthStencilState = DepthStencilState.Default; @@ -150,6 +157,7 @@ namespace Kav DeferredPBREffect.GAlbedo = gAlbedo; DeferredPBREffect.GNormal = gNormal; DeferredPBREffect.GMetallicRoughness = gMetallicRoughness; + DeferredPBREffect.ShadowMap = DepthRenderTarget; DeferredPBREffect.EyePosition = Matrix.Invert(camera.View).Translation; int i = 0; @@ -173,23 +181,18 @@ namespace Kav SpriteBatch.End(); } - public void Render( - Camera camera, - IEnumerable<(Model, Matrix)> modelTransforms, - IEnumerable pointLights, - IEnumerable directionalLights - ) { - Render(camera.View, camera.Projection, modelTransforms, pointLights, directionalLights); - } - // for shadow mapping public void DepthRender(IEnumerable<(Model, Matrix)> modelTransforms, DirectionalLight directionalLight) { GraphicsDevice.SetRenderTarget(DepthRenderTarget); - GraphicsDevice.Clear(ClearOptions.DepthBuffer, Color.Black, 1, 0); - + GraphicsDevice.Clear(Color.White); + GraphicsDevice.DepthStencilState = DepthStencilState.Default; + GraphicsDevice.BlendState = BlendState.Opaque; + SimpleDepthEffect.View = directionalLight.View; SimpleDepthEffect.Projection = directionalLight.Projection; + SimpleDepthEffect.Near = 0.1f; // FIXME: this is a kludge + SimpleDepthEffect.Far = 200f; foreach (var (model, transform) in modelTransforms) { @@ -220,6 +223,15 @@ namespace Kav } } + public void Render( + Camera camera, + IEnumerable<(Model, Matrix)> modelTransforms, + IEnumerable pointLights, + IEnumerable directionalLights + ) { + Render(camera.View, camera.Projection, modelTransforms, pointLights, directionalLights); + } + private void Render( Matrix view, Matrix projection, -- 2.25.1 From 3540e098d55e3b8edc4e78504487576e07537ec8 Mon Sep 17 00:00:00 2001 From: cosmonaut Date: Wed, 16 Sep 2020 12:58:23 -0700 Subject: [PATCH 02/15] drawing shadows with fixed projection --- EffectInterfaces/DirectionalLightEffect.cs | 8 --- Effects/DeferredPBREffect.cs | 41 +++++------- Effects/DirectionalLightCollection.cs | 47 -------------- Effects/FXB/DeferredPBREffect.fxb | Bin 54108 -> 52140 bytes Effects/FXB/SimpleDepthEffect.fxb | Bin 1008 -> 848 bytes Effects/HLSL/DeferredPBREffect.fx | 19 +++--- Effects/HLSL/SimpleDepthEffect.fx | 4 -- Effects/PBREffect.cs | 26 +------- Effects/SimpleDepthEffect.cs | 10 --- Renderer.cs | 70 +++++++++++---------- 10 files changed, 59 insertions(+), 166 deletions(-) delete mode 100644 EffectInterfaces/DirectionalLightEffect.cs delete mode 100644 Effects/DirectionalLightCollection.cs diff --git a/EffectInterfaces/DirectionalLightEffect.cs b/EffectInterfaces/DirectionalLightEffect.cs deleted file mode 100644 index 53ba894..0000000 --- a/EffectInterfaces/DirectionalLightEffect.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Kav -{ - public interface DirectionalLightEffect - { - int MaxDirectionalLights { get; } - DirectionalLightCollection DirectionalLights { get; } - } -} diff --git a/Effects/DeferredPBREffect.cs b/Effects/DeferredPBREffect.cs index 8b76aca..390f5f3 100644 --- a/Effects/DeferredPBREffect.cs +++ b/Effects/DeferredPBREffect.cs @@ -11,11 +11,13 @@ namespace Kav EffectParameter gMetallicRoughnessParam; EffectParameter shadowMapParam; + EffectParameter directionalLightColorParam; + EffectParameter directionalLightDirectionParam; + EffectParameter eyePositionParam; EffectParameter lightSpaceMatrixParam; PointLightCollection pointLightCollection; - DirectionalLightCollection directionalLightCollection; public Texture2D GPosition { get; set; } public Texture2D GAlbedo { get; set; } @@ -23,9 +25,12 @@ namespace Kav public Texture2D GMetallicRoughness { get; set; } public Texture2D ShadowMap { get; set; } - public Vector3 EyePosition { get; set; } + public Vector3 DirectionalLightColor { get; set; } + public Vector3 DirectionalLightDirection { get; set; } public Matrix LightSpaceMatrix { get; set; } + public Vector3 EyePosition { get; set; } + public int MaxPointLights { get; } = 64; public PointLightCollection PointLights @@ -34,14 +39,6 @@ namespace Kav private set { pointLightCollection = value; } } - public int MaxDirectionalLights { get; } = 4; - - public DirectionalLightCollection DirectionalLights - { - get { return directionalLightCollection; } - private set { directionalLightCollection = value; } - } - public DeferredPBREffect(GraphicsDevice graphicsDevice) : base(graphicsDevice, Resources.DeferredPBREffect) { CacheEffectParameters(); @@ -51,11 +48,6 @@ namespace Kav Parameters["PointLightColors"], MaxPointLights ); - - DirectionalLights = new DirectionalLightCollection( - Parameters["DirectionalLightDirections"], - Parameters["DirectionalLightColors"] - ); } protected DeferredPBREffect(DeferredPBREffect cloneSource) : base(cloneSource) @@ -77,16 +69,6 @@ namespace Kav { PointLights[i] = cloneSource.PointLights[i]; } - - DirectionalLights = new DirectionalLightCollection( - Parameters["DirectionalLightDirections"], - Parameters["DirectionalLightColors"] - ); - - for (int i = 0; i < MaxDirectionalLights; i++) - { - DirectionalLights[i] = cloneSource.DirectionalLights[i]; - } } public override Effect Clone() @@ -102,6 +84,9 @@ namespace Kav gMetallicRoughnessParam.SetValue(GMetallicRoughness); shadowMapParam.SetValue(ShadowMap); + directionalLightColorParam.SetValue(DirectionalLightColor); + directionalLightDirectionParam.SetValue(DirectionalLightDirection); + eyePositionParam.SetValue(EyePosition); lightSpaceMatrixParam.SetValue(LightSpaceMatrix); } @@ -114,9 +99,11 @@ namespace Kav gMetallicRoughnessParam = Parameters["gMetallicRoughness"]; shadowMapParam = Parameters["shadowMap"]; - eyePositionParam = Parameters["EyePosition"]; - + directionalLightDirectionParam = Parameters["DirectionalLightDirection"]; + directionalLightColorParam = Parameters["DirectionalLightColor"]; lightSpaceMatrixParam = Parameters["LightSpaceMatrix"]; + + eyePositionParam = Parameters["EyePosition"]; } } } diff --git a/Effects/DirectionalLightCollection.cs b/Effects/DirectionalLightCollection.cs deleted file mode 100644 index 10bcbda..0000000 --- a/Effects/DirectionalLightCollection.cs +++ /dev/null @@ -1,47 +0,0 @@ -using Microsoft.Xna.Framework; -using Microsoft.Xna.Framework.Graphics; - -namespace Kav -{ - public class DirectionalLightCollection - { - private readonly Vector3[] directions = new Vector3[4]; - private readonly Vector3[] colors = new Vector3[4]; - private readonly float[] intensities = new float[4]; - - readonly EffectParameter lightDirectionsParam; - readonly EffectParameter lightColorsParam; - - public DirectionalLightCollection(EffectParameter lightDirectionsParam, EffectParameter lightColorsParam) - { - this.lightDirectionsParam = lightDirectionsParam; - this.lightColorsParam = lightColorsParam; - } - - public DirectionalLight this[int i] - { - get - { - var color = colors[i] / intensities[i]; - return new DirectionalLight( - directions[i], - new Color( - color.X, - color.Y, - color.Z, - 1f - ), - intensities[i] - ); - } - set - { - directions[i] = value.Direction; - colors[i] = value.Color.ToVector3() * value.Intensity; - intensities[i] = value.Intensity; - lightDirectionsParam.SetValue(directions); - lightColorsParam.SetValue(colors); - } - } - } -} diff --git a/Effects/FXB/DeferredPBREffect.fxb b/Effects/FXB/DeferredPBREffect.fxb index 7bb87291b73782956945627126e1d62215f198ce..17baa97a597c24b1e7d8cb6a8fef55e8eb125d54 100644 GIT binary patch literal 52140 zcmeI0O>i7X6~}vKN770*k|R681VaQ7;DAYN5+HzMuVn?CAQ{E-1_J?SEv==6l~(M^ z*ybyv*r1Q13RS^@1BZq3fs#0I;6NEuP@Hq%KozNi3srl_fddz+NQ%nx|94N%jwHKW zd~{#@#_rwsUcY|tb@#7lMjQ0D-#pu=^wopU2OrfuEUg$|1p*1%HcbYHglqSp*&sFwJ`TlbnRnxpLW_-HgW5?QY{Laq#NhnXC>AWV^ zj=bN?#ALj^XfxyG#Zt9enL1OuG&4J2ZZyn3aIUS4KWn^O4Y%O`E3^l18#gT{jdOsT znOwd2=KeQkOVhQ>+LaTU-y4P2L z&!^6rIa#aL>UjEg&c|4CX0kTQ_TqDPv{Elm@qMXO)h932(Pz)u5APD^f!oB<)g&?a znB1MbyFt|NL3}R%uNUX9i=JF4O_j$>i}lJC#@vO*_n7TD?CS`h%cJFs<$Aq5Juz}d z?Saldz8A2Mkx!Ajmv>#>kpF#d(M*kP&JvJHSA;@DHeG&3XM!y014;eke zg6-#`CD$Ry=Q8?r$jyv?3-YTO{qK0c*v*%|3_|j zuyG%$;~M%UZ2yE1s+SS3FXZ|Tp&dpG)e>yosqI;9W8P5j1O1-Ywrkki{%{uZ6*RtU zhU$bWsP5nu$bVr&@gYXSrnru@agsUMkVEZZZMcTCjn;m3G_j3o8%Ou47ZO`l+e|HS zVq4d?2!4a=Mq*3PU2+c1`PtkIbY|wc{k45us&9Go+;K`abBG(^-<{&#KHHtGJMKf9 zo!Sd`9wt4q0G)Y#+wH5P z*IxhWvG4!>A4ea%`I@$`X@7OhjEM`i!D}}k!Xpfit{6|Pb;^jfuJQv-D+d`l%*eTn z96=u8V^0krP7C8(O$5pXs)zbgs9H_@ri1=`9H^FVc>cpBkN!3vbzzXms3jTK)_&B@ zIO@jNj$>b_bF||T*TNh;3)0NZ{q8|43Bz;^DJXH~|h55H)yuToCKF+BP z)K%2Avp>>#OfKr|=Q^PF)Q2`U&c*sHtBoet)#|p7bEGflnNuO;VkVZ+X*2$;rx-7* zc9V0p{%csbHPpyC;I!#EaPD>vIDdK$9K(LjfpK#9HhUdl4oc@>KV|wbC)d-gZ7VkM zLmi*%$@pDZqd?bOY?}BX#%+%uY9GeKdtD-%_yN|C{%cqt)6e`xw5|1UjqDzAf0&}Cven%3DP>SUxlVI)Y-O&)8&oXd#EIa0?VKs~e- zn`Z9<+H@U*@afe)WJh6NSg*?U!Nz&@LgyIzK-Oc}Z^oE@t1~bCsoT8^Fu&}+85SbM z-iWzxYv6-xL4VGfIag6fwuU~$WEbn$Nnaj@K&WH7}W#^Nv^8|9R&Rx0&=5OmfHZRXB^O8mGJ>&Dd4xF07@u!4i;n*?9_#MVM$ESp2 z;n*?9_#MMJ$Il4I!m(qH@%x-}j-M5dg=5DY<2PsL9DiCk7LFZryxRlEW5Tg;?3m-X zc;I+kI2Mi_bKLKNiaO{}l0S_F1MmQFZ9do?L1IH7>v2g5|C*fGZsdEofGa4Z}<=6J{h$1e)U z!m(qH-{pbhl5i{>JLdS^9yq=r91F*eIeyp!$5XGtMiqD^yKFt+m%0M4mTk(G?QEZw*-y5u&T$HC(;f@k!uEf` zw&BMx87YPTrGkU1?>!}`T{X5XfYl!%~ zM>96_@t#h3MQz}mywOBo*sJn52gRik`12g~s=viizSRWy`aa*QHkSGj;~d7@`~Hej z7+-7-V?1*#As^#MkR!wou{O-ZbI6)|w5=8If}T?m`V?yDjOQ5Rn@@f1SjOo>JrVs2(F z`n4XZ^(00=;;+Z6*t;Of8>oK7ZN)FE^>hq#(#E_zAAx!uO(9mt##&dgf3%r(HRp(H z$lAyp#~9h{iIF4N&0g8F$2kvR4B1u~W8@HWF_9z4oiVI!6>HFsF)DAy7}<<5vKhnk zrSf^bH|;id1Q*V2dZAz8Y^laOHhWQKJu?%vMrE;5n}_^wnBq?7 literal 54108 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 Tdst^alLzaK>Rh!xjr#upzD>+; diff --git a/Effects/FXB/SimpleDepthEffect.fxb b/Effects/FXB/SimpleDepthEffect.fxb index ab998b21921d45b8c6862de4bb82f4f3d84f72f2..64ee5cd433d0b364a520af23c6edb4c447037029 100644 GIT binary patch delta 129 zcmeyseu0gRk@Nq*0}K<{s#qBr7#IX5Dhf}0W5CEbS(Z`WbOTTX1VAz?z$^v^CLlct z%B}&@6+k>0%AW$HCrqBmc%Cs~aw5~^$v(^_GK>t24}iiA3=FFpfaU_JN&^k@NpQ5ypvZRl&>*5HJHugD6HQ2C-RzI4?D^2*ie|Jp$rD0kT>apjcXB z(ZqUBM&^kpfHVUEi24I&F)%Oz=}A!b1t5I}h$ln&FM#xu z$+C>+8G9zbWxOmA2y`9`1OEb`usu`>n3{Zmsl)(iYy*%7#H$*B768rVc>vVj0CXhK SUPd6B0mNPa!vFs>FaQ8j`X#mi diff --git a/Effects/HLSL/DeferredPBREffect.fx b/Effects/HLSL/DeferredPBREffect.fx index 05a3df7..d6e2d9c 100644 --- a/Effects/HLSL/DeferredPBREffect.fx +++ b/Effects/HLSL/DeferredPBREffect.fx @@ -2,13 +2,12 @@ static const float PI = 3.141592653589793; static const int MAX_POINT_LIGHTS = 64; -static const int MAX_DIRECTIONAL_LIGHTS = 4; DECLARE_TEXTURE(gPosition, 0); DECLARE_TEXTURE(gAlbedo, 1); DECLARE_TEXTURE(gNormal, 2); DECLARE_TEXTURE(gMetallicRoughness, 3); -DECLARE_TEXTURE(shadowMap, 4); // TODO: one map per directional light +DECLARE_TEXTURE(shadowMap, 4); BEGIN_CONSTANTS @@ -17,12 +16,12 @@ BEGIN_CONSTANTS float3 PointLightPositions[MAX_POINT_LIGHTS] _ps(c1) _cb(c1); float3 PointLightColors[MAX_POINT_LIGHTS] _ps(c65) _cb(c65); - float3 DirectionalLightDirections[MAX_DIRECTIONAL_LIGHTS] _ps(c129) _cb(c129); - float3 DirectionalLightColors[MAX_DIRECTIONAL_LIGHTS] _ps(c133) _cb(c133); + float3 DirectionalLightDirection _ps(c129) _cb(c129); + float3 DirectionalLightColor _ps(c130) _cb(c130); MATRIX_CONSTANTS - float4x4 LightSpaceMatrix _ps(c137) _cb(c137); + float4x4 LightSpaceMatrix _ps(c131) _cb(c131); END_CONSTANTS @@ -83,6 +82,7 @@ float ComputeShadow(float4 positionLightSpace) // maps to [0, 1] projectionCoords = (projectionCoords * 0.5) + 0.5; + projectionCoords.y *= -1; float closestDepth = SAMPLE_TEXTURE(shadowMap, projectionCoords.xy).r; float currentDepth = projectionCoords.z; @@ -153,13 +153,10 @@ float4 ComputeColor( } // directional light - for (int i = 0; i < MAX_DIRECTIONAL_LIGHTS; i++) - { - float3 lightDir = DirectionalLightDirections[i]; - float3 radiance = DirectionalLightColors[i]; + float3 lightDir = DirectionalLightDirection; + float3 radiance = DirectionalLightColor; - Lo += ComputeLight(lightDir, radiance, F0, V, N, albedo, metallic, roughness, (1.0 - shadow)); - } + Lo += ComputeLight(lightDir, radiance, F0, V, N, albedo, metallic, roughness, (1.0 - shadow)); float3 ambient = float3(0.03, 0.03, 0.03) * albedo; // * AO; float3 color = ambient + Lo; diff --git a/Effects/HLSL/SimpleDepthEffect.fx b/Effects/HLSL/SimpleDepthEffect.fx index 787ea2a..f813bd7 100644 --- a/Effects/HLSL/SimpleDepthEffect.fx +++ b/Effects/HLSL/SimpleDepthEffect.fx @@ -3,8 +3,6 @@ BEGIN_CONSTANTS float4x4 ModelViewProjection _vs(c0) _cb(c0); - float near _vs(c4) _cb(c4); - float far _vs(c5) _cb(c5); MATRIX_CONSTANTS @@ -26,9 +24,7 @@ VertexShaderOutput main_vs(VertexShaderInput input) VertexShaderOutput output; output.Position = mul(input.Position, ModelViewProjection); - output.Depth = output.Position.z / output.Position.w; - output.Depth = (output.Depth * 0.5) + 0.5; return output; } diff --git a/Effects/PBREffect.cs b/Effects/PBREffect.cs index db04a25..2df74cd 100644 --- a/Effects/PBREffect.cs +++ b/Effects/PBREffect.cs @@ -3,7 +3,7 @@ using Microsoft.Xna.Framework.Graphics; namespace Kav { - public class PBREffect : Effect, TransformEffect, PointLightEffect, DirectionalLightEffect + public class PBREffect : Effect, TransformEffect, PointLightEffect { EffectParameter worldParam; EffectParameter worldViewProjectionParam; @@ -31,7 +31,6 @@ namespace Kav Matrix view = Matrix.Identity; Matrix projection = Matrix.Identity; PointLightCollection pointLightCollection; - DirectionalLightCollection directionalLightCollection; Vector3 albedo; float metallic; @@ -84,14 +83,6 @@ namespace Kav private set { pointLightCollection = value; } } - public int MaxDirectionalLights { get; } = 4; - - public DirectionalLightCollection DirectionalLights - { - get { return directionalLightCollection; } - private set { directionalLightCollection = value; } - } - public Vector3 Albedo { get { return albedo; } @@ -204,11 +195,6 @@ namespace Kav Parameters["PositionLightColors"], MaxPointLights ); - - directionalLightCollection = new DirectionalLightCollection( - Parameters["LightDirections"], - Parameters["DirectionLightColors"] - ); } protected PBREffect(PBREffect cloneSource) : base(cloneSource) @@ -230,16 +216,6 @@ namespace Kav PointLights[i] = cloneSource.PointLights[i]; } - DirectionalLights = new DirectionalLightCollection( - Parameters["LightDirections"], - Parameters["DirectionLightColors"] - ); - - for (int i = 0; i < MaxDirectionalLights; i++) - { - DirectionalLights[i] = cloneSource.DirectionalLights[i]; - } - AlbedoTexture = cloneSource.AlbedoTexture; NormalTexture = cloneSource.NormalTexture; EmissionTexture = cloneSource.EmissionTexture; diff --git a/Effects/SimpleDepthEffect.cs b/Effects/SimpleDepthEffect.cs index c821a6a..9443540 100644 --- a/Effects/SimpleDepthEffect.cs +++ b/Effects/SimpleDepthEffect.cs @@ -6,8 +6,6 @@ namespace Kav public class SimpleDepthEffect : Effect { EffectParameter modelViewProjectionParam; - EffectParameter nearParam; - EffectParameter farParam; Matrix model; Matrix view; @@ -45,9 +43,6 @@ namespace Kav } } - public float Near { get; set; } - public float Far { get; set; } - public SimpleDepthEffect(GraphicsDevice graphicsDevice) : base(graphicsDevice, Resources.SimpleDepthEffect) { CacheEffectParameters(); @@ -64,16 +59,11 @@ namespace Kav dirtyFlags &= ~EffectDirtyFlags.WorldViewProj; } - - nearParam.SetValue(Near); - farParam.SetValue(Far); } private void CacheEffectParameters() { modelViewProjectionParam = Parameters["ModelViewProjection"]; - nearParam = Parameters["near"]; - farParam = Parameters["far"]; } } } diff --git a/Renderer.cs b/Renderer.cs index 35f4b7d..99c26d3 100644 --- a/Renderer.cs +++ b/Renderer.cs @@ -10,7 +10,7 @@ namespace Kav private int RenderDimensionsX { get; } private int RenderDimensionsY { get; } - private RenderTarget2D DepthRenderTarget { get; } + private RenderTarget2D ShadowRenderTarget { get; } private SimpleDepthEffect SimpleDepthEffect { get; } private RenderTarget2D gPosition { get; } @@ -31,10 +31,10 @@ namespace Kav RenderDimensionsX = renderDimensionsX; RenderDimensionsY = renderDimensionsY; - DepthRenderTarget = new RenderTarget2D( + ShadowRenderTarget = new RenderTarget2D( GraphicsDevice, - renderDimensionsX, - renderDimensionsY, + 1024, + 1024, false, SurfaceFormat.Single, DepthFormat.Depth24 @@ -103,14 +103,9 @@ namespace Kav Camera camera, IEnumerable<(Model, Matrix)> modelTransforms, IEnumerable pointLights, - IEnumerable directionalLights + DirectionalLight directionalLight ) { - foreach (var directionalLight in directionalLights) - { - DepthRender(modelTransforms, directionalLight); - DeferredPBREffect.LightSpaceMatrix = directionalLight.View * directionalLight.Projection; - break; // FIXME: this is a kludge - } + ShadowMapRender(camera, modelTransforms, directionalLight); GraphicsDevice.SetRenderTargets(GBuffer); GraphicsDevice.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, Color.Black, 1f, 0); @@ -157,7 +152,7 @@ namespace Kav DeferredPBREffect.GAlbedo = gAlbedo; DeferredPBREffect.GNormal = gNormal; DeferredPBREffect.GMetallicRoughness = gMetallicRoughness; - DeferredPBREffect.ShadowMap = DepthRenderTarget; + DeferredPBREffect.ShadowMap = ShadowRenderTarget; DeferredPBREffect.EyePosition = Matrix.Invert(camera.View).Translation; int i = 0; @@ -168,13 +163,8 @@ namespace Kav i++; } - i = 0; - foreach (var directionalLight in directionalLights) - { - if (i > DeferredPBREffect.MaxDirectionalLights) { break; } - DeferredPBREffect.DirectionalLights[i] = directionalLight; - i++; - } + DeferredPBREffect.DirectionalLightColor = directionalLight.Color.ToVector3() * directionalLight.Intensity; + DeferredPBREffect.DirectionalLightDirection = directionalLight.Direction; SpriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, null, null, null, DeferredPBREffect); SpriteBatch.Draw(deferredRenderTarget, Vector2.Zero, Color.White); @@ -182,17 +172,40 @@ namespace Kav } // for shadow mapping - public void DepthRender(IEnumerable<(Model, Matrix)> modelTransforms, DirectionalLight directionalLight) + public void ShadowMapRender(Camera camera, IEnumerable<(Model, Matrix)> modelTransforms, DirectionalLight directionalLight) { - GraphicsDevice.SetRenderTarget(DepthRenderTarget); + GraphicsDevice.SetRenderTarget(ShadowRenderTarget); GraphicsDevice.Clear(Color.White); GraphicsDevice.DepthStencilState = DepthStencilState.Default; GraphicsDevice.BlendState = BlendState.Opaque; + var right = Vector3.Cross(Vector3.Up, directionalLight.Direction); + var up = Vector3.Cross(directionalLight.Direction, right); + + var lightRotation = Matrix.CreateLookAt(Vector3.Zero, -directionalLight.Direction, up); + + var cameraBoundingFrustum = new BoundingFrustum(camera.View * camera.Projection); + + Vector3[] frustumCorners = cameraBoundingFrustum.GetCorners(); + for (var i = 0; i < frustumCorners.Length; i++) + { + frustumCorners[i] = Vector3.Transform(frustumCorners[i], lightRotation); + } + + BoundingBox lightBox = BoundingBox.CreateFromPoints(frustumCorners); + Vector3 boxSize = lightBox.Max - lightBox.Min; + Vector3 halfBoxSize = boxSize * 0.5f; + + Vector3 lightPosition = lightBox.Min + halfBoxSize; + lightPosition.Z = lightBox.Min.Z; + lightPosition = Vector3.Transform(lightPosition, Matrix.Invert(lightRotation)); + + //SimpleDepthEffect.View = Matrix.CreateLookAt(lightPosition, lightPosition - directionalLight.Direction, up); + //SimpleDepthEffect.Projection = Matrix.CreateOrthographic(boxSize.X, boxSize.Y, -boxSize.Z, boxSize.Z); + SimpleDepthEffect.View = directionalLight.View; SimpleDepthEffect.Projection = directionalLight.Projection; - SimpleDepthEffect.Near = 0.1f; // FIXME: this is a kludge - SimpleDepthEffect.Far = 200f; + DeferredPBREffect.LightSpaceMatrix = SimpleDepthEffect.View * SimpleDepthEffect.Projection; foreach (var (model, transform) in modelTransforms) { @@ -266,17 +279,6 @@ namespace Kav } } - if (meshPart.Effect is DirectionalLightEffect directionalLightEffect) - { - int i = 0; - foreach (var directionalLight in directionalLights) - { - if (i > directionalLightEffect.MaxDirectionalLights) { break; } - directionalLightEffect.DirectionalLights[i] = directionalLight; - i++; - } - } - foreach (var pass in meshPart.Effect.CurrentTechnique.Passes) { pass.Apply(); -- 2.25.1 From 345ffaa247854a29670e264240500fa11322fe17 Mon Sep 17 00:00:00 2001 From: cosmonaut Date: Wed, 16 Sep 2020 16:49:08 -0700 Subject: [PATCH 03/15] fix fragment depth being translated to clip space --- Effects/FXB/DeferredPBREffect.fxb | Bin 52140 -> 52160 bytes Effects/HLSL/DeferredPBREffect.fx | 16 +++++++++---- Effects/SimpleDepthEffect.cs | 4 ++-- Renderer.cs | 37 +++++++++++++++++++----------- 4 files changed, 37 insertions(+), 20 deletions(-) diff --git a/Effects/FXB/DeferredPBREffect.fxb b/Effects/FXB/DeferredPBREffect.fxb index 17baa97a597c24b1e7d8cb6a8fef55e8eb125d54..a9961cf37ec65f66aecf53a97bebf2335aa4df03 100644 GIT binary patch delta 2588 zcma*pQAkr!7zgn0o_p1;`yjZ7i4uz-7%WZ5VzQVOBNc;C7!FMcK^P2C31TsjI}E*Q zA$5XD5)L66TZC~z)S;+}bNbM$;e#(eEPUumP<*(aGkpKP3kSaQzu);i|9_;^&6K)y zs4Gp@U)@vHJX|xr)&)>81=Z>Ci^bsV_SvI*Yo#EFPv+Ai7o)fvt>Qek?YAN8t}CP| zCLeXX)M>E!q}!u__1*n}xlORs5dcjA#n8Yu6!Au`{MnT&vAU1Q3Xj!2u%b6#B5T&A zha{cfMx~iMB+2xmQoWBP)i$D1u8AaN?xRxRAxZMLpwdBrB<%%JsqYC%iiA+<^chJi zb)Zt>IZ2AYKqbAKBss#UG~Yv#=6X@d*H4nXuTUvJK$2DlQK>UZlG%?@=i*OOpJbQ0d?^N!m-HQr{Pn z6#0rurwb&hw1`THZzL(6K_xv$k{mx#X?}?$%~`1ATP8`~RaDC7Nz%#&Ds^s=r1qT= zq^t1%yA9l5xaE|c=mwk^6TGY78ma_fGLa3b0zhS;mS-k5wCe`NAY0C4tCE@Ak^fD| zrsWZD83RpZ;p9_cIH6k1B|~$Zq?x#1`$?zutDrN{*w%}la$`F#iaN&aVw)1?ZTkr& J%YX0xy8^)KDv$sG delta 2604 zcma*p-%C?r7zgm@eRs@l2Nc@H5q~f!guyI>eqc;y%BY2b7z|fSkRTY0$Pi>Ph%>C* zW@2@MNfI4|aBL#NflvohQT~`0(N#uQ!7dgCT?ziUIKAim1J85u!t=cE_x)UdKjs~a zdB^gu%vz0~vbX+1`1et85TGs#jk;+*3QZi&2WLL-cXzJU?E9R!2Y?`e+9S@D2PY@i zj1EsoVma-h;&EgFv`NLwBF2I;&6OOOdK5rnz*GrLm5D!AyN^?UbY4$cM`G-l8Zv>UfcS+L9JyhzClcaD0m44kP zNxP${lo%sP@o`i-PLia96e>+UCP|qzDz!WzNllZeRLYX1!gEv#%#ft6SE#h}nj~%I zQ7QI@Bt_q%(%w6gRGUYo)O(VYETEEHB1z6ARGM2RNwWqj`9F{(-zqAV%Oq)K9hLe& zk)-gKQKYMA|2EDjRv&UhRec6k{R9BFcF~PAWwZSj2g!bWD&Fb<931NH-o!;-5?#tA zc5BW+QP04UF8*;BI8_%!(tRTDhDI^}RV8Ot=VG^P7ZlTO closestDepth ? 1.0 : 0.0; - - return shadow; + if (currentDepth - bias > closestDepth) + { + return 1.0; + } + else + { + return 0.0; + } } float3 ComputeLight( diff --git a/Effects/SimpleDepthEffect.cs b/Effects/SimpleDepthEffect.cs index 9443540..1854e3c 100644 --- a/Effects/SimpleDepthEffect.cs +++ b/Effects/SimpleDepthEffect.cs @@ -52,8 +52,8 @@ namespace Kav { if ((dirtyFlags & EffectDirtyFlags.WorldViewProj) != 0) { - Matrix.Multiply(ref model, ref view, out Matrix modelView); - Matrix.Multiply(ref modelView, ref projection, out Matrix worldViewProj); + Matrix.Multiply(ref view, ref projection, out Matrix viewProjection); + Matrix.Multiply(ref model, ref viewProjection, out Matrix worldViewProj); modelViewProjectionParam.SetValue(worldViewProj); diff --git a/Renderer.cs b/Renderer.cs index 99c26d3..058a383 100644 --- a/Renderer.cs +++ b/Renderer.cs @@ -181,32 +181,43 @@ namespace Kav var right = Vector3.Cross(Vector3.Up, directionalLight.Direction); var up = Vector3.Cross(directionalLight.Direction, right); - - var lightRotation = Matrix.CreateLookAt(Vector3.Zero, -directionalLight.Direction, up); - var cameraBoundingFrustum = new BoundingFrustum(camera.View * camera.Projection); - Vector3[] frustumCorners = cameraBoundingFrustum.GetCorners(); + + Vector3 frustumCenter = Vector3.Zero; for (var i = 0; i < frustumCorners.Length; i++) { - frustumCorners[i] = Vector3.Transform(frustumCorners[i], lightRotation); + frustumCenter += frustumCorners[i]; + } + frustumCenter /= 8f; + + var lightView = Matrix.CreateLookAt(frustumCenter, frustumCenter - directionalLight.Direction, camera.View.Right); + + for (var i = 0; i < frustumCorners.Length; i++) + { + frustumCorners[i] = Vector3.Transform(frustumCorners[i], lightView); } BoundingBox lightBox = BoundingBox.CreateFromPoints(frustumCorners); Vector3 boxSize = lightBox.Max - lightBox.Min; Vector3 halfBoxSize = boxSize * 0.5f; - Vector3 lightPosition = lightBox.Min + halfBoxSize; - lightPosition.Z = lightBox.Min.Z; - lightPosition = Vector3.Transform(lightPosition, Matrix.Invert(lightRotation)); + Vector3 lightPosition = frustumCenter + directionalLight.Direction * -lightBox.Min.Z; + //lightPosition.Z = lightBox.Min.Z; + //lightPosition = Vector3.Transform(lightPosition, Matrix.Invert(lightRotation)); + + SimpleDepthEffect.View = Matrix.CreateLookAt(lightPosition, frustumCenter, camera.View.Right); + SimpleDepthEffect.Projection = Matrix.CreateOrthographicOffCenter(lightBox.Min.X, lightBox.Max.X, lightBox.Min.Y, lightBox.Max.Y, 0, lightBox.Max.Z - lightBox.Min.Z); - //SimpleDepthEffect.View = Matrix.CreateLookAt(lightPosition, lightPosition - directionalLight.Direction, up); - //SimpleDepthEffect.Projection = Matrix.CreateOrthographic(boxSize.X, boxSize.Y, -boxSize.Z, boxSize.Z); - - SimpleDepthEffect.View = directionalLight.View; - SimpleDepthEffect.Projection = directionalLight.Projection; + //SimpleDepthEffect.View = directionalLight.View; + //SimpleDepthEffect.Projection = directionalLight.Projection; DeferredPBREffect.LightSpaceMatrix = SimpleDepthEffect.View * SimpleDepthEffect.Projection; + var globalShadowView = Matrix.CreateLookAt(frustumCenter + directionalLight.Direction * -0.5f, frustumCenter, camera.View.Right); + var globalShadowProjection = Matrix.CreateOrthographic(1f, 1f, 0f, 1f); + //var texScaleBias = Matrix.CreateScale(0.5f, -0.5f, 1f) * Matrix.CreateTranslation(0.5f, 0.5f, 0f); + //DeferredPBREffect.LightSpaceMatrix = globalShadowView * globalShadowProjection; // * texScaleBias; + foreach (var (model, transform) in modelTransforms) { foreach (var modelMesh in model.Meshes) -- 2.25.1 From 7062992d9b5a6b64277e5bb2366be87b109232af Mon Sep 17 00:00:00 2001 From: cosmonaut Date: Wed, 16 Sep 2020 16:58:21 -0700 Subject: [PATCH 04/15] dynamic shadow bias --- Effects/FXB/DeferredPBREffect.fxb | Bin 52160 -> 52564 bytes Effects/HLSL/DeferredPBREffect.fx | 21 +++++++++++---------- Renderer.cs | 23 ++++++++--------------- 3 files changed, 19 insertions(+), 25 deletions(-) diff --git a/Effects/FXB/DeferredPBREffect.fxb b/Effects/FXB/DeferredPBREffect.fxb index a9961cf37ec65f66aecf53a97bebf2335aa4df03..5934933a7e19ec1cde2840c6542e7438fa47a5c7 100644 GIT binary patch literal 52564 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 wzF6z*qHh6h_C&pLpdoq6wLTB|-!Hs+2LJ#7 diff --git a/Effects/HLSL/DeferredPBREffect.fx b/Effects/HLSL/DeferredPBREffect.fx index 65c1eb9..c64a9f2 100644 --- a/Effects/HLSL/DeferredPBREffect.fx +++ b/Effects/HLSL/DeferredPBREffect.fx @@ -73,9 +73,10 @@ float GeometrySmith(float3 N, float3 V, float3 L, float roughness) return ggx1 * ggx2; } -float ComputeShadow(float4 positionLightSpace) +float ComputeShadow(float4 positionLightSpace, float3 N, float L) { - float bias = 0.001; //0.005; + float bias = 0.005 * tan(acos(dot(N, L))); + bias = clamp(bias, 0, 0.01); // maps to [-1, 1] float3 projectionCoords = positionLightSpace.xyz / positionLightSpace.w; @@ -99,7 +100,7 @@ float ComputeShadow(float4 positionLightSpace) } float3 ComputeLight( - float3 lightDir, + float3 L, float3 radiance, float3 F0, float3 V, @@ -109,7 +110,6 @@ float3 ComputeLight( float roughness, float shadow ) { - float3 L = normalize(lightDir); float3 H = normalize(V + L); float NDF = DistributionGGX(N, H, roughness); @@ -136,9 +136,6 @@ float4 ComputeColor( float metallic, float roughness ) { - float4 positionLightSpace = mul(float4(worldPosition, 1.0), LightSpaceMatrix); - float shadow = ComputeShadow(positionLightSpace); - float3 V = normalize(EyePosition - worldPosition); float3 N = normalize(worldNormal); @@ -151,18 +148,22 @@ float4 ComputeColor( for (int i = 0; i < MAX_POINT_LIGHTS; i++) { float3 lightDir = PointLightPositions[i] - worldPosition; + float3 L = normalize(lightDir); float distance = length(lightDir); float attenuation = 1.0 / (distance * distance); float3 radiance = PointLightColors[i] * attenuation; - Lo += ComputeLight(lightDir, radiance, F0, V, N, albedo, metallic, roughness, 1.0); + Lo += ComputeLight(L, radiance, F0, V, N, albedo, metallic, roughness, 1.0); } // directional light - float3 lightDir = DirectionalLightDirection; + float3 L = normalize(DirectionalLightDirection); float3 radiance = DirectionalLightColor; - Lo += ComputeLight(lightDir, radiance, F0, V, N, albedo, metallic, roughness, (1.0 - shadow)); + float4 positionLightSpace = mul(float4(worldPosition, 1.0), LightSpaceMatrix); + float shadow = ComputeShadow(positionLightSpace, N, L); + + Lo += ComputeLight(L, radiance, F0, V, N, albedo, metallic, roughness, (1.0 - shadow)); float3 ambient = float3(0.03, 0.03, 0.03) * albedo; // * AO; float3 color = ambient + Lo; diff --git a/Renderer.cs b/Renderer.cs index 058a383..d5d136f 100644 --- a/Renderer.cs +++ b/Renderer.cs @@ -171,7 +171,6 @@ namespace Kav SpriteBatch.End(); } - // for shadow mapping public void ShadowMapRender(Camera camera, IEnumerable<(Model, Matrix)> modelTransforms, DirectionalLight directionalLight) { GraphicsDevice.SetRenderTarget(ShadowRenderTarget); @@ -199,24 +198,18 @@ namespace Kav } BoundingBox lightBox = BoundingBox.CreateFromPoints(frustumCorners); - Vector3 boxSize = lightBox.Max - lightBox.Min; - Vector3 halfBoxSize = boxSize * 0.5f; - Vector3 lightPosition = frustumCenter + directionalLight.Direction * -lightBox.Min.Z; - //lightPosition.Z = lightBox.Min.Z; - //lightPosition = Vector3.Transform(lightPosition, Matrix.Invert(lightRotation)); SimpleDepthEffect.View = Matrix.CreateLookAt(lightPosition, frustumCenter, camera.View.Right); - SimpleDepthEffect.Projection = Matrix.CreateOrthographicOffCenter(lightBox.Min.X, lightBox.Max.X, lightBox.Min.Y, lightBox.Max.Y, 0, lightBox.Max.Z - lightBox.Min.Z); - - //SimpleDepthEffect.View = directionalLight.View; - //SimpleDepthEffect.Projection = directionalLight.Projection; + SimpleDepthEffect.Projection = Matrix.CreateOrthographicOffCenter( + lightBox.Min.X, + lightBox.Max.X, + lightBox.Min.Y, + lightBox.Max.Y, + 0, + lightBox.Max.Z - lightBox.Min.Z + ); DeferredPBREffect.LightSpaceMatrix = SimpleDepthEffect.View * SimpleDepthEffect.Projection; - - var globalShadowView = Matrix.CreateLookAt(frustumCenter + directionalLight.Direction * -0.5f, frustumCenter, camera.View.Right); - var globalShadowProjection = Matrix.CreateOrthographic(1f, 1f, 0f, 1f); - //var texScaleBias = Matrix.CreateScale(0.5f, -0.5f, 1f) * Matrix.CreateTranslation(0.5f, 0.5f, 0f); - //DeferredPBREffect.LightSpaceMatrix = globalShadowView * globalShadowProjection; // * texScaleBias; foreach (var (model, transform) in modelTransforms) { -- 2.25.1 From 10f3d9cd98b53f16bc39ab7fa56a7bff93558da0 Mon Sep 17 00:00:00 2001 From: cosmonaut Date: Thu, 17 Sep 2020 15:47:23 -0700 Subject: [PATCH 05/15] single triangle renderer optimization --- Effects/FXB/DeferredPBREffect.fxb | Bin 52564 -> 52792 bytes Effects/HLSL/DeferredPBREffect.fx | 17 +++++ Renderer.cs | 102 +++++++++++++++++++++++++++--- 3 files changed, 110 insertions(+), 9 deletions(-) diff --git a/Effects/FXB/DeferredPBREffect.fxb b/Effects/FXB/DeferredPBREffect.fxb index 5934933a7e19ec1cde2840c6542e7438fa47a5c7..f06f5aabc8421dd6bd589e0c45af7e7fb941028d 100644 GIT binary patch delta 259 zcmcaIi+RT!W>!Yd|Nn9}vTAcN3QTrn6lUa^tjlH2%E7?Ez`Hq`5R&AkYAcsQ~e02$#VD$hHAuRv@2!@=R`XM#jw-xz8VPgvvAg2Lc#-35db` z@BhF5A`H$Uj!rT_t};{%L_vW9NW5(FTp@iAc?JfM?F|eJ{1ZTIpfwGQP&QQj0YaSl c0nph%_52SQ7@kZ3u^AaaVoxSao^xgg01@yk^#A|> delta 98 zcmdlnhxy7ZW>!Yd|NlZZvTAcN@=tbT6rY^HCBw?jz`($>xsi*RQP+hF$OZyNAhtnb jPX>!JFlYe9RDhTj$Y+~;klUP*Ve?P!^9LrkoZSKdqNx(O diff --git a/Effects/HLSL/DeferredPBREffect.fx b/Effects/HLSL/DeferredPBREffect.fx index c64a9f2..2f9f3a1 100644 --- a/Effects/HLSL/DeferredPBREffect.fx +++ b/Effects/HLSL/DeferredPBREffect.fx @@ -25,12 +25,28 @@ MATRIX_CONSTANTS END_CONSTANTS +struct VertexInput +{ + float4 Position : POSITION; + float2 TexCoord : TEXCOORD; +}; + struct PixelInput { float4 Position : SV_POSITION; float2 TexCoord : TEXCOORD0; }; +PixelInput main_vs(VertexInput input) +{ + PixelInput output; + + output.Position = input.Position; + output.TexCoord = input.TexCoord; + + return output; +} + // Pixel Shader float3 FresnelSchlick(float cosTheta, float3 F0) @@ -195,6 +211,7 @@ Technique DeferredPBR { Pass { + VertexShader = compile vs_3_0 main_vs(); PixelShader = compile ps_3_0 main_ps(); } } diff --git a/Renderer.cs b/Renderer.cs index d5d136f..210aed8 100644 --- a/Renderer.cs +++ b/Renderer.cs @@ -10,7 +10,10 @@ namespace Kav private int RenderDimensionsX { get; } private int RenderDimensionsY { get; } + private VertexBuffer FullscreenTriangle { get; } private RenderTarget2D ShadowRenderTarget { get; } + + private DeferredPBREffect DeferredPBREffect { get; } private SimpleDepthEffect SimpleDepthEffect { get; } private RenderTarget2D gPosition { get; } @@ -21,8 +24,6 @@ namespace Kav private RenderTargetBinding[] GBuffer { get; } - private DeferredPBREffect DeferredPBREffect { get; } - private SpriteBatch SpriteBatch { get; } public Renderer(GraphicsDevice graphicsDevice, int renderDimensionsX, int renderDimensionsY) @@ -92,6 +93,13 @@ namespace Kav SimpleDepthEffect = new SimpleDepthEffect(GraphicsDevice); DeferredPBREffect = new DeferredPBREffect(GraphicsDevice); + FullscreenTriangle = new VertexBuffer(GraphicsDevice, typeof(VertexPositionTexture), 3, BufferUsage.WriteOnly); + FullscreenTriangle.SetData(new VertexPositionTexture[3] { + new VertexPositionTexture(new Vector3(-1, -1, 0), new Vector2(0, 0)), + new VertexPositionTexture(new Vector3(-1, 3, 0), new Vector2(0, -2)), + new VertexPositionTexture(new Vector3(3, -1, 0), new Vector2(2, 0)) + }); + SpriteBatch = new SpriteBatch(GraphicsDevice); GraphicsDevice.SetRenderTarget(deferredRenderTarget); @@ -103,9 +111,10 @@ namespace Kav Camera camera, IEnumerable<(Model, Matrix)> modelTransforms, IEnumerable pointLights, - DirectionalLight directionalLight + DirectionalLight directionalLight, + int numCascades ) { - ShadowMapRender(camera, modelTransforms, directionalLight); + ShadowMapRender(camera, modelTransforms, directionalLight, numCascades); GraphicsDevice.SetRenderTargets(GBuffer); GraphicsDevice.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, Color.Black, 1f, 0); @@ -166,13 +175,25 @@ namespace Kav DeferredPBREffect.DirectionalLightColor = directionalLight.Color.ToVector3() * directionalLight.Intensity; DeferredPBREffect.DirectionalLightDirection = directionalLight.Direction; - SpriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, null, null, null, DeferredPBREffect); - SpriteBatch.Draw(deferredRenderTarget, Vector2.Zero, Color.White); - SpriteBatch.End(); + + foreach (EffectPass pass in DeferredPBREffect.CurrentTechnique.Passes) + { + pass.Apply(); + GraphicsDevice.SetVertexBuffer(FullscreenTriangle); + GraphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, 1); + } + + // SpriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, null, null, null, DeferredPBREffect); + // SpriteBatch.Draw(deferredRenderTarget, Vector2.Zero, Color.White); + // SpriteBatch.End(); } - public void ShadowMapRender(Camera camera, IEnumerable<(Model, Matrix)> modelTransforms, DirectionalLight directionalLight) - { + public void ShadowMapRender( + Camera camera, + IEnumerable<(Model, Matrix)> modelTransforms, + DirectionalLight directionalLight, + int numCascades + ) { GraphicsDevice.SetRenderTarget(ShadowRenderTarget); GraphicsDevice.Clear(Color.White); GraphicsDevice.DepthStencilState = DepthStencilState.Default; @@ -240,6 +261,69 @@ namespace Kav } } + private void RenderShadowMap(Camera camera, IEnumerable<(Model, Matrix)> modelTransforms, DirectionalLight directionalLight) + { + var right = Vector3.Cross(Vector3.Up, directionalLight.Direction); + var up = Vector3.Cross(directionalLight.Direction, right); + var cameraBoundingFrustum = new BoundingFrustum(camera.View * camera.Projection); + Vector3[] frustumCorners = cameraBoundingFrustum.GetCorners(); + + Vector3 frustumCenter = Vector3.Zero; + for (var i = 0; i < frustumCorners.Length; i++) + { + frustumCenter += frustumCorners[i]; + } + frustumCenter /= 8f; + + var lightView = Matrix.CreateLookAt(frustumCenter, frustumCenter - directionalLight.Direction, camera.View.Right); + + for (var i = 0; i < frustumCorners.Length; i++) + { + frustumCorners[i] = Vector3.Transform(frustumCorners[i], lightView); + } + + BoundingBox lightBox = BoundingBox.CreateFromPoints(frustumCorners); + Vector3 lightPosition = frustumCenter + directionalLight.Direction * -lightBox.Min.Z; + + SimpleDepthEffect.View = Matrix.CreateLookAt(lightPosition, frustumCenter, camera.View.Right); + SimpleDepthEffect.Projection = Matrix.CreateOrthographicOffCenter( + lightBox.Min.X, + lightBox.Max.X, + lightBox.Min.Y, + lightBox.Max.Y, + 0, + lightBox.Max.Z - lightBox.Min.Z + ); + + foreach (var (model, transform) in modelTransforms) + { + foreach (var modelMesh in model.Meshes) + { + foreach (var meshPart in modelMesh.MeshParts) + { + GraphicsDevice.SetVertexBuffer(meshPart.VertexBuffer); + GraphicsDevice.Indices = meshPart.IndexBuffer; + + SimpleDepthEffect.Model = transform; + + foreach (var pass in SimpleDepthEffect.CurrentTechnique.Passes) + { + pass.Apply(); + + GraphicsDevice.DrawIndexedPrimitives( + PrimitiveType.TriangleList, + 0, + 0, + meshPart.VertexBuffer.VertexCount, + 0, + meshPart.Triangles.Length + ); + } + } + } + } + } + public void Render( Camera camera, IEnumerable<(Model, Matrix)> modelTransforms, -- 2.25.1 From a55e33f9a9097a59eb8dfafd36f6c75e95841916 Mon Sep 17 00:00:00 2001 From: cosmonaut Date: Fri, 18 Sep 2020 04:00:53 -0700 Subject: [PATCH 06/15] cascaded shadow maps --- Cameras/Camera.cs | 34 ++++--- Effects/DeferredPBREffect.cs | 74 +++++++++++--- Effects/FXB/DeferredPBREffect.fxb | Bin 52792 -> 55208 bytes Effects/HLSL/DeferredPBREffect.fx | 73 +++++++++++-- Renderer.cs | 164 +++++++++++++++++------------- 5 files changed, 243 insertions(+), 102 deletions(-) diff --git a/Cameras/Camera.cs b/Cameras/Camera.cs index c0729ee..5462985 100644 --- a/Cameras/Camera.cs +++ b/Cameras/Camera.cs @@ -2,22 +2,32 @@ using Microsoft.Xna.Framework; namespace Kav { - public struct Camera + public struct PerspectiveCamera { - public Matrix Transform { get; } - public Matrix View - { - get - { - return Matrix.CreateLookAt(Transform.Translation, Transform.Translation + Transform.Forward, Transform.Up); - } - } + public Matrix View { get; } public Matrix Projection { get; } - public Camera(Matrix transform, Matrix projection) + public Vector3 Position { get; } + public Vector3 Forward { get; } + public Vector3 Up { get; } + + public float FieldOfView { get; } + public float AspectRatio { get; } + public float NearPlane { get; } + public float FarPlane { get; } + + public PerspectiveCamera(Vector3 position, Vector3 forward, Vector3 up, float fieldOfView, float aspectRatio, float nearPlane, float farPlane) { - Transform = transform; - Projection = projection; + Position = position; + Forward = forward; + Up = up; + View = Matrix.CreateLookAt(Position, Position + Forward, Up); + + FieldOfView = fieldOfView; + AspectRatio = aspectRatio; + NearPlane = nearPlane; + FarPlane = farPlane; + Projection = Matrix.CreatePerspectiveFieldOfView(FieldOfView, AspectRatio, NearPlane, FarPlane); } } } diff --git a/Effects/DeferredPBREffect.cs b/Effects/DeferredPBREffect.cs index 390f5f3..1ad21d4 100644 --- a/Effects/DeferredPBREffect.cs +++ b/Effects/DeferredPBREffect.cs @@ -9,13 +9,24 @@ namespace Kav EffectParameter gAlbedoParam; EffectParameter gNormalParam; EffectParameter gMetallicRoughnessParam; - EffectParameter shadowMapParam; + + EffectParameter shadowMapOneParam; + EffectParameter shadowMapTwoParam; + EffectParameter shadowMapThreeParam; + EffectParameter shadowMapFourParam; + + EffectParameter lightSpaceMatrixOneParam; + EffectParameter lightSpaceMatrixTwoParam; + EffectParameter lightSpaceMatrixThreeParam; + EffectParameter lightSpaceMatrixFourParam; + + EffectParameter viewMatrixParam; + EffectParameter cascadeFarPlanesParam; EffectParameter directionalLightColorParam; EffectParameter directionalLightDirectionParam; EffectParameter eyePositionParam; - EffectParameter lightSpaceMatrixParam; PointLightCollection pointLightCollection; @@ -23,11 +34,22 @@ namespace Kav public Texture2D GAlbedo { get; set; } public Texture2D GNormal { get; set; } public Texture2D GMetallicRoughness { get; set; } - public Texture2D ShadowMap { get; set; } + + public Texture2D ShadowMapOne { get; set; } + public Texture2D ShadowMapTwo { get; set; } + public Texture2D ShadowMapThree { get; set; } + public Texture2D ShadowMapFour { get; set; } + + public Matrix LightSpaceMatrixOne { get; set; } + public Matrix LightSpaceMatrixTwo { get; set; } + public Matrix LightSpaceMatrixThree { get; set; } + public Matrix LightSpaceMatrixFour { get; set; } + + public Matrix ViewMatrix { get; set; } + public float[] CascadeFarPlanes; public Vector3 DirectionalLightColor { get; set; } public Vector3 DirectionalLightDirection { get; set; } - public Matrix LightSpaceMatrix { get; set; } public Vector3 EyePosition { get; set; } @@ -41,6 +63,8 @@ namespace Kav public DeferredPBREffect(GraphicsDevice graphicsDevice) : base(graphicsDevice, Resources.DeferredPBREffect) { + CascadeFarPlanes = new float[4]; + CacheEffectParameters(); pointLightCollection = new PointLightCollection( @@ -82,28 +106,50 @@ namespace Kav gAlbedoParam.SetValue(GAlbedo); gNormalParam.SetValue(GNormal); gMetallicRoughnessParam.SetValue(GMetallicRoughness); - shadowMapParam.SetValue(ShadowMap); + + shadowMapOneParam.SetValue(ShadowMapOne); + shadowMapTwoParam.SetValue(ShadowMapTwo); + shadowMapThreeParam.SetValue(ShadowMapThree); + shadowMapFourParam.SetValue(ShadowMapFour); + + lightSpaceMatrixOneParam.SetValue(LightSpaceMatrixOne); + lightSpaceMatrixTwoParam.SetValue(LightSpaceMatrixTwo); + lightSpaceMatrixThreeParam.SetValue(LightSpaceMatrixThree); + lightSpaceMatrixFourParam.SetValue(LightSpaceMatrixFour); + + viewMatrixParam.SetValue(ViewMatrix); + cascadeFarPlanesParam.SetValue(CascadeFarPlanes); directionalLightColorParam.SetValue(DirectionalLightColor); directionalLightDirectionParam.SetValue(DirectionalLightDirection); eyePositionParam.SetValue(EyePosition); - lightSpaceMatrixParam.SetValue(LightSpaceMatrix); } void CacheEffectParameters() { - gPositionParam = Parameters["gPosition"]; - gAlbedoParam = Parameters["gAlbedo"]; - gNormalParam = Parameters["gNormal"]; - gMetallicRoughnessParam = Parameters["gMetallicRoughness"]; - shadowMapParam = Parameters["shadowMap"]; + gPositionParam = Parameters["gPosition"]; + gAlbedoParam = Parameters["gAlbedo"]; + gNormalParam = Parameters["gNormal"]; + gMetallicRoughnessParam = Parameters["gMetallicRoughness"]; + + shadowMapOneParam = Parameters["shadowMapOne"]; + shadowMapTwoParam = Parameters["shadowMapTwo"]; + shadowMapThreeParam = Parameters["shadowMapThree"]; + shadowMapFourParam = Parameters["shadowMapFour"]; + + lightSpaceMatrixOneParam = Parameters["LightSpaceMatrixOne"]; + lightSpaceMatrixTwoParam = Parameters["LightSpaceMatrixTwo"]; + lightSpaceMatrixThreeParam = Parameters["LightSpaceMatrixThree"]; + lightSpaceMatrixFourParam = Parameters["LightSpaceMatrixFour"]; + + viewMatrixParam = Parameters["ViewMatrix"]; + cascadeFarPlanesParam = Parameters["CascadeFarPlanes"]; directionalLightDirectionParam = Parameters["DirectionalLightDirection"]; - directionalLightColorParam = Parameters["DirectionalLightColor"]; - lightSpaceMatrixParam = Parameters["LightSpaceMatrix"]; + directionalLightColorParam = Parameters["DirectionalLightColor"]; - eyePositionParam = Parameters["EyePosition"]; + eyePositionParam = Parameters["EyePosition"]; } } } diff --git a/Effects/FXB/DeferredPBREffect.fxb b/Effects/FXB/DeferredPBREffect.fxb index f06f5aabc8421dd6bd589e0c45af7e7fb941028d..cc460f8a1157194da6882a0633a2c72f1fa92235 100644 GIT binary patch literal 55208 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

@zjj)mjE9A{(T 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 H%|iY+TL%H9 diff --git a/Effects/HLSL/DeferredPBREffect.fx b/Effects/HLSL/DeferredPBREffect.fx index 2f9f3a1..0ce4d56 100644 --- a/Effects/HLSL/DeferredPBREffect.fx +++ b/Effects/HLSL/DeferredPBREffect.fx @@ -2,12 +2,16 @@ static const float PI = 3.141592653589793; static const int MAX_POINT_LIGHTS = 64; +static const int NUM_SHADOW_CASCADES = 4; DECLARE_TEXTURE(gPosition, 0); DECLARE_TEXTURE(gAlbedo, 1); DECLARE_TEXTURE(gNormal, 2); DECLARE_TEXTURE(gMetallicRoughness, 3); -DECLARE_TEXTURE(shadowMap, 4); +DECLARE_TEXTURE(shadowMapOne, 4); +DECLARE_TEXTURE(shadowMapTwo, 5); +DECLARE_TEXTURE(shadowMapThree, 6); +DECLARE_TEXTURE(shadowMapFour, 7); BEGIN_CONSTANTS @@ -19,9 +23,17 @@ BEGIN_CONSTANTS float3 DirectionalLightDirection _ps(c129) _cb(c129); float3 DirectionalLightColor _ps(c130) _cb(c130); + float CascadeFarPlanes[NUM_SHADOW_CASCADES] _ps(c131) _cb(c131); + MATRIX_CONSTANTS - float4x4 LightSpaceMatrix _ps(c131) _cb(c131); + float4x4 LightSpaceMatrixOne _ps(c135) _cb(c135); + float4x4 LightSpaceMatrixTwo _ps(c139) _cb(c139); + float4x4 LightSpaceMatrixThree _ps(c143) _cb(c143); + float4x4 LightSpaceMatrixFour _ps(c147) _cb(c147); + + // used to select shadow cascade + float4x4 ViewMatrix _ps(c151) _cb(c151); END_CONSTANTS @@ -89,11 +101,44 @@ float GeometrySmith(float3 N, float3 V, float3 L, float roughness) return ggx1 * ggx2; } -float ComputeShadow(float4 positionLightSpace, float3 N, float L) +float ComputeShadow(float3 positionWorldSpace, float3 N, float L) { float bias = 0.005 * tan(acos(dot(N, L))); bias = clamp(bias, 0, 0.01); + float4 positionCameraSpace = mul(float4(positionWorldSpace, 1.0), ViewMatrix); + + int shadowCascadeIndex = 0; // 0 is closest + for (int i = 0; i < NUM_SHADOW_CASCADES; i++) + { + if (positionCameraSpace.z < CascadeFarPlanes[i]) + { + shadowCascadeIndex = i; + break; + } + } + + float4x4 lightSpaceMatrix; + + if (shadowCascadeIndex == 0) + { + lightSpaceMatrix = LightSpaceMatrixOne; + } + else if (shadowCascadeIndex == 1) + { + lightSpaceMatrix = LightSpaceMatrixTwo; + } + else if (shadowCascadeIndex == 2) + { + lightSpaceMatrix = LightSpaceMatrixThree; + } + else + { + lightSpaceMatrix = LightSpaceMatrixFour; + } + + float4 positionLightSpace = mul(float4(positionWorldSpace, 1.0), lightSpaceMatrix); + // maps to [-1, 1] float3 projectionCoords = positionLightSpace.xyz / positionLightSpace.w; @@ -102,7 +147,24 @@ float ComputeShadow(float4 positionLightSpace, float3 N, float L) projectionCoords.y = (projectionCoords.y * 0.5) + 0.5; projectionCoords.y *= -1; - float closestDepth = SAMPLE_TEXTURE(shadowMap, projectionCoords.xy).r; + float closestDepth; + if (shadowCascadeIndex == 0) + { + closestDepth = SAMPLE_TEXTURE(shadowMapOne, projectionCoords.xy).r; + } + else if (shadowCascadeIndex == 1) + { + closestDepth = SAMPLE_TEXTURE(shadowMapTwo, projectionCoords.xy).r; + } + else if (shadowCascadeIndex == 2) + { + closestDepth = SAMPLE_TEXTURE(shadowMapThree, projectionCoords.xy).r; + } + else + { + closestDepth = SAMPLE_TEXTURE(shadowMapFour, projectionCoords.xy).r; + } + float currentDepth = projectionCoords.z; if (currentDepth - bias > closestDepth) @@ -176,8 +238,7 @@ float4 ComputeColor( float3 L = normalize(DirectionalLightDirection); float3 radiance = DirectionalLightColor; - float4 positionLightSpace = mul(float4(worldPosition, 1.0), LightSpaceMatrix); - float shadow = ComputeShadow(positionLightSpace, N, L); + float shadow = ComputeShadow(worldPosition, N, L); Lo += ComputeLight(L, radiance, F0, V, N, albedo, metallic, roughness, (1.0 - shadow)); diff --git a/Renderer.cs b/Renderer.cs index 210aed8..8d05f96 100644 --- a/Renderer.cs +++ b/Renderer.cs @@ -6,12 +6,15 @@ namespace Kav { public class Renderer { + private const int MAX_SHADOW_CASCADES = 4; + private GraphicsDevice GraphicsDevice { get; } private int RenderDimensionsX { get; } private int RenderDimensionsY { get; } private VertexBuffer FullscreenTriangle { get; } - private RenderTarget2D ShadowRenderTarget { get; } + private int NumShadowCascades { get; } + private RenderTarget2D[] ShadowRenderTargets { get; } private DeferredPBREffect DeferredPBREffect { get; } private SimpleDepthEffect SimpleDepthEffect { get; } @@ -24,22 +27,26 @@ namespace Kav private RenderTargetBinding[] GBuffer { get; } - private SpriteBatch SpriteBatch { get; } - - public Renderer(GraphicsDevice graphicsDevice, int renderDimensionsX, int renderDimensionsY) + public Renderer(GraphicsDevice graphicsDevice, int renderDimensionsX, int renderDimensionsY, int numShadowCascades) { GraphicsDevice = graphicsDevice; RenderDimensionsX = renderDimensionsX; RenderDimensionsY = renderDimensionsY; - ShadowRenderTarget = new RenderTarget2D( - GraphicsDevice, - 1024, - 1024, - false, - SurfaceFormat.Single, - DepthFormat.Depth24 - ); + NumShadowCascades = (int)MathHelper.Clamp(numShadowCascades, 1, MAX_SHADOW_CASCADES); + ShadowRenderTargets = new RenderTarget2D[numShadowCascades]; + + for (var i = 0; i < numShadowCascades; i++) + { + ShadowRenderTargets[i] = new RenderTarget2D( + GraphicsDevice, + 1024, + 1024, + false, + SurfaceFormat.Single, + DepthFormat.Depth24 + ); + } gPosition = new RenderTarget2D( GraphicsDevice, @@ -100,21 +107,18 @@ namespace Kav new VertexPositionTexture(new Vector3(3, -1, 0), new Vector2(2, 0)) }); - SpriteBatch = new SpriteBatch(GraphicsDevice); - GraphicsDevice.SetRenderTarget(deferredRenderTarget); graphicsDevice.Clear(Color.White); GraphicsDevice.SetRenderTarget(null); } public void DeferredRender( - Camera camera, + PerspectiveCamera camera, IEnumerable<(Model, Matrix)> modelTransforms, IEnumerable pointLights, - DirectionalLight directionalLight, - int numCascades + DirectionalLight directionalLight ) { - ShadowMapRender(camera, modelTransforms, directionalLight, numCascades); + ShadowMapRender(camera, modelTransforms, directionalLight); GraphicsDevice.SetRenderTargets(GBuffer); GraphicsDevice.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, Color.Black, 1f, 0); @@ -161,7 +165,23 @@ namespace Kav DeferredPBREffect.GAlbedo = gAlbedo; DeferredPBREffect.GNormal = gNormal; DeferredPBREffect.GMetallicRoughness = gMetallicRoughness; - DeferredPBREffect.ShadowMap = ShadowRenderTarget; + + DeferredPBREffect.ShadowMapOne = ShadowRenderTargets[0]; + if (NumShadowCascades > 1) + { + DeferredPBREffect.ShadowMapTwo = ShadowRenderTargets[1]; + } + if (NumShadowCascades > 2) + { + DeferredPBREffect.ShadowMapThree = ShadowRenderTargets[2]; + } + if (NumShadowCascades > 3) + { + DeferredPBREffect.ShadowMapFour = ShadowRenderTargets[3]; + } + + DeferredPBREffect.ViewMatrix = camera.View; + DeferredPBREffect.EyePosition = Matrix.Invert(camera.View).Translation; int i = 0; @@ -182,23 +202,15 @@ namespace Kav GraphicsDevice.SetVertexBuffer(FullscreenTriangle); GraphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, 1); } - - // SpriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, null, null, null, DeferredPBREffect); - // SpriteBatch.Draw(deferredRenderTarget, Vector2.Zero, Color.White); - // SpriteBatch.End(); } public void ShadowMapRender( - Camera camera, + PerspectiveCamera camera, IEnumerable<(Model, Matrix)> modelTransforms, - DirectionalLight directionalLight, - int numCascades + DirectionalLight directionalLight ) { - GraphicsDevice.SetRenderTarget(ShadowRenderTarget); - GraphicsDevice.Clear(Color.White); - GraphicsDevice.DepthStencilState = DepthStencilState.Default; - GraphicsDevice.BlendState = BlendState.Opaque; - + // set up global light matrix + var right = Vector3.Cross(Vector3.Up, directionalLight.Direction); var up = Vector3.Cross(directionalLight.Direction, right); var cameraBoundingFrustum = new BoundingFrustum(camera.View * camera.Projection); @@ -221,48 +233,39 @@ namespace Kav BoundingBox lightBox = BoundingBox.CreateFromPoints(frustumCorners); Vector3 lightPosition = frustumCenter + directionalLight.Direction * -lightBox.Min.Z; - SimpleDepthEffect.View = Matrix.CreateLookAt(lightPosition, frustumCenter, camera.View.Right); - SimpleDepthEffect.Projection = Matrix.CreateOrthographicOffCenter( - lightBox.Min.X, - lightBox.Max.X, - lightBox.Min.Y, - lightBox.Max.Y, - 0, - lightBox.Max.Z - lightBox.Min.Z - ); - DeferredPBREffect.LightSpaceMatrix = SimpleDepthEffect.View * SimpleDepthEffect.Projection; - - foreach (var (model, transform) in modelTransforms) + // render the individual shadow maps + + var frustumDistance = camera.FarPlane - camera.NearPlane; + var sectionDistance = frustumDistance / NumShadowCascades; + + for (var i = 0; i < NumShadowCascades; i++) { - foreach (var modelMesh in model.Meshes) - { - foreach (var meshPart in modelMesh.MeshParts) - { - GraphicsDevice.SetVertexBuffer(meshPart.VertexBuffer); - GraphicsDevice.Indices = meshPart.IndexBuffer; - - SimpleDepthEffect.Model = transform; - - foreach (var pass in SimpleDepthEffect.CurrentTechnique.Passes) - { - pass.Apply(); - - GraphicsDevice.DrawIndexedPrimitives( - PrimitiveType.TriangleList, - 0, - 0, - meshPart.VertexBuffer.VertexCount, - 0, - meshPart.Triangles.Length - ); - } - } - } + // divide the view frustum + var shadowCamera = new PerspectiveCamera( + camera.Position, + camera.Forward, + camera.Up, + camera.FieldOfView, + camera.AspectRatio, + camera.NearPlane + (i * sectionDistance), + camera.NearPlane + ((i + 1) * sectionDistance) + ); + + RenderShadowMap(shadowCamera, modelTransforms, directionalLight, i); } } - private void RenderShadowMap(Camera camera, IEnumerable<(Model, Matrix)> modelTransforms, DirectionalLight directionalLight) - { + private void RenderShadowMap( + PerspectiveCamera camera, + IEnumerable<(Model, Matrix)> modelTransforms, + DirectionalLight directionalLight, + int shadowCascadeIndex + ) { + GraphicsDevice.SetRenderTarget(ShadowRenderTargets[shadowCascadeIndex]); + GraphicsDevice.Clear(Color.White); + GraphicsDevice.DepthStencilState = DepthStencilState.Default; + GraphicsDevice.BlendState = BlendState.Opaque; + var right = Vector3.Cross(Vector3.Up, directionalLight.Direction); var up = Vector3.Cross(directionalLight.Direction, right); var cameraBoundingFrustum = new BoundingFrustum(camera.View * camera.Projection); @@ -295,6 +298,27 @@ namespace Kav lightBox.Max.Z - lightBox.Min.Z ); + var lightSpaceMatrix = SimpleDepthEffect.View * SimpleDepthEffect.Projection; + + if (shadowCascadeIndex == 0) + { + DeferredPBREffect.LightSpaceMatrixOne = lightSpaceMatrix; + } + else if (shadowCascadeIndex == 1) + { + DeferredPBREffect.LightSpaceMatrixTwo = lightSpaceMatrix; + } + else if (shadowCascadeIndex == 2) + { + DeferredPBREffect.LightSpaceMatrixThree = lightSpaceMatrix; + } + else if (shadowCascadeIndex == 3) + { + DeferredPBREffect.LightSpaceMatrixFour = lightSpaceMatrix; + } + + DeferredPBREffect.CascadeFarPlanes[shadowCascadeIndex] = camera.FarPlane; + foreach (var (model, transform) in modelTransforms) { foreach (var modelMesh in model.Meshes) @@ -325,7 +349,7 @@ namespace Kav } public void Render( - Camera camera, + PerspectiveCamera camera, IEnumerable<(Model, Matrix)> modelTransforms, IEnumerable pointLights, IEnumerable directionalLights -- 2.25.1 From 314b3088404bcb3a1d5d9761f3c1871d28d317ff Mon Sep 17 00:00:00 2001 From: cosmonaut Date: Fri, 18 Sep 2020 22:43:12 -0700 Subject: [PATCH 07/15] fixing CSM gaps --- Effects/DeferredPBREffect.cs | 2 +- Effects/FXB/DeferredPBREffect.fxb | Bin 55208 -> 55496 bytes Effects/FXB/SimpleDepthEffect.fxb | Bin 848 -> 832 bytes Effects/HLSL/DeferredPBREffect.fx | 73 ++++++++++++++++++++---------- Effects/HLSL/SimpleDepthEffect.fx | 2 +- Renderer.cs | 26 +++++------ 6 files changed, 64 insertions(+), 39 deletions(-) diff --git a/Effects/DeferredPBREffect.cs b/Effects/DeferredPBREffect.cs index 1ad21d4..181d1ec 100644 --- a/Effects/DeferredPBREffect.cs +++ b/Effects/DeferredPBREffect.cs @@ -46,7 +46,7 @@ namespace Kav public Matrix LightSpaceMatrixFour { get; set; } public Matrix ViewMatrix { get; set; } - public float[] CascadeFarPlanes; + public readonly float[] CascadeFarPlanes; public Vector3 DirectionalLightColor { get; set; } public Vector3 DirectionalLightDirection { get; set; } diff --git a/Effects/FXB/DeferredPBREffect.fxb b/Effects/FXB/DeferredPBREffect.fxb index cc460f8a1157194da6882a0633a2c72f1fa92235..c5b5a89f711c3908e1db68c776981bd68f368ea5 100644 GIT binary patch literal 55496 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

@zjj)mjE9A{(T z_;KM_I1bG5XJgiu(u^HM6UWpn2lTlDYQ9*53Z*gjjAIa{@=@9*dE$G&?W z{KEQozLKzeB8n;L{Cl=3Q&wK9*8M4)6BC!SPu-JhTI*59H8MBG26L6t{Zq&tIWjdd zfj;H5OFh~>Y-Pr7Qro4Dv-{#48^-SM=g8E=Zx*ooE6;8cd(C)hmv)T{vsLyN+kOlBT$kNztY1|Yqxy52>>TEv zHqvwEC_TrqM?1_9@}l?N#L-^)tzEmnf4`d}Qxm^Nzj6P5X=ThaS#kT$TA4#Q|JaSyA>T#8;)@Aw(965#@HOh{INidj7s|<3hCUABZJgY76O`SCE z4(^L9hY*R79tkM2|dq|QfeoWAPkl=qoFm+o#Dcl+#gcz5wG zGBLJbEwh+Y!#>Pb%)VsozK~;;GMv@4?+%^27U(-&vxl@{<|ehhCiG3*(Vba!>w;hC zm_1LJeFJ~AUpq}5%GhW1`%_9)nKw7iF$G;^b-5mnalI+zF$H;}zMD%OW1IIk?=s`7 zZcj6ZSoGbAd)aVh`+CmYcxl7zo%7*ra*kO~uCh#X!}t4~UT+3@VjOA3=TS}P75fJ! zb&j=PjT%a^#$4_L`;0rG*FDYLA`kS*JWN-P*}S>6@$NACIqmZp*rbhwTB`eL zHEC@uh5bp)pROjXAL_9EteTAG&luovXnnLy7%tAovL@JuUr}}?HkLk-g!rM zThFeZ?5@Flf1#Y+QrbTc7Sce8%D-}&2Jgy7)>8AFg%&S%E007Mr6_Nk| diff --git a/Effects/HLSL/DeferredPBREffect.fx b/Effects/HLSL/DeferredPBREffect.fx index 0ce4d56..14a58c2 100644 --- a/Effects/HLSL/DeferredPBREffect.fx +++ b/Effects/HLSL/DeferredPBREffect.fx @@ -111,7 +111,7 @@ float ComputeShadow(float3 positionWorldSpace, float3 N, float L) int shadowCascadeIndex = 0; // 0 is closest for (int i = 0; i < NUM_SHADOW_CASCADES; i++) { - if (positionCameraSpace.z < CascadeFarPlanes[i]) + if (abs(positionCameraSpace.z) < CascadeFarPlanes[i]) { shadowCascadeIndex = i; break; @@ -146,35 +146,61 @@ float ComputeShadow(float3 positionWorldSpace, float3 N, float L) projectionCoords.x = (projectionCoords.x * 0.5) + 0.5; projectionCoords.y = (projectionCoords.y * 0.5) + 0.5; projectionCoords.y *= -1; + // in XNA clip z is 0 to 1 already - float closestDepth; - if (shadowCascadeIndex == 0) + float inc = 1.0 / 1024.0; + + float shadowFactor = 0; + for (int row = -1; row <= 1; row++) { - closestDepth = SAMPLE_TEXTURE(shadowMapOne, projectionCoords.xy).r; - } - else if (shadowCascadeIndex == 1) - { - closestDepth = SAMPLE_TEXTURE(shadowMapTwo, projectionCoords.xy).r; - } - else if (shadowCascadeIndex == 2) - { - closestDepth = SAMPLE_TEXTURE(shadowMapThree, projectionCoords.xy).r; - } - else - { - closestDepth = SAMPLE_TEXTURE(shadowMapFour, projectionCoords.xy).r; + for (int col = -1; col <= 1; col++) + { + float closestDepth; + if (shadowCascadeIndex == 0) + { + closestDepth = SAMPLE_TEXTURE(shadowMapOne, projectionCoords.xy + float2(row, col) * inc).r; + } + else if (shadowCascadeIndex == 1) + { + closestDepth = SAMPLE_TEXTURE(shadowMapTwo, projectionCoords.xy + float2(row, col) * inc).r; + } + else if (shadowCascadeIndex == 2) + { + closestDepth = SAMPLE_TEXTURE(shadowMapThree, projectionCoords.xy + float2(row, col) * inc).r; + } + else + { + closestDepth = SAMPLE_TEXTURE(shadowMapFour, projectionCoords.xy + float2(row, col) * inc).r; + } + shadowFactor += projectionCoords.z - bias > closestDepth ? 1.0 : 0.0; + } } - float currentDepth = projectionCoords.z; + shadowFactor /= 9.0; - if (currentDepth - bias > closestDepth) + if (projectionCoords.z > 1.0) { - return 1.0; - } - else - { - return 0.0; + shadowFactor = 1.0; } + + return shadowFactor; + + + // float currentDepth = projectionCoords.z; + + // if (currentDepth > 1.0) + // { + // return 0.0; + // } + + // if (currentDepth - bias > closestDepth) + // { + // return 1.0; + // } + // else + // { + // return 0.0; + // } } float3 ComputeLight( @@ -239,7 +265,6 @@ float4 ComputeColor( float3 radiance = DirectionalLightColor; float shadow = ComputeShadow(worldPosition, N, L); - Lo += ComputeLight(L, radiance, F0, V, N, albedo, metallic, roughness, (1.0 - shadow)); float3 ambient = float3(0.03, 0.03, 0.03) * albedo; // * AO; diff --git a/Effects/HLSL/SimpleDepthEffect.fx b/Effects/HLSL/SimpleDepthEffect.fx index f813bd7..3598eb5 100644 --- a/Effects/HLSL/SimpleDepthEffect.fx +++ b/Effects/HLSL/SimpleDepthEffect.fx @@ -24,7 +24,7 @@ VertexShaderOutput main_vs(VertexShaderInput input) VertexShaderOutput output; output.Position = mul(input.Position, ModelViewProjection); - output.Depth = output.Position.z / output.Position.w; + output.Depth = output.Position.z; return output; } diff --git a/Renderer.cs b/Renderer.cs index 8d05f96..b16a3b7 100644 --- a/Renderer.cs +++ b/Renderer.cs @@ -8,6 +8,7 @@ namespace Kav { private const int MAX_SHADOW_CASCADES = 4; + private GraphicsDevice GraphicsDevice { get; } private int RenderDimensionsX { get; } private int RenderDimensionsY { get; } @@ -235,11 +236,11 @@ namespace Kav // render the individual shadow maps - var frustumDistance = camera.FarPlane - camera.NearPlane; - var sectionDistance = frustumDistance / NumShadowCascades; - + var previousFarPlane = camera.NearPlane; for (var i = 0; i < NumShadowCascades; i++) { + var farPlane = camera.FarPlane / (MathHelper.Max((NumShadowCascades - i - 1) * 5f, 1f)); + // divide the view frustum var shadowCamera = new PerspectiveCamera( camera.Position, @@ -247,11 +248,13 @@ namespace Kav camera.Up, camera.FieldOfView, camera.AspectRatio, - camera.NearPlane + (i * sectionDistance), - camera.NearPlane + ((i + 1) * sectionDistance) + previousFarPlane, + farPlane ); RenderShadowMap(shadowCamera, modelTransforms, directionalLight, i); + + previousFarPlane = farPlane; } } @@ -265,9 +268,7 @@ namespace Kav GraphicsDevice.Clear(Color.White); GraphicsDevice.DepthStencilState = DepthStencilState.Default; GraphicsDevice.BlendState = BlendState.Opaque; - - var right = Vector3.Cross(Vector3.Up, directionalLight.Direction); - var up = Vector3.Cross(directionalLight.Direction, right); + var cameraBoundingFrustum = new BoundingFrustum(camera.View * camera.Projection); Vector3[] frustumCorners = cameraBoundingFrustum.GetCorners(); @@ -278,7 +279,7 @@ namespace Kav } frustumCenter /= 8f; - var lightView = Matrix.CreateLookAt(frustumCenter, frustumCenter - directionalLight.Direction, camera.View.Right); + var lightView = Matrix.CreateLookAt(frustumCenter + directionalLight.Direction, frustumCenter, Vector3.Backward); for (var i = 0; i < frustumCorners.Length; i++) { @@ -286,16 +287,15 @@ namespace Kav } BoundingBox lightBox = BoundingBox.CreateFromPoints(frustumCorners); - Vector3 lightPosition = frustumCenter + directionalLight.Direction * -lightBox.Min.Z; - SimpleDepthEffect.View = Matrix.CreateLookAt(lightPosition, frustumCenter, camera.View.Right); + SimpleDepthEffect.View = lightView; SimpleDepthEffect.Projection = Matrix.CreateOrthographicOffCenter( lightBox.Min.X, lightBox.Max.X, lightBox.Min.Y, lightBox.Max.Y, - 0, - lightBox.Max.Z - lightBox.Min.Z + -lightBox.Max.Z - 10f, // TODO: near clip plane needs scene AABB info to get rid of this magic value + -lightBox.Min.Z ); var lightSpaceMatrix = SimpleDepthEffect.View * SimpleDepthEffect.Projection; -- 2.25.1 From 8cb760171745a7bc75ff56d88f0773dc55e2f2e3 Mon Sep 17 00:00:00 2001 From: cosmonaut Date: Sat, 19 Sep 2020 01:06:08 -0700 Subject: [PATCH 08/15] clean up renderer a bit + README --- README.md | 24 ++++++++++++++++++++++++ Renderer.cs | 25 ------------------------- 2 files changed, 24 insertions(+), 25 deletions(-) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..c5dff2d --- /dev/null +++ b/README.md @@ -0,0 +1,24 @@ +# Kav + +A 3D renderer built on top of FNA. + +## Roadmap + +Essential + +- [x] PBR shading +- [x] Deferred rendering +- [x] Point lighting +- [x] Directional lighting +- [x] Directional shadow maps +- [x] Cascading shadow maps +- [ ] Frustum culling +- [ ] Shadow-casting point lights +- [ ] Parabolic lights +- [ ] Skyboxes +- [ ] Screen-space reflection + +Nice-To-Haves + +- [ ] Volumetric lighting +- [ ] Volumetric smoke diff --git a/Renderer.cs b/Renderer.cs index b16a3b7..b99fe02 100644 --- a/Renderer.cs +++ b/Renderer.cs @@ -210,32 +210,7 @@ namespace Kav IEnumerable<(Model, Matrix)> modelTransforms, DirectionalLight directionalLight ) { - // set up global light matrix - - var right = Vector3.Cross(Vector3.Up, directionalLight.Direction); - var up = Vector3.Cross(directionalLight.Direction, right); - var cameraBoundingFrustum = new BoundingFrustum(camera.View * camera.Projection); - Vector3[] frustumCorners = cameraBoundingFrustum.GetCorners(); - - Vector3 frustumCenter = Vector3.Zero; - for (var i = 0; i < frustumCorners.Length; i++) - { - frustumCenter += frustumCorners[i]; - } - frustumCenter /= 8f; - - var lightView = Matrix.CreateLookAt(frustumCenter, frustumCenter - directionalLight.Direction, camera.View.Right); - - for (var i = 0; i < frustumCorners.Length; i++) - { - frustumCorners[i] = Vector3.Transform(frustumCorners[i], lightView); - } - - BoundingBox lightBox = BoundingBox.CreateFromPoints(frustumCorners); - Vector3 lightPosition = frustumCenter + directionalLight.Direction * -lightBox.Min.Z; - // render the individual shadow maps - var previousFarPlane = camera.NearPlane; for (var i = 0; i < NumShadowCascades; i++) { -- 2.25.1 From a69873708db7417d1edb0a0f6c56eb52ce3cb0d3 Mon Sep 17 00:00:00 2001 From: cosmonaut Date: Sat, 19 Sep 2020 01:35:30 -0700 Subject: [PATCH 09/15] more roadmap stuff --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index c5dff2d..7a2bff0 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,7 @@ Essential - [x] Directional shadow maps - [x] Cascading shadow maps - [ ] Frustum culling +- [ ] PCF soft shadowing - [ ] Shadow-casting point lights - [ ] Parabolic lights - [ ] Skyboxes @@ -20,5 +21,8 @@ Essential Nice-To-Haves +- [ ] Anti-aliasing +- [ ] Image-based lighting - [ ] Volumetric lighting - [ ] Volumetric smoke +- [ ] -- 2.25.1 From 7ea2b57315fe0dcd4677b91e683e2eb97897b368 Mon Sep 17 00:00:00 2001 From: cosmonaut Date: Sun, 20 Sep 2020 02:05:19 -0700 Subject: [PATCH 10/15] restructure lights to use one pass per light --- Effects/DeferredPBR_AmbientLightEffect.cs | 31 +++ Effects/DeferredPBR_DirectionalLightEffect.cs | 152 ++++++++++++++ Effects/DeferredPBR_PointLightEffect.cs | 77 +++++++ .../FXB/DeferredPBR_AmbientLightEffect.fxb | Bin 0 -> 1016 bytes .../DeferredPBR_DirectionalLightEffect.fxb | Bin 0 -> 6916 bytes Effects/FXB/DeferredPBR_GBufferEffect.fxb | Bin 7476 -> 7476 bytes Effects/FXB/DeferredPBR_PointLightEffect.fxb | Bin 0 -> 3108 bytes .../HLSL/DeferredPBR_AmbientLightEffect.fx | 54 +++++ .../DeferredPBR_DirectionalLightEffect.fx | 190 ++++++++++++++++++ Effects/HLSL/DeferredPBR_GBufferEffect.fx | 40 ++-- Effects/HLSL/DeferredPBR_PointLightEffect.fx | 91 +++++++++ Effects/HLSL/Lighting.fxh | 71 +++++++ Kav.Core.csproj | 9 + Kav.Framework.csproj | 9 + README.md | 1 + Renderer.cs | 173 +++++++++++----- Resources.cs | 38 ++++ 17 files changed, 864 insertions(+), 72 deletions(-) create mode 100644 Effects/DeferredPBR_AmbientLightEffect.cs create mode 100644 Effects/DeferredPBR_DirectionalLightEffect.cs create mode 100644 Effects/DeferredPBR_PointLightEffect.cs create mode 100644 Effects/FXB/DeferredPBR_AmbientLightEffect.fxb create mode 100644 Effects/FXB/DeferredPBR_DirectionalLightEffect.fxb create mode 100644 Effects/FXB/DeferredPBR_PointLightEffect.fxb create mode 100644 Effects/HLSL/DeferredPBR_AmbientLightEffect.fx create mode 100644 Effects/HLSL/DeferredPBR_DirectionalLightEffect.fx create mode 100644 Effects/HLSL/DeferredPBR_PointLightEffect.fx create mode 100644 Effects/HLSL/Lighting.fxh diff --git a/Effects/DeferredPBR_AmbientLightEffect.cs b/Effects/DeferredPBR_AmbientLightEffect.cs new file mode 100644 index 0000000..f1d5e31 --- /dev/null +++ b/Effects/DeferredPBR_AmbientLightEffect.cs @@ -0,0 +1,31 @@ +using Microsoft.Xna.Framework.Graphics; + +namespace Kav +{ + + public class DeferredPBR_AmbientLightEffect : Effect + { + EffectParameter gPositionParam; + EffectParameter gAlbedoParam; + + public Texture2D GPosition { get; set; } + public Texture2D GAlbedo { get; set; } + + public DeferredPBR_AmbientLightEffect(GraphicsDevice graphicsDevice) : base(graphicsDevice, Resources.DeferredPBR_AmbientLightEffect) + { + CacheEffectParameters(); + } + + protected override void OnApply() + { + gPositionParam.SetValue(GPosition); + gAlbedoParam.SetValue(GAlbedo); + } + + void CacheEffectParameters() + { + gPositionParam = Parameters["gPosition"]; + gAlbedoParam = Parameters["gAlbedo"]; + } + } +} diff --git a/Effects/DeferredPBR_DirectionalLightEffect.cs b/Effects/DeferredPBR_DirectionalLightEffect.cs new file mode 100644 index 0000000..fb24e78 --- /dev/null +++ b/Effects/DeferredPBR_DirectionalLightEffect.cs @@ -0,0 +1,152 @@ +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; + +namespace Kav +{ + public class DeferredPBR_DirectionalLightEffect : Effect + { + EffectParameter gPositionParam; + EffectParameter gAlbedoParam; + EffectParameter gNormalParam; + EffectParameter gMetallicRoughnessParam; + + EffectParameter shadowMapOneParam; + EffectParameter shadowMapTwoParam; + EffectParameter shadowMapThreeParam; + EffectParameter shadowMapFourParam; + + EffectParameter eyePositionParam; + + EffectParameter directionalLightColorParam; + EffectParameter directionalLightDirectionParam; + + EffectParameter cascadeFarPlanesParam; + + EffectParameter lightSpaceMatrixOneParam; + EffectParameter lightSpaceMatrixTwoParam; + EffectParameter lightSpaceMatrixThreeParam; + EffectParameter lightSpaceMatrixFourParam; + + EffectParameter viewMatrixParam; + + public Texture2D GPosition { get; set; } + public Texture2D GAlbedo { get; set; } + public Texture2D GNormal { get; set; } + public Texture2D GMetallicRoughness { get; set; } + + public Texture2D ShadowMapOne { get; set; } + public Texture2D ShadowMapTwo { get; set; } + public Texture2D ShadowMapThree { get; set; } + public Texture2D ShadowMapFour { get; set; } + + public Vector3 EyePosition { get; set; } + + public Vector3 DirectionalLightDirection { get; set; } + public Vector3 DirectionalLightColor { get; set; } + + public readonly float[] CascadeFarPlanes; + + public Matrix LightSpaceMatrixOne { get; set; } + public Matrix LightSpaceMatrixTwo { get; set; } + public Matrix LightSpaceMatrixThree { get; set; } + public Matrix LightSpaceMatrixFour { get; set; } + + public Matrix ViewMatrix { get; set; } + + public DeferredPBR_DirectionalLightEffect(GraphicsDevice graphicsDevice) : base(graphicsDevice, Resources.DeferredPBR_DirectionalLightEffect) + { + CascadeFarPlanes = new float[4]; + CacheEffectParameters(); + } + + public DeferredPBR_DirectionalLightEffect(DeferredPBR_DirectionalLightEffect cloneSource) : base(cloneSource) + { + GPosition = cloneSource.GPosition; + GAlbedo = cloneSource.GAlbedo; + GNormal = cloneSource.GNormal; + GMetallicRoughness = cloneSource.GMetallicRoughness; + + ShadowMapOne = cloneSource.ShadowMapOne; + ShadowMapTwo = cloneSource.ShadowMapTwo; + ShadowMapThree = cloneSource.ShadowMapThree; + ShadowMapFour = cloneSource.ShadowMapFour; + + EyePosition = cloneSource.EyePosition; + + DirectionalLightDirection = cloneSource.DirectionalLightDirection; + DirectionalLightColor = cloneSource.DirectionalLightColor; + + CascadeFarPlanes = new float[4]; + for (int i = 0 ; i < 4; i++) + { + CascadeFarPlanes[i] = cloneSource.CascadeFarPlanes[i]; + } + + LightSpaceMatrixOne = cloneSource.LightSpaceMatrixOne; + LightSpaceMatrixTwo = cloneSource.LightSpaceMatrixTwo; + LightSpaceMatrixThree = cloneSource.LightSpaceMatrixThree; + LightSpaceMatrixFour = cloneSource.LightSpaceMatrixFour; + + ViewMatrix = cloneSource.ViewMatrix; + } + + public override Effect Clone() + { + return new DeferredPBR_DirectionalLightEffect(this); + } + + protected override void OnApply() + { + gPositionParam.SetValue(GPosition); + gAlbedoParam.SetValue(GAlbedo); + gNormalParam.SetValue(GNormal); + gMetallicRoughnessParam.SetValue(GMetallicRoughness); + + shadowMapOneParam.SetValue(ShadowMapOne); + shadowMapTwoParam.SetValue(ShadowMapTwo); + shadowMapThreeParam.SetValue(ShadowMapThree); + shadowMapFourParam.SetValue(ShadowMapFour); + + eyePositionParam.SetValue(EyePosition); + + directionalLightDirectionParam.SetValue(DirectionalLightDirection); + directionalLightColorParam.SetValue(DirectionalLightColor); + + cascadeFarPlanesParam.SetValue(CascadeFarPlanes); + + lightSpaceMatrixOneParam.SetValue(LightSpaceMatrixOne); + lightSpaceMatrixTwoParam.SetValue(LightSpaceMatrixTwo); + lightSpaceMatrixThreeParam.SetValue(LightSpaceMatrixThree); + lightSpaceMatrixFourParam.SetValue(LightSpaceMatrixFour); + + viewMatrixParam.SetValue(ViewMatrix); + } + + void CacheEffectParameters() + { + gPositionParam = Parameters["gPosition"]; + gAlbedoParam = Parameters["gAlbedo"]; + gNormalParam = Parameters["gNormal"]; + gMetallicRoughnessParam = Parameters["gMetallicRoughness"]; + + shadowMapOneParam = Parameters["shadowMapOne"]; + shadowMapTwoParam = Parameters["shadowMapTwo"]; + shadowMapThreeParam = Parameters["shadowMapThree"]; + shadowMapFourParam = Parameters["shadowMapFour"]; + + eyePositionParam = Parameters["EyePosition"]; + + directionalLightDirectionParam = Parameters["DirectionalLightDirection"]; + directionalLightColorParam = Parameters["DirectionalLightColor"]; + + cascadeFarPlanesParam = Parameters["CascadeFarPlanes"]; + + lightSpaceMatrixOneParam = Parameters["LightSpaceMatrixOne"]; + lightSpaceMatrixTwoParam = Parameters["LightSpaceMatrixTwo"]; + lightSpaceMatrixThreeParam = Parameters["LightSpaceMatrixThree"]; + lightSpaceMatrixFourParam = Parameters["LightSpaceMatrixFour"]; + + viewMatrixParam = Parameters["ViewMatrix"]; + } + } +} diff --git a/Effects/DeferredPBR_PointLightEffect.cs b/Effects/DeferredPBR_PointLightEffect.cs new file mode 100644 index 0000000..6f959ea --- /dev/null +++ b/Effects/DeferredPBR_PointLightEffect.cs @@ -0,0 +1,77 @@ +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; + +namespace Kav +{ + public class DeferredPBR_PointLightEffect : Effect + { + EffectParameter gPositionParam; + EffectParameter gAlbedoParam; + EffectParameter gNormalParam; + EffectParameter gMetallicRoughnessParam; + + EffectParameter eyePositionParam; + + EffectParameter pointLightColorParam; + EffectParameter pointLightPositionParam; + + public Texture2D GPosition { get; set; } + public Texture2D GAlbedo { get; set; } + public Texture2D GNormal { get; set; } + public Texture2D GMetallicRoughness { get; set; } + + public Vector3 EyePosition { get; set; } + + public Vector3 PointLightPosition { get; set; } + public Vector3 PointLightColor { get; set; } + + public DeferredPBR_PointLightEffect(GraphicsDevice graphicsDevice) : base(graphicsDevice, Resources.DeferredPBR_PointLightEffect) + { + CacheEffectParameters(); + } + + public DeferredPBR_PointLightEffect(DeferredPBR_PointLightEffect cloneSource) : base(cloneSource) + { + GPosition = cloneSource.GPosition; + GAlbedo = cloneSource.GAlbedo; + GNormal = cloneSource.GNormal; + GMetallicRoughness = cloneSource.GMetallicRoughness; + + EyePosition = cloneSource.EyePosition; + + PointLightPosition = cloneSource.PointLightPosition; + PointLightColor = cloneSource.PointLightColor; + } + + public override Effect Clone() + { + return new DeferredPBR_PointLightEffect(this); + } + + protected override void OnApply() + { + gPositionParam.SetValue(GPosition); + gAlbedoParam.SetValue(GAlbedo); + gNormalParam.SetValue(GNormal); + gMetallicRoughnessParam.SetValue(GMetallicRoughness); + + eyePositionParam.SetValue(EyePosition); + + pointLightPositionParam.SetValue(PointLightPosition); + pointLightColorParam.SetValue(PointLightColor); + } + + void CacheEffectParameters() + { + gPositionParam = Parameters["gPosition"]; + gAlbedoParam = Parameters["gAlbedo"]; + gNormalParam = Parameters["gNormal"]; + gMetallicRoughnessParam = Parameters["gMetallicRoughness"]; + + eyePositionParam = Parameters["EyePosition"]; + + pointLightPositionParam = Parameters["PointLightPosition"]; + pointLightColorParam = Parameters["PointLightColor"]; + } + } +} diff --git a/Effects/FXB/DeferredPBR_AmbientLightEffect.fxb b/Effects/FXB/DeferredPBR_AmbientLightEffect.fxb new file mode 100644 index 0000000000000000000000000000000000000000..7cb8ee712be74b75e6f90b30631a3ba3dd848762 GIT binary patch literal 1016 zcmb7DJxc>Y5Z%jZa*7az6e$ExY!re-v=lTSg++vD6zzhVOFT$&;S#J<*c-I5u(-lP z@Gn@1zri11k;1~(PIA7t*$p9$I50c&-ptO<&g~g9;hY_R$em>z7O0FkYfrfMhq1J`iKb2f`1%@As8i`f%YHim;=lpF`B`O;&cwI z%~@Ri^FFHAE?$)En|Cmjk8Et>T+dy{ HC;#DJKYo&2 literal 0 HcmV?d00001 diff --git a/Effects/FXB/DeferredPBR_DirectionalLightEffect.fxb b/Effects/FXB/DeferredPBR_DirectionalLightEffect.fxb new file mode 100644 index 0000000000000000000000000000000000000000..e47ec4036bb500428a8c76598e5740752765984b GIT binary patch literal 6916 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 OLKDro+x+(@=zjr((opOG literal 0 HcmV?d00001 diff --git a/Effects/FXB/DeferredPBR_GBufferEffect.fxb b/Effects/FXB/DeferredPBR_GBufferEffect.fxb index 9b2d6844f486894e8a28f804c05221fee8d81938..413c4150f99c4b125895c10345c85e7fc9caf58a 100644 GIT binary patch delta 146 zcmdmDwZ&?~J2s{U`^f_QDw9Rn(#Vb4j delta 160 zcmdmDwZ&?~J2o~3AZW0koWrFsS%f`}F?6y&ry?^m1HH8KHa~W9VjSZf{1I$N>(W%`#<0Nkc2W&i*H diff --git a/Effects/FXB/DeferredPBR_PointLightEffect.fxb b/Effects/FXB/DeferredPBR_PointLightEffect.fxb new file mode 100644 index 0000000000000000000000000000000000000000..e3457052ceb8360da247256aaa21341ede7f6c7f GIT binary patch literal 3108 zcmb7`&ud&&6vyxTG0n6}Qfn7Q1PAIup-77^Txd;`2nwmhq=D{YY^IZeNnV(YV!_3f zx^N*l=%x!7FOr2!wl0c9bm1S6MZtxeE@siCJ1Iz?pYQ$g^75kuZ+mjj`JSKm+#heE z)5X(zV(b{>ZwkHaMEf`5?-{5ymuV4CH>?U@17y>QL)i=hNm&W@}OI$6l{V@=3XW$7UCrfPAbnbMQT{V*RKw+q@rT z-L>UTnfH^5%_(exZ}8JxUGv)rZ$UP%i*+RLH(wby&I z?bd3qbT9L6pWuJ?fd6#X$-3q=m~i!_I&@~$^TT*(-8pnK&D+gxx4AMmHNRx*0(%aU zje06x{uiJyF2URk0`phU{fhh({KJa;FZ|h%6utpJTakP4UsvR9_`Qmp(vwD|FYmzL ztjJ%&->=AjdtMZ4>vL+ez3NJqTb{LK6Zq<+Qxi@0S08(Ay@J5^%;fF&Tp>OT1e z=ES1o_MTy4W-R97-SBoR7IS&O@L2T>M>d9{A3yfbtvh%7a(RvqJu5pqDK|ychL}h1 zbhvxR*dBZZaU_d^`9+1OlqbLq0V-oDk%HnZEk#%uGhH$IqMm~AYqF0VAZjp=M{ zqs`;nxN`Z8D`w|owmmTu`Ck(sPSS;|#`NEVCWWWqXa2nSEA+1GQ2#gKHE`Kv=kEPd z6nAFsJ^AtKD{Lp{r_as|#A81&^@%+2QH75yd;&kE?KBhkj_?`fI46-AlYBQaPUd!6 z87$}h!@k}}XNmKMOC5dDrv~p^SRKdofG*g{_OLFtIf{N<$w5Bl?N@VfZ_>ooV{@tw z<#ja{*8WxUrr0Ftv_5()d=Rrf>1tjV)>6YRg(OF;MZEGleMJ1K99vm=d9SdKuwBGw zm^(Q}HWfKVF0Ab8jOfkKQnj{(+MGV7?nO&lEC2Cdw3M16cbIY-Xh_^*%lRe9BTFHV z!G^}``mlz`6nC`-z85~X&V%+Q^eFW{A%TiyxN Z^1oZ9XaCDl>izF&>4k+M{_yYp{{XO closestDepth ? 1.0 : 0.0; + } + } + + shadowFactor /= 9.0; + + if (projectionCoords.z > 1.0) + { + shadowFactor = 1.0; + } + + return shadowFactor; +} + +float4 ComputeColor( + float3 worldPosition, + float3 worldNormal, + float3 albedo, + float metallic, + float roughness +) { + float3 V = normalize(EyePosition - worldPosition); + float3 N = normalize(worldNormal); + + float3 F0 = float3(0.04, 0.04, 0.04); + F0 = lerp(F0, albedo, metallic); + + float3 L = normalize(DirectionalLightDirection); + float3 radiance = DirectionalLightColor; + + float shadow = ComputeShadow(worldPosition, N, L); + float3 color = ComputeLight(L, radiance, F0, V, N, albedo, metallic, roughness, (1.0 - shadow)); + + return float4(color, 1.0); +} + +float4 main_ps(PixelInput input) : SV_TARGET0 +{ + float3 worldPosition = SAMPLE_TEXTURE(gPosition, input.TexCoord).rgb; + float3 normal = SAMPLE_TEXTURE(gNormal, input.TexCoord).xyz; + float3 albedo = SAMPLE_TEXTURE(gAlbedo, input.TexCoord).rgb; + float2 metallicRoughness = SAMPLE_TEXTURE(gMetallicRoughness, input.TexCoord).rg; + + return ComputeColor( + worldPosition, + normal, + albedo, + metallicRoughness.r, + metallicRoughness.g + ); +} + +Technique DeferredPBR_Directional +{ + Pass + { + VertexShader = compile vs_3_0 main_vs(); + PixelShader = compile ps_3_0 main_ps(); + } +} diff --git a/Effects/HLSL/DeferredPBR_GBufferEffect.fx b/Effects/HLSL/DeferredPBR_GBufferEffect.fx index 63cb2ce..f24da19 100644 --- a/Effects/HLSL/DeferredPBR_GBufferEffect.fx +++ b/Effects/HLSL/DeferredPBR_GBufferEffect.fx @@ -79,10 +79,10 @@ PixelOutput NonePS(PixelInput input) { PixelOutput output; - output.gPosition = float4(input.PositionWorld, 0.0); - output.gNormal = float4(normalize(input.NormalWorld), 0.0); + output.gPosition = float4(input.PositionWorld, 1.0); + output.gNormal = float4(normalize(input.NormalWorld), 1.0); output.gAlbedo = float4(AlbedoValue, 1.0); - output.gMetallicRoughness = float4(MetallicValue, RoughnessValue, 0.0, 0.0); + output.gMetallicRoughness = float4(MetallicValue, RoughnessValue, 0.0, 1.0); return output; } @@ -91,10 +91,10 @@ PixelOutput AlbedoPS(PixelInput input) { PixelOutput output; - output.gPosition = float4(input.PositionWorld, 0.0); - output.gNormal = float4(normalize(input.NormalWorld), 0.0); + output.gPosition = float4(input.PositionWorld, 1.0); + output.gNormal = float4(normalize(input.NormalWorld), 1.0); output.gAlbedo = SAMPLE_TEXTURE(AlbedoTexture, input.TexCoord); - output.gMetallicRoughness = float4(MetallicValue, RoughnessValue, 0.0, 0.0); + output.gMetallicRoughness = float4(MetallicValue, RoughnessValue, 0.0, 1.0); return output; } @@ -103,8 +103,8 @@ PixelOutput MetallicRoughnessPS(PixelInput input) { PixelOutput output; - output.gPosition = float4(input.PositionWorld, 0.0); - output.gNormal = float4(normalize(input.NormalWorld), 0.0); + output.gPosition = float4(input.PositionWorld, 1.0); + output.gNormal = float4(normalize(input.NormalWorld), 1.0); output.gAlbedo = float4(AlbedoValue, 1.0); output.gMetallicRoughness = SAMPLE_TEXTURE(MetallicRoughnessTexture, input.TexCoord); @@ -115,10 +115,10 @@ PixelOutput NormalPS(PixelInput input) { PixelOutput output; - output.gPosition = float4(input.PositionWorld, 0.0); - output.gNormal = float4(GetNormalFromMap(input.PositionWorld, input.TexCoord, input.NormalWorld), 0.0); + output.gPosition = float4(input.PositionWorld, 1.0); + output.gNormal = float4(GetNormalFromMap(input.PositionWorld, input.TexCoord, input.NormalWorld), 1.0); output.gAlbedo = float4(AlbedoValue, 1.0); - output.gMetallicRoughness = float4(MetallicValue, RoughnessValue, 0.0, 0.0); + output.gMetallicRoughness = float4(MetallicValue, RoughnessValue, 0.0, 1.0); return output; } @@ -127,8 +127,8 @@ PixelOutput AlbedoMetallicRoughnessPS(PixelInput input) { PixelOutput output; - output.gPosition = float4(input.PositionWorld, 0.0); - output.gNormal = float4(normalize(input.NormalWorld), 0.0); + output.gPosition = float4(input.PositionWorld, 1.0); + output.gNormal = float4(normalize(input.NormalWorld), 1.0); output.gAlbedo = SAMPLE_TEXTURE(AlbedoTexture, input.TexCoord); output.gMetallicRoughness = SAMPLE_TEXTURE(MetallicRoughnessTexture, input.TexCoord); @@ -139,10 +139,10 @@ PixelOutput AlbedoNormalPS(PixelInput input) { PixelOutput output; - output.gPosition = float4(input.PositionWorld, 0.0); - output.gNormal = float4(GetNormalFromMap(input.PositionWorld, input.TexCoord, input.NormalWorld), 0.0); + output.gPosition = float4(input.PositionWorld, 1.0); + output.gNormal = float4(GetNormalFromMap(input.PositionWorld, input.TexCoord, input.NormalWorld), 1.0); output.gAlbedo = SAMPLE_TEXTURE(AlbedoTexture, input.TexCoord); - output.gMetallicRoughness = float4(MetallicValue, RoughnessValue, 0.0, 0.0); + output.gMetallicRoughness = float4(MetallicValue, RoughnessValue, 0.0, 1.0); return output; } @@ -151,8 +151,8 @@ PixelOutput MetallicRoughnessNormalPS(PixelInput input) { PixelOutput output; - output.gPosition = float4(input.PositionWorld, 0.0); - output.gNormal = float4(GetNormalFromMap(input.PositionWorld, input.TexCoord, input.NormalWorld), 0.0); + output.gPosition = float4(input.PositionWorld, 1.0); + output.gNormal = float4(GetNormalFromMap(input.PositionWorld, input.TexCoord, input.NormalWorld), 1.0); output.gAlbedo = float4(AlbedoValue, 1.0); output.gMetallicRoughness = SAMPLE_TEXTURE(MetallicRoughnessTexture, input.TexCoord); @@ -163,8 +163,8 @@ PixelOutput AlbedoMetallicRoughnessNormalMapPS(PixelInput input) { PixelOutput output; - output.gPosition = float4(input.PositionWorld, 0.0); - output.gNormal = float4(GetNormalFromMap(input.PositionWorld, input.TexCoord, input.NormalWorld), 0.0); + output.gPosition = float4(input.PositionWorld, 1.0); + output.gNormal = float4(GetNormalFromMap(input.PositionWorld, input.TexCoord, input.NormalWorld), 1.0); output.gAlbedo = SAMPLE_TEXTURE(AlbedoTexture, input.TexCoord); output.gMetallicRoughness = SAMPLE_TEXTURE(MetallicRoughnessTexture, input.TexCoord); diff --git a/Effects/HLSL/DeferredPBR_PointLightEffect.fx b/Effects/HLSL/DeferredPBR_PointLightEffect.fx new file mode 100644 index 0000000..fba3c6e --- /dev/null +++ b/Effects/HLSL/DeferredPBR_PointLightEffect.fx @@ -0,0 +1,91 @@ +#include "Macros.fxh" //from FNA +#include "Lighting.fxh" + +DECLARE_TEXTURE(gPosition, 0); +DECLARE_TEXTURE(gAlbedo, 1); +DECLARE_TEXTURE(gNormal, 2); +DECLARE_TEXTURE(gMetallicRoughness, 3); + +BEGIN_CONSTANTS + + float3 EyePosition _ps(c0) _cb(c0); + + float3 PointLightPosition _ps(c1) _cb(c1); + float3 PointLightColor _ps(c2) _cb(c2); + +MATRIX_CONSTANTS + +END_CONSTANTS + +struct VertexInput +{ + float4 Position : POSITION; + float2 TexCoord : TEXCOORD; +}; + +struct PixelInput +{ + float4 Position : SV_POSITION; + float2 TexCoord : TEXCOORD0; +}; + +PixelInput main_vs(VertexInput input) +{ + PixelInput output; + + output.Position = input.Position; + output.TexCoord = input.TexCoord; + + return output; +} + +// Pixel Shader + +float4 ComputeColor( + float3 worldPosition, + float3 worldNormal, + float3 albedo, + float metallic, + float roughness +) { + float3 V = normalize(EyePosition - worldPosition); + float3 N = normalize(worldNormal); + + float3 F0 = float3(0.04, 0.04, 0.04); + F0 = lerp(F0, albedo, metallic); + + float3 lightDir = PointLightPosition - worldPosition; + float3 L = normalize(lightDir); + float distance = length(lightDir); + float attenuation = 1.0 / (distance * distance); + float3 radiance = PointLightColor * attenuation; + + float3 color = ComputeLight(L, radiance, F0, V, N, albedo, metallic, roughness, 1.0); + + return float4(color, 1.0); +} + +float4 main_ps(PixelInput input) : SV_TARGET0 +{ + float3 worldPosition = SAMPLE_TEXTURE(gPosition, input.TexCoord).rgb; + float3 normal = SAMPLE_TEXTURE(gNormal, input.TexCoord).xyz; + float3 albedo = SAMPLE_TEXTURE(gAlbedo, input.TexCoord).rgb; + float2 metallicRoughness = SAMPLE_TEXTURE(gMetallicRoughness, input.TexCoord).rg; + + return ComputeColor( + worldPosition, + normal, + albedo, + metallicRoughness.r, + metallicRoughness.g + ); +} + +Technique DeferredPBR_Point +{ + Pass + { + VertexShader = compile vs_3_0 main_vs(); + PixelShader = compile ps_3_0 main_ps(); + } +} diff --git a/Effects/HLSL/Lighting.fxh b/Effects/HLSL/Lighting.fxh new file mode 100644 index 0000000..1cc6ae8 --- /dev/null +++ b/Effects/HLSL/Lighting.fxh @@ -0,0 +1,71 @@ +static const float PI = 3.141592653589793; + +float3 FresnelSchlick(float cosTheta, float3 F0) +{ + return F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0); +} + +float DistributionGGX(float3 N, float3 H, float roughness) +{ + float a = roughness * roughness; + float a2 = a * a; + float NdotH = max(dot(N, H), 0.0); + float NdotH2 = NdotH * NdotH; + + float num = a2; + float denom = (NdotH2 * (a2 - 1.0) + 1.0); + denom = PI * denom * denom; + + return num / denom; +} + +float GeometrySchlickGGX(float NdotV, float roughness) +{ + float r = (roughness + 1.0); + float k = (r * r) / 8.0; + + float num = NdotV; + float denom = NdotV * (1.0 - k) + k; + + return num / denom; +} + +float GeometrySmith(float3 N, float3 V, float3 L, float roughness) +{ + float NdotV = max(dot(N, V), 0.0); + float NdotL = max(dot(N, L), 0.0); + float ggx2 = GeometrySchlickGGX(NdotV, roughness); + float ggx1 = GeometrySchlickGGX(NdotL, roughness); + + return ggx1 * ggx2; +} + +float3 ComputeLight( + float3 L, + float3 radiance, + float3 F0, + float3 V, + float3 N, + float3 albedo, + float metallic, + float roughness, + float shadow +) { + float3 H = normalize(V + L); + + float NDF = DistributionGGX(N, H, roughness); + float G = GeometrySmith(N, V, L, roughness); + float3 F = FresnelSchlick(max(dot(H, V), 0.0), F0); + + float3 numerator = NDF * G * F; + float denominator = 4.0 * max(dot(N, V), 0.0) * max(dot(N, L), 0.0); + float3 specular = numerator / max(denominator, 0.001); + + float3 kS = F; + float3 kD = float3(1.0, 1.0, 1.0) - kS; + + kD *= 1.0 - metallic; + + float NdotL = max(dot(N, L), 0.0); + return (kD * albedo / PI + specular) * radiance * NdotL * shadow; +} diff --git a/Kav.Core.csproj b/Kav.Core.csproj index 7957739..c7c437f 100644 --- a/Kav.Core.csproj +++ b/Kav.Core.csproj @@ -15,6 +15,15 @@ + + Kav.Resources.DeferredPBR_AmbientLightEffect.fxb + + + Kav.Resources.DeferredPBR_PointLightEffect.fxb + + + Kav.Resources.DeferredPBR_DirectionalLightEffect.fxb + Kav.Resources.DeferredPBR_GBufferEffect.fxb diff --git a/Kav.Framework.csproj b/Kav.Framework.csproj index 0557675..a98bb21 100644 --- a/Kav.Framework.csproj +++ b/Kav.Framework.csproj @@ -15,6 +15,15 @@ + + Kav.Resources.DeferredPBR_AmbientLightEffect.fxb + + + Kav.Resources.DeferredPBR_PointLightEffect.fxb + + + Kav.Resources.DeferredPBR_DirectionalLightEffect.fxb + Kav.Resources.DeferredPBR_GBufferEffect.fxb diff --git a/README.md b/README.md index 7a2bff0..bf40a9f 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ Essential - [x] Directional lighting - [x] Directional shadow maps - [x] Cascading shadow maps +- [ ] Tone map shader - [ ] Frustum culling - [ ] PCF soft shadowing - [ ] Shadow-casting point lights diff --git a/Renderer.cs b/Renderer.cs index b99fe02..82a9a0f 100644 --- a/Renderer.cs +++ b/Renderer.cs @@ -8,26 +8,32 @@ namespace Kav { private const int MAX_SHADOW_CASCADES = 4; - private GraphicsDevice GraphicsDevice { get; } private int RenderDimensionsX { get; } private int RenderDimensionsY { get; } private VertexBuffer FullscreenTriangle { get; } private int NumShadowCascades { get; } + + private RenderTarget2D ColorRenderTarget { get; } + private RenderTarget2D DirectionalRenderTarget { get; } private RenderTarget2D[] ShadowRenderTargets { get; } private DeferredPBREffect DeferredPBREffect { get; } + private DeferredPBR_AmbientLightEffect DeferredAmbientLightEffect { get; } + private DeferredPBR_PointLightEffect DeferredPointLightEffect { get; } + private DeferredPBR_DirectionalLightEffect DeferredDirectionalLightEffect { get; } private SimpleDepthEffect SimpleDepthEffect { get; } private RenderTarget2D gPosition { get; } private RenderTarget2D gNormal { get; } private RenderTarget2D gAlbedo { get; } private RenderTarget2D gMetallicRoughness { get; } - private RenderTarget2D deferredRenderTarget { get; } private RenderTargetBinding[] GBuffer { get; } + private SpriteBatch SpriteBatch { get; } + public Renderer(GraphicsDevice graphicsDevice, int renderDimensionsX, int renderDimensionsY, int numShadowCascades) { GraphicsDevice = graphicsDevice; @@ -49,6 +55,28 @@ namespace Kav ); } + ColorRenderTarget = new RenderTarget2D( + graphicsDevice, + renderDimensionsX, + renderDimensionsY, + false, + SurfaceFormat.Color, + DepthFormat.None, + 0, + RenderTargetUsage.PreserveContents + ); + + DirectionalRenderTarget = new RenderTarget2D( + graphicsDevice, + renderDimensionsX, + renderDimensionsY, + false, + SurfaceFormat.Color, + DepthFormat.None, + 0, + RenderTargetUsage.PreserveContents + ); + gPosition = new RenderTarget2D( GraphicsDevice, renderDimensionsX, @@ -91,26 +119,21 @@ namespace Kav new RenderTargetBinding(gAlbedo), new RenderTargetBinding(gMetallicRoughness) }; - - deferredRenderTarget = new RenderTarget2D( - GraphicsDevice, - renderDimensionsX, - renderDimensionsY - ); SimpleDepthEffect = new SimpleDepthEffect(GraphicsDevice); DeferredPBREffect = new DeferredPBREffect(GraphicsDevice); + DeferredAmbientLightEffect = new DeferredPBR_AmbientLightEffect(GraphicsDevice); + DeferredPointLightEffect = new DeferredPBR_PointLightEffect(GraphicsDevice); + DeferredDirectionalLightEffect = new DeferredPBR_DirectionalLightEffect(GraphicsDevice); FullscreenTriangle = new VertexBuffer(GraphicsDevice, typeof(VertexPositionTexture), 3, BufferUsage.WriteOnly); FullscreenTriangle.SetData(new VertexPositionTexture[3] { - new VertexPositionTexture(new Vector3(-1, -1, 0), new Vector2(0, 0)), - new VertexPositionTexture(new Vector3(-1, 3, 0), new Vector2(0, -2)), - new VertexPositionTexture(new Vector3(3, -1, 0), new Vector2(2, 0)) + new VertexPositionTexture(new Vector3(-1, -3, 0), new Vector2(0, 2)), + new VertexPositionTexture(new Vector3(-1, 1, 0), new Vector2(0, 0)), + new VertexPositionTexture(new Vector3(3, 1, 0), new Vector2(2, 0)) }); - GraphicsDevice.SetRenderTarget(deferredRenderTarget); - graphicsDevice.Clear(Color.White); - GraphicsDevice.SetRenderTarget(null); + SpriteBatch = new SpriteBatch(graphicsDevice); } public void DeferredRender( @@ -119,7 +142,7 @@ namespace Kav IEnumerable pointLights, DirectionalLight directionalLight ) { - ShadowMapRender(camera, modelTransforms, directionalLight); + // g-buffer pass GraphicsDevice.SetRenderTargets(GBuffer); GraphicsDevice.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, Color.Black, 1f, 0); @@ -159,45 +182,54 @@ namespace Kav } } - GraphicsDevice.SetRenderTarget(null); - GraphicsDevice.Clear(Color.CornflowerBlue); + GraphicsDevice.SetRenderTarget(ColorRenderTarget); + GraphicsDevice.Clear(Color.Black); + GraphicsDevice.BlendState = BlendState.Additive; + GraphicsDevice.DepthStencilState = DepthStencilState.None; - DeferredPBREffect.GPosition = gPosition; - DeferredPBREffect.GAlbedo = gAlbedo; - DeferredPBREffect.GNormal = gNormal; - DeferredPBREffect.GMetallicRoughness = gMetallicRoughness; - - DeferredPBREffect.ShadowMapOne = ShadowRenderTargets[0]; - if (NumShadowCascades > 1) + DeferredAmbientLightEffect.GPosition = gPosition; + DeferredAmbientLightEffect.GAlbedo = gAlbedo; + + foreach (var pass in DeferredAmbientLightEffect.CurrentTechnique.Passes) { - DeferredPBREffect.ShadowMapTwo = ShadowRenderTargets[1]; - } - if (NumShadowCascades > 2) - { - DeferredPBREffect.ShadowMapThree = ShadowRenderTargets[2]; - } - if (NumShadowCascades > 3) - { - DeferredPBREffect.ShadowMapFour = ShadowRenderTargets[3]; + pass.Apply(); + GraphicsDevice.SetVertexBuffer(FullscreenTriangle); + GraphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, 1); } - DeferredPBREffect.ViewMatrix = camera.View; + DeferredPointLightEffect.EyePosition = camera.Position; - DeferredPBREffect.EyePosition = Matrix.Invert(camera.View).Translation; - - int i = 0; foreach (var pointLight in pointLights) { - if (i > DeferredPBREffect.MaxPointLights) { break; } - DeferredPBREffect.PointLights[i] = pointLight; - i++; + PointLightRender(pointLight); } - DeferredPBREffect.DirectionalLightColor = directionalLight.Color.ToVector3() * directionalLight.Intensity; - DeferredPBREffect.DirectionalLightDirection = directionalLight.Direction; + DirectionalLightRender(camera, modelTransforms, directionalLight); + // return; + // GraphicsDevice.SetRenderTarget(null); + // SpriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied); + // SpriteBatch.Draw(DirectionalRenderTarget, Vector2.Zero, Color.White); + // SpriteBatch.End(); + GraphicsDevice.SetRenderTarget(null); + GraphicsDevice.Clear(Color.Black); + SpriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Opaque); + SpriteBatch.Draw(ColorRenderTarget, Vector2.Zero, Color.White); + SpriteBatch.End(); + } - foreach (EffectPass pass in DeferredPBREffect.CurrentTechnique.Passes) + private void PointLightRender(PointLight pointLight) + { + DeferredPointLightEffect.GPosition = gPosition; + DeferredPointLightEffect.GAlbedo = gAlbedo; + DeferredPointLightEffect.GNormal = gNormal; + DeferredPointLightEffect.GMetallicRoughness = gMetallicRoughness; + + DeferredPointLightEffect.PointLightPosition = pointLight.Position; + DeferredPointLightEffect.PointLightColor = + pointLight.Color.ToVector3() * pointLight.Intensity; + + foreach (var pass in DeferredPointLightEffect.CurrentTechnique.Passes) { pass.Apply(); GraphicsDevice.SetVertexBuffer(FullscreenTriangle); @@ -205,12 +237,12 @@ namespace Kav } } - public void ShadowMapRender( - PerspectiveCamera camera, - IEnumerable<(Model, Matrix)> modelTransforms, + private void DirectionalLightRender( + PerspectiveCamera camera, + IEnumerable<(Model, Matrix)> modelTransforms, DirectionalLight directionalLight ) { - // render the individual shadow maps + // render the individual shadow cascades var previousFarPlane = camera.NearPlane; for (var i = 0; i < NumShadowCascades; i++) { @@ -227,10 +259,47 @@ namespace Kav farPlane ); + // TODO: This is tightly coupled to the effect and it sucks RenderShadowMap(shadowCamera, modelTransforms, directionalLight, i); previousFarPlane = farPlane; } + + DeferredDirectionalLightEffect.GPosition = gPosition; + DeferredDirectionalLightEffect.GAlbedo = gAlbedo; + DeferredDirectionalLightEffect.GNormal = gNormal; + DeferredDirectionalLightEffect.GMetallicRoughness = gMetallicRoughness; + + DeferredDirectionalLightEffect.ShadowMapOne = ShadowRenderTargets[0]; + if (NumShadowCascades > 1) + { + DeferredDirectionalLightEffect.ShadowMapTwo = ShadowRenderTargets[1]; + } + if (NumShadowCascades > 2) + { + DeferredDirectionalLightEffect.ShadowMapThree = ShadowRenderTargets[2]; + } + if (NumShadowCascades > 3) + { + DeferredDirectionalLightEffect.ShadowMapFour = ShadowRenderTargets[3]; + } + + DeferredDirectionalLightEffect.DirectionalLightDirection = directionalLight.Direction; + DeferredDirectionalLightEffect.DirectionalLightColor = + directionalLight.Color.ToVector3() * directionalLight.Intensity; + + DeferredDirectionalLightEffect.ViewMatrix = camera.View; + DeferredDirectionalLightEffect.EyePosition = Matrix.Invert(camera.View).Translation; + + GraphicsDevice.SetRenderTarget(ColorRenderTarget); + GraphicsDevice.BlendState = BlendState.Additive; + + foreach (EffectPass pass in DeferredDirectionalLightEffect.CurrentTechnique.Passes) + { + pass.Apply(); + GraphicsDevice.SetVertexBuffer(FullscreenTriangle); + GraphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, 1); + } } private void RenderShadowMap( @@ -277,22 +346,22 @@ namespace Kav if (shadowCascadeIndex == 0) { - DeferredPBREffect.LightSpaceMatrixOne = lightSpaceMatrix; + DeferredDirectionalLightEffect.LightSpaceMatrixOne = lightSpaceMatrix; } else if (shadowCascadeIndex == 1) { - DeferredPBREffect.LightSpaceMatrixTwo = lightSpaceMatrix; + DeferredDirectionalLightEffect.LightSpaceMatrixTwo = lightSpaceMatrix; } else if (shadowCascadeIndex == 2) { - DeferredPBREffect.LightSpaceMatrixThree = lightSpaceMatrix; + DeferredDirectionalLightEffect.LightSpaceMatrixThree = lightSpaceMatrix; } else if (shadowCascadeIndex == 3) { - DeferredPBREffect.LightSpaceMatrixFour = lightSpaceMatrix; + DeferredDirectionalLightEffect.LightSpaceMatrixFour = lightSpaceMatrix; } - DeferredPBREffect.CascadeFarPlanes[shadowCascadeIndex] = camera.FarPlane; + DeferredDirectionalLightEffect.CascadeFarPlanes[shadowCascadeIndex] = camera.FarPlane; foreach (var (model, transform) in modelTransforms) { diff --git a/Resources.cs b/Resources.cs index 83d4b04..9ac9358 100644 --- a/Resources.cs +++ b/Resources.cs @@ -4,6 +4,41 @@ namespace Kav { internal class Resources { + public static byte[] DeferredPBR_AmbientLightEffect + { + get + { + if (ambientLightEffect == null) + { + ambientLightEffect = GetResource("DeferredPBR_AmbientLightEffect"); + } + return ambientLightEffect; + } + } + public static byte[] DeferredPBR_PointLightEffect + { + get + { + if (pointLightEffect == null) + { + pointLightEffect = GetResource("DeferredPBR_PointLightEffect"); + } + return pointLightEffect; + } + } + + public static byte[] DeferredPBR_DirectionalLightEffect + { + get + { + if (directionalLightEffect == null) + { + directionalLightEffect = GetResource("DeferredPBR_DirectionalLightEffect"); + } + return directionalLightEffect; + } + } + public static byte[] DeferredPBR_GBufferEffect { get @@ -52,6 +87,9 @@ namespace Kav } } + private static byte[] ambientLightEffect; + private static byte[] pointLightEffect; + private static byte[] directionalLightEffect; private static byte[] gBufferEffect; private static byte[] deferredPBREffect; private static byte[] pbrEffect; -- 2.25.1 From 7e82f2f32ef039ab1432eb45b2f5fc02fe617e90 Mon Sep 17 00:00:00 2001 From: cosmonaut Date: Sun, 20 Sep 2020 20:50:55 -0700 Subject: [PATCH 11/15] initial soft shadow implementation --- Effects/DeferredPBR_DirectionalLightEffect.cs | 9 ++ .../DeferredPBR_DirectionalLightEffect.fxb | Bin 6916 -> 11216 bytes .../DeferredPBR_DirectionalLightEffect.fx | 138 +++++++++++++----- Effects/HLSL/Lighting.fxh | 4 +- Effects/HLSL/Macros.fxh | 1 + Renderer.cs | 18 ++- 6 files changed, 126 insertions(+), 44 deletions(-) diff --git a/Effects/DeferredPBR_DirectionalLightEffect.cs b/Effects/DeferredPBR_DirectionalLightEffect.cs index fb24e78..5e1f919 100644 --- a/Effects/DeferredPBR_DirectionalLightEffect.cs +++ b/Effects/DeferredPBR_DirectionalLightEffect.cs @@ -22,6 +22,8 @@ namespace Kav EffectParameter cascadeFarPlanesParam; + EffectParameter shadowMapSizeParam; + EffectParameter lightSpaceMatrixOneParam; EffectParameter lightSpaceMatrixTwoParam; EffectParameter lightSpaceMatrixThreeParam; @@ -46,6 +48,8 @@ namespace Kav public readonly float[] CascadeFarPlanes; + public int ShadowMapSize { get; set; } + public Matrix LightSpaceMatrixOne { get; set; } public Matrix LightSpaceMatrixTwo { get; set; } public Matrix LightSpaceMatrixThree { get; set; } @@ -82,6 +86,8 @@ namespace Kav CascadeFarPlanes[i] = cloneSource.CascadeFarPlanes[i]; } + ShadowMapSize = cloneSource.ShadowMapSize; + LightSpaceMatrixOne = cloneSource.LightSpaceMatrixOne; LightSpaceMatrixTwo = cloneSource.LightSpaceMatrixTwo; LightSpaceMatrixThree = cloneSource.LightSpaceMatrixThree; @@ -113,6 +119,7 @@ namespace Kav directionalLightColorParam.SetValue(DirectionalLightColor); cascadeFarPlanesParam.SetValue(CascadeFarPlanes); + shadowMapSizeParam.SetValue(ShadowMapSize); lightSpaceMatrixOneParam.SetValue(LightSpaceMatrixOne); lightSpaceMatrixTwoParam.SetValue(LightSpaceMatrixTwo); @@ -141,6 +148,8 @@ namespace Kav cascadeFarPlanesParam = Parameters["CascadeFarPlanes"]; + shadowMapSizeParam = Parameters["ShadowMapSize"]; + lightSpaceMatrixOneParam = Parameters["LightSpaceMatrixOne"]; lightSpaceMatrixTwoParam = Parameters["LightSpaceMatrixTwo"]; lightSpaceMatrixThreeParam = Parameters["LightSpaceMatrixThree"]; diff --git a/Effects/FXB/DeferredPBR_DirectionalLightEffect.fxb b/Effects/FXB/DeferredPBR_DirectionalLightEffect.fxb index e47ec4036bb500428a8c76598e5740752765984b..adbc9a72a67b265ca1cb5176c01e4f14c0fae6d8 100644 GIT binary patch literal 11216 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 OLKDro+x+(@=zjr((opOG diff --git a/Effects/HLSL/DeferredPBR_DirectionalLightEffect.fx b/Effects/HLSL/DeferredPBR_DirectionalLightEffect.fx index 77a3860..06f3a6b 100644 --- a/Effects/HLSL/DeferredPBR_DirectionalLightEffect.fx +++ b/Effects/HLSL/DeferredPBR_DirectionalLightEffect.fx @@ -21,18 +21,40 @@ BEGIN_CONSTANTS float CascadeFarPlanes[NUM_SHADOW_CASCADES] _ps(c3) _cb(c3); + float ShadowMapSize _ps(c7) _cb(c7); + MATRIX_CONSTANTS - float4x4 LightSpaceMatrixOne _ps(c7) _cb(c7); - float4x4 LightSpaceMatrixTwo _ps(c11) _cb(c11); - float4x4 LightSpaceMatrixThree _ps(c15) _cb(c15); - float4x4 LightSpaceMatrixFour _ps(c19) _cb(c19); + float4x4 LightSpaceMatrixOne _ps(c8) _cb(c8); + float4x4 LightSpaceMatrixTwo _ps(c12) _cb(c12); + float4x4 LightSpaceMatrixThree _ps(c16) _cb(c16); + float4x4 LightSpaceMatrixFour _ps(c20) _cb(c20); // used to select shadow cascade - float4x4 ViewMatrix _ps(c23) _cb(c23); + float4x4 ViewMatrix _ps(c24) _cb(c24); END_CONSTANTS +static float2 poissonDisk[16] = +{ + float2( -0.94201624, -0.39906216 ), + float2( 0.94558609, -0.76890725 ), + float2( -0.094184101, -0.92938870 ), + float2( 0.34495938, 0.29387760 ), + float2( -0.91588581, 0.45771432 ), + float2( -0.81544232, -0.87912464 ), + float2( -0.38277543, 0.27676845 ), + float2( 0.97484398, 0.75648379 ), + float2( 0.44323325, -0.97511554 ), + float2( 0.53742981, -0.47373420 ), + float2( -0.26496911, -0.41893023 ), + float2( 0.79197514, 0.19090188 ), + float2( -0.24188840, 0.99706507 ), + float2( -0.81409955, 0.91437590 ), + float2( 0.19984126, 0.78641367 ), + float2( 0.14383161, -0.14100790 ) +}; + struct VertexInput { float4 Position : POSITION; @@ -57,6 +79,30 @@ PixelInput main_vs(VertexInput input) // Pixel Shader +// Returns a random number based on a vec3 and an int. +float random(float3 seed, int i){ + float4 seed4 = float4(seed, i); + float dot_product = dot(seed4, float4(12.9898,78.233,45.164,94.673)); + return frac(sin(dot_product) * 43758.5453); +} + +float PoissonCoord(sampler shadowMap, float3 worldPosition, float2 texCoord, float fragmentDepth, float bias) +{ + float visibility = 1.0; + + for (int i = 0; i < 16; i++) + { + int index = int(16.0 * random(floor(worldPosition * 1000.0), i)) % 16; + + if (tex2D(shadowMap, texCoord + poissonDisk[i] / 700.0).r < fragmentDepth - bias) + { + visibility -= 0.2; + } + } + + return visibility; +} + float ComputeShadow(float3 positionWorldSpace, float3 N, float3 L) { float bias = 0.005 * tan(acos(dot(N, L))); @@ -104,42 +150,58 @@ float ComputeShadow(float3 positionWorldSpace, float3 N, float3 L) projectionCoords.y *= -1; // in XNA clip z is 0 to 1 already - float inc = 1.0 / 1024.0; - - float shadowFactor = 0; - for (int row = -1; row <= 1; row++) - { - for (int col = -1; col <= 1; col++) - { - float closestDepth; - if (shadowCascadeIndex == 0) - { - closestDepth = SAMPLE_TEXTURE(shadowMapOne, projectionCoords.xy + float2(row, col) * inc).r; - } - else if (shadowCascadeIndex == 1) - { - closestDepth = SAMPLE_TEXTURE(shadowMapTwo, projectionCoords.xy + float2(row, col) * inc).r; - } - else if (shadowCascadeIndex == 2) - { - closestDepth = SAMPLE_TEXTURE(shadowMapThree, projectionCoords.xy + float2(row, col) * inc).r; - } - else - { - closestDepth = SAMPLE_TEXTURE(shadowMapFour, projectionCoords.xy + float2(row, col) * inc).r; - } - shadowFactor += projectionCoords.z - bias > closestDepth ? 1.0 : 0.0; - } - } - - shadowFactor /= 9.0; - if (projectionCoords.z > 1.0) { - shadowFactor = 1.0; + return 1.0; } - return shadowFactor; + float inc = 1.0 / ShadowMapSize; // TODO: shadow map size uniform + + // PCF + Poisson soft shadows + float visibility = 0.0; + // for (int row = -1; row <= 1; row++) + // { + // for (int col = -1; col <= 1; col++) + // { + // if (shadowCascadeIndex == 0) + // { + // visibility += PoissonCoord(SAMPLER(shadowMapOne), positionWorldSpace, projectionCoords.xy + float2(row, col) * inc, projectionCoords.z, bias); + // } + // else if (shadowCascadeIndex == 1) + // { + // visibility += PoissonCoord(SAMPLER(shadowMapTwo), positionWorldSpace, projectionCoords.xy + float2(row, col) * inc, projectionCoords.z, bias); + // } + // else if (shadowCascadeIndex == 2) + // { + // visibility += PoissonCoord(SAMPLER(shadowMapThree), positionWorldSpace, projectionCoords.xy + float2(row, col) * inc, projectionCoords.z, bias); + // } + // else + // { + // visibility += PoissonCoord(SAMPLER(shadowMapFour), positionWorldSpace, projectionCoords.xy + float2(row, col) * inc, projectionCoords.z, bias); + // } + // } + // } + + // visibility /= 9.0; + + if (shadowCascadeIndex == 0) + { + visibility = PoissonCoord(SAMPLER(shadowMapOne), positionWorldSpace, projectionCoords.xy, projectionCoords.z, bias); + } + else if (shadowCascadeIndex == 1) + { + visibility = PoissonCoord(SAMPLER(shadowMapTwo), positionWorldSpace, projectionCoords.xy, projectionCoords.z, bias); + } + else if (shadowCascadeIndex == 2) + { + visibility = PoissonCoord(SAMPLER(shadowMapThree), positionWorldSpace, projectionCoords.xy, projectionCoords.z, bias); + } + else + { + visibility = PoissonCoord(SAMPLER(shadowMapFour), positionWorldSpace, projectionCoords.xy, projectionCoords.z, bias); + } + + return visibility; } float4 ComputeColor( @@ -159,7 +221,7 @@ float4 ComputeColor( float3 radiance = DirectionalLightColor; float shadow = ComputeShadow(worldPosition, N, L); - float3 color = ComputeLight(L, radiance, F0, V, N, albedo, metallic, roughness, (1.0 - shadow)); + float3 color = ComputeLight(L, radiance, F0, V, N, albedo, metallic, roughness, shadow); return float4(color, 1.0); } diff --git a/Effects/HLSL/Lighting.fxh b/Effects/HLSL/Lighting.fxh index 1cc6ae8..e57b391 100644 --- a/Effects/HLSL/Lighting.fxh +++ b/Effects/HLSL/Lighting.fxh @@ -49,7 +49,7 @@ float3 ComputeLight( float3 albedo, float metallic, float roughness, - float shadow + float visibility ) { float3 H = normalize(V + L); @@ -67,5 +67,5 @@ float3 ComputeLight( kD *= 1.0 - metallic; float NdotL = max(dot(N, L), 0.0); - return (kD * albedo / PI + specular) * radiance * NdotL * shadow; + return (kD * albedo / PI + specular) * radiance * NdotL * visibility; } diff --git a/Effects/HLSL/Macros.fxh b/Effects/HLSL/Macros.fxh index 91d1702..c0aaaac 100644 --- a/Effects/HLSL/Macros.fxh +++ b/Effects/HLSL/Macros.fxh @@ -54,4 +54,5 @@ #define SAMPLE_TEXTURE(Name, texCoord) tex2D(Name##Sampler, texCoord) #define SAMPLE_CUBEMAP(Name, texCoord) texCUBE(Name##Sampler, texCoord) #define SAMPLE_CUBEMAP_LOD(Name, texCoord) texCUBElod(Name##Sampler, texCoord) +#define SAMPLER(Name) Name##Sampler #endif diff --git a/Renderer.cs b/Renderer.cs index 82a9a0f..37eead5 100644 --- a/Renderer.cs +++ b/Renderer.cs @@ -7,6 +7,7 @@ namespace Kav public class Renderer { private const int MAX_SHADOW_CASCADES = 4; + private int ShadowMapSize { get; } private GraphicsDevice GraphicsDevice { get; } private int RenderDimensionsX { get; } @@ -34,12 +35,19 @@ namespace Kav private SpriteBatch SpriteBatch { get; } - public Renderer(GraphicsDevice graphicsDevice, int renderDimensionsX, int renderDimensionsY, int numShadowCascades) - { + public Renderer( + GraphicsDevice graphicsDevice, + int renderDimensionsX, + int renderDimensionsY, + int numShadowCascades, + int shadowMapSize + ) { GraphicsDevice = graphicsDevice; RenderDimensionsX = renderDimensionsX; RenderDimensionsY = renderDimensionsY; + ShadowMapSize = shadowMapSize; + NumShadowCascades = (int)MathHelper.Clamp(numShadowCascades, 1, MAX_SHADOW_CASCADES); ShadowRenderTargets = new RenderTarget2D[numShadowCascades]; @@ -47,8 +55,8 @@ namespace Kav { ShadowRenderTargets[i] = new RenderTarget2D( GraphicsDevice, - 1024, - 1024, + ShadowMapSize, + ShadowMapSize, false, SurfaceFormat.Single, DepthFormat.Depth24 @@ -126,6 +134,8 @@ namespace Kav DeferredPointLightEffect = new DeferredPBR_PointLightEffect(GraphicsDevice); DeferredDirectionalLightEffect = new DeferredPBR_DirectionalLightEffect(GraphicsDevice); + DeferredDirectionalLightEffect.ShadowMapSize = ShadowMapSize; + FullscreenTriangle = new VertexBuffer(GraphicsDevice, typeof(VertexPositionTexture), 3, BufferUsage.WriteOnly); FullscreenTriangle.SetData(new VertexPositionTexture[3] { new VertexPositionTexture(new Vector3(-1, -3, 0), new Vector2(0, 2)), -- 2.25.1 From a341d3ba93336e90b122bbc4e7352729faaa95ea Mon Sep 17 00:00:00 2001 From: cosmonaut Date: Sun, 20 Sep 2020 22:41:36 -0700 Subject: [PATCH 12/15] poisson sampled soft shadows --- .../DeferredPBR_DirectionalLightEffect.fxb | Bin 11216 -> 21692 bytes .../DeferredPBR_DirectionalLightEffect.fx | 4 ++-- Renderer.cs | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Effects/FXB/DeferredPBR_DirectionalLightEffect.fxb b/Effects/FXB/DeferredPBR_DirectionalLightEffect.fxb index adbc9a72a67b265ca1cb5176c01e4f14c0fae6d8..d3bd6a251c5496c647cf9c207303fcc040e8329d 100644 GIT binary patch literal 21692 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 kpNNS#rUuM!Oo Date: Sun, 20 Sep 2020 23:01:13 -0700 Subject: [PATCH 13/15] tone mapping --- Effects/FXB/ToneMapEffect.fxb | Bin 0 -> 584 bytes Effects/HLSL/ToneMapEffect.fx | 20 ++++++++++++++++++++ Kav.Core.csproj | 3 +++ Kav.Framework.csproj | 3 +++ Renderer.cs | 5 +++-- Resources.cs | 13 +++++++++++++ 6 files changed, 42 insertions(+), 2 deletions(-) create mode 100644 Effects/FXB/ToneMapEffect.fxb create mode 100644 Effects/HLSL/ToneMapEffect.fx diff --git a/Effects/FXB/ToneMapEffect.fxb b/Effects/FXB/ToneMapEffect.fxb new file mode 100644 index 0000000000000000000000000000000000000000..dde4f5fa1bde7d2572e995635e6ad9576a56e110 GIT binary patch literal 584 zcmZ9JzfQw25XR3*fPfHL8ITAYERYIj{^>EmFrkLZ!YyX8Hy^ zPY0f&=DXMt5f`2O^WA;+og?=3_LYnsVDaE5c3&+4pf4BmPbK5wH2aW>NFc^>uCE)% z*$&~4ax9gSqi(ZT)^pB5a^y$E!3kCpA;jB5{5i1IV}Y&N(aMD#RyQ|T-n&%^WHMcD4doiKhM zj-(1(=`@?*dBR3?*`W0^(GFgzAj literal 0 HcmV?d00001 diff --git a/Effects/HLSL/ToneMapEffect.fx b/Effects/HLSL/ToneMapEffect.fx new file mode 100644 index 0000000..281b714 --- /dev/null +++ b/Effects/HLSL/ToneMapEffect.fx @@ -0,0 +1,20 @@ +sampler TextureSampler : register(s0); + +float4 main_ps(float2 texCoord : TEXCOORD0) : COLOR0 +{ + float3 color = tex2D(TextureSampler, texCoord).xyz; + + color = color / (color + float3(1.0, 1.0, 1.0)); + float exposureConstant = 1.0 / 2.2; + color = pow(color, float3(exposureConstant, exposureConstant, exposureConstant)); + + return float4(color, 1.0); +} + +Technique DeferredPBR +{ + Pass + { + PixelShader = compile ps_3_0 main_ps(); + } +} diff --git a/Kav.Core.csproj b/Kav.Core.csproj index c7c437f..327d7bb 100644 --- a/Kav.Core.csproj +++ b/Kav.Core.csproj @@ -27,6 +27,9 @@ Kav.Resources.DeferredPBR_GBufferEffect.fxb + + Kav.Resources.ToneMapEffect.fxb + Kav.Resources.DeferredPBREffect.fxb diff --git a/Kav.Framework.csproj b/Kav.Framework.csproj index a98bb21..2b2dd4f 100644 --- a/Kav.Framework.csproj +++ b/Kav.Framework.csproj @@ -27,6 +27,9 @@ Kav.Resources.DeferredPBR_GBufferEffect.fxb + + Kav.Resources.ToneMapEffect.fxb + Kav.Resources.DeferredPBREffect.fxb diff --git a/Renderer.cs b/Renderer.cs index 0a77713..63b34e5 100644 --- a/Renderer.cs +++ b/Renderer.cs @@ -25,6 +25,7 @@ namespace Kav private DeferredPBR_PointLightEffect DeferredPointLightEffect { get; } private DeferredPBR_DirectionalLightEffect DeferredDirectionalLightEffect { get; } private SimpleDepthEffect SimpleDepthEffect { get; } + private Effect ToneMapEffect { get; } private RenderTarget2D gPosition { get; } private RenderTarget2D gNormal { get; } @@ -133,8 +134,8 @@ namespace Kav DeferredAmbientLightEffect = new DeferredPBR_AmbientLightEffect(GraphicsDevice); DeferredPointLightEffect = new DeferredPBR_PointLightEffect(GraphicsDevice); DeferredDirectionalLightEffect = new DeferredPBR_DirectionalLightEffect(GraphicsDevice); - DeferredDirectionalLightEffect.ShadowMapSize = ShadowMapSize; + ToneMapEffect = new Effect(graphicsDevice, Resources.ToneMapEffect); FullscreenTriangle = new VertexBuffer(GraphicsDevice, typeof(VertexPositionTexture), 3, BufferUsage.WriteOnly); FullscreenTriangle.SetData(new VertexPositionTexture[3] { @@ -223,7 +224,7 @@ namespace Kav GraphicsDevice.SetRenderTarget(null); GraphicsDevice.Clear(Color.Black); - SpriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Opaque); + SpriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Opaque, null, null, null, ToneMapEffect); SpriteBatch.Draw(ColorRenderTarget, Vector2.Zero, Color.White); SpriteBatch.End(); } diff --git a/Resources.cs b/Resources.cs index 9ac9358..7791eaa 100644 --- a/Resources.cs +++ b/Resources.cs @@ -51,6 +51,18 @@ namespace Kav } } + public static byte[] ToneMapEffect + { + get + { + if (toneMapEffect == null) + { + toneMapEffect = GetResource("ToneMapEffect"); + } + return toneMapEffect; + } + } + public static byte[] DeferredPBREffect { get @@ -91,6 +103,7 @@ namespace Kav private static byte[] pointLightEffect; private static byte[] directionalLightEffect; private static byte[] gBufferEffect; + private static byte[] toneMapEffect; private static byte[] deferredPBREffect; private static byte[] pbrEffect; private static byte[] simpleDepthEffect; -- 2.25.1 From 016cb6e6a4e9ad32aec022717f5c2dac14cc101d Mon Sep 17 00:00:00 2001 From: cosmonaut Date: Mon, 21 Sep 2020 00:42:38 -0700 Subject: [PATCH 14/15] checking off some features --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index bf40a9f..8f3190b 100644 --- a/README.md +++ b/README.md @@ -12,9 +12,9 @@ Essential - [x] Directional lighting - [x] Directional shadow maps - [x] Cascading shadow maps -- [ ] Tone map shader +- [x] Tone map shader +- [x] Poisson soft shadowing - [ ] Frustum culling -- [ ] PCF soft shadowing - [ ] Shadow-casting point lights - [ ] Parabolic lights - [ ] Skyboxes -- 2.25.1 From 9527529adebf1850b942445d7392023c7b3a3918 Mon Sep 17 00:00:00 2001 From: cosmonaut Date: Mon, 21 Sep 2020 00:46:16 -0700 Subject: [PATCH 15/15] track fxb using lfs --- .gitattributes | 1 + Effects/FXB/DeferredPBREffect.fxb | Bin 55496 -> 130 bytes .../FXB/DeferredPBR_AmbientLightEffect.fxb | Bin 1016 -> 129 bytes .../DeferredPBR_DirectionalLightEffect.fxb | Bin 21692 -> 130 bytes Effects/FXB/DeferredPBR_GBufferEffect.fxb | Bin 7476 -> 129 bytes Effects/FXB/DeferredPBR_PointLightEffect.fxb | Bin 3108 -> 129 bytes Effects/FXB/GBufferEffect.fxb | Bin 7476 -> 129 bytes Effects/FXB/PBREffect.fxb | Bin 58384 -> 130 bytes Effects/FXB/SimpleDepthEffect.fxb | Bin 832 -> 128 bytes Effects/FXB/ToneMapEffect.fxb | Bin 584 -> 128 bytes 10 files changed, 1 insertion(+) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..40824ed --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +*.fxb filter=lfs diff=lfs merge=lfs -text diff --git a/Effects/FXB/DeferredPBREffect.fxb b/Effects/FXB/DeferredPBREffect.fxb index c5b5a89f711c3908e1db68c776981bd68f368ea5..1d4d26cc9ff85c859faa1157041406f9ff188d00 100644 GIT binary patch literal 130 zcmWN?OA^8$3;@u5Pr(H&-=v|p0TP56wH?7OJiWfnyW~Ck@ltKhbL?8*`@B8sSpT+XvqQYlF_Hl16o(e3nQCQVq`YeR-lYA8Ss=ikU+@^9Lu(m#Xylf N#bxxbD1Z=g`2p!GCoup3 literal 55496 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 Mbz=4}Bx!Qu2iTe?z5oCK literal 1016 zcmb7DJxc>Y5Z%jZa*7az6e$ExY!re-v=lTSg++vD6zzhVOFT$&;S#J<*c-I5u(-lP z@Gn@1zri11k;1~(PIA7t*$p9$I50c&-ptO<&g~g9;hY_R$em>z7O0FkYfrfMhq1J`iKb2f`1%@As8i`f%YHim;=lpF`B`O;&cwI z%~@Ri^FFHAE?$)En|Cmjk8Et>T+dy{ HC;#DJKYo&2 diff --git a/Effects/FXB/DeferredPBR_DirectionalLightEffect.fxb b/Effects/FXB/DeferredPBR_DirectionalLightEffect.fxb index d3bd6a251c5496c647cf9c207303fcc040e8329d..9f6cf273326d5892a4936589e0d0f379aaaa87ce 100644 GIT binary patch literal 130 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 diff --git a/Effects/FXB/DeferredPBR_PointLightEffect.fxb b/Effects/FXB/DeferredPBR_PointLightEffect.fxb index e3457052ceb8360da247256aaa21341ede7f6c7f..398f66bdafb1383d05b2fe948effb68ddd001756 100644 GIT binary patch literal 129 zcmWN_K@Ni;5CFituiyg}76irL0J~dA+G4usp|7u}N#>%jwEj_bj6-&ZwkHaMEf`5?-{5ymuV4CH>?U@17y>QL)i=hNm&W@}OI$6l{V@=3XW$7UCrfPAbnbMQT{V*RKw+q@rT z-L>UTnfH^5%_(exZ}8JxUGv)rZ$UP%i*+RLH(wby&I z?bd3qbT9L6pWuJ?fd6#X$-3q=m~i!_I&@~$^TT*(-8pnK&D+gxx4AMmHNRx*0(%aU zje06x{uiJyF2URk0`phU{fhh({KJa;FZ|h%6utpJTakP4UsvR9_`Qmp(vwD|FYmzL ztjJ%&->=AjdtMZ4>vL+ez3NJqTb{LK6Zq<+Qxi@0S08(Ay@J5^%;fF&Tp>OT1e z=ES1o_MTy4W-R97-SBoR7IS&O@L2T>M>d9{A3yfbtvh%7a(RvqJu5pqDK|ychL}h1 zbhvxR*dBZZaU_d^`9+1OlqbLq0V-oDk%HnZEk#%uGhH$IqMm~AYqF0VAZjp=M{ zqs`;nxN`Z8D`w|owmmTu`Ck(sPSS;|#`NEVCWWWqXa2nSEA+1GQ2#gKHE`Kv=kEPd z6nAFsJ^AtKD{Lp{r_as|#A81&^@%+2QH75yd;&kE?KBhkj_?`fI46-AlYBQaPUd!6 z87$}h!@k}}XNmKMOC5dDrv~p^SRKdofG*g{_OLFtIf{N<$w5Bl?N@VfZ_>ooV{@tw z<#ja{*8WxUrr0Ftv_5()d=Rrf>1tjV)>6YRg(OF;MZEGleMJ1K99vm=d9SdKuwBGw zm^(Q}HWfKVF0Ab8jOfkKQnj{(+MGV7?nO&lEC2Cdw3M16cbIY-Xh_^*%lRe9BTFHV z!G^}``mlz`6nC`-z85~X&V%+Q^eFW{A%TiyxN Z^1oZ9XaCDl>izF&>4k+M{_yYp{{XO@LELN=7gTtJimV7r$p8FWJ^Q=b_ZSuiK-}?SK1} z4aZZ>lTlr}7@g#7+4yjzgiwnG(ol^f*x54{48ay854$D{fKDeJV`0PWh-#+fbgji; M7PEg@Mi+zl0pms}XaE2J literal 7476 zcmds6O=w(I6u$4hX)BVR!9iS95J^!%L}{9Y+Q3v}lh%NRCMHuGnP!HWq(*S@ zgf3iUF{q0!x_Dqf>9{E9BDkpw5er?E;-VD65oh5-(3J?5jNfR%7Pd%%bDZ{?;7W4VR7`BD!0E--zIU?CrZ z)FC!LS;vwUj^a54ei+VEu1Crj*?)FC!L z-G1DFejUtywA9(AA5XKOr5$)a20oUXo6P5Drj8fp-#$B=E0>#Hisxq0Dc6NE8}(@# z{uAKLI`vEUI3KM37Jn>>O>{ncz-FFLPUh!xdLC&{6D&)tLaN~$%h*f_Gxv0TX^k?y zco)xuEyTXi{R^iIaUuU(;#|H_DCMW|ly>$}u(Va}Jc9akJHxZ*bER@_tTZ`WE*8p+ ze`$Ad8`|9`?Vg;;y*pAWypx-nn<>mH&NIh$%eI+@%$VYHCzxw;H<*||04A1vH!^y- zRGNHG;{jD=EC_t(Vo#ZHv3j~N)+KIP_sHn*?DWi3t_)dV9kF%6)KVAwFnV@!I#)uO z+=A}=Uf3LW7E!dxxU|9kNIbHSuMEteIfK+dKiWC22f?iCQLs=Sgj1s6xd@INeHn7y z(f6UU1JV%eS;z~Heg*P%NBr$Us+mg2VuqIyN&^DwGRn<}wG4Kb1L}9nEG;z-0yt=ZZ7fOEL%ho;j$h)z>lnK=nqZ z>F|(r;c*NP^AciaDn|A~4>Dp7Q$e|2A!FV!4Icbd2>-xKcWc?a$2+almvTBp;fS*5 z$iDH>c3<01XgNr;9pfLQ{SXaVdylTC(J*UR$DzjUKPt{rBW|Bf*WEN1>1n8|u0 zW+Eror>9P6oVf4Z;l%5;Hyq>iCnp=W6dr=p?~wCg!D#{Vhv19aX4c;4*0zkN$#Pxl z24qqV>{~p=?{4rkN3hj-wCy>!&DxIR!Bo^K>~6m8N9MVTVS&O!2H(~BwjJ&sa6hG| z(yX^PwVV!pb)v!?jGP@R)d1H+zC+%oTrp*gAF#pyZeUy&-w&Q_`i7M4dtIlmeXqgy zX2`PCmAZ!VH_>igmknTp91frlzB0MNL!5~}-=T9+fcRlQ=^vmh`mO6^Khdb}6YSc? zKKSY~+Nx{2$x+NFb>O`%|1o24my-f@IilU{KSoZ*cc2>DXT4m-dYG$texS2i z1JA{4;8U2Ax#|*3Lo|b#tAK|lt_#Uki`LfZUK7XVHsq=%PuTwnN%2T=l=jUGLEgJ{@SfW>@3&a)cF2p)*IVT$X>N5uIC|kLoa~S#`se6>e z81HWUx65hD$!Q|5m%Hf0HNo%T8qv^eVklk{zraKrm&<<2AjUS&&U8-ACwEkwycfMCk%ro~P=VJ=HL+;1ioACt`-VggW;to0W-t5~0 z2%n8|yywKc$jN=&!#>V9=bcE6fve+uFpl$|U`odMEtnYRjQ!*{qQrTGccd#|iSzHZ zMdCcM3AZ^%$~WR>_wDVB@vYA5|A%-7oY8-k<2MuklIP90waMq$hcz*FNj%!P)Bw@O zrEm!X&eHMyr}D>-tTbMW*G8{)96v%|y$3+=0T0>ox-8@ga1D&h9o-(!K6k*)`%r#s znNt(Xw5R6xuYKR&>ervp!-O6s^luV+twne5UZrmwZabP=*!*SM3T`F_v%_PoXQi=6 z9U0FKvR?3B`_yxvfz1aCrL6D=s29YXB}B|b=AEhvR&YPr+^c`)OW26H#!0EnZOF1$ zGiOae=Y0tCm9J9BSr5fF-jfl#lzJkznZF-Ys{uSy_{Y9)1-1Wsfwm@Y^UrsBjajuh zCqypO=TOW?i2D_LCRHK6N9C@IJ~jID+=_2bD|_$}*$*-H8t$P2+)UKCSA!#?`&!3$ zv(-~t$G2^S_{MjySL5AhD%O3i?}+u3*1K$ZM29Y)j&)Q2&R93~?~3)b_TLR1xcKZR p+O9B47%P$k6o|NO^!GTH!Cb|}GTbj)_8aK*bK^k#TE1)G_YchewfX=6 diff --git a/Effects/FXB/PBREffect.fxb b/Effects/FXB/PBREffect.fxb index 1747aa48900f927512bed8397d446fd22572faee..2214837cabe728ec27abfe852d904da5765e8cd1 100644 GIT binary patch literal 130 zcmWN?!4bkB5CFhGRnUL|4mcp)9N`dVR5F5jSiSCNFMiKHUb3xq&RwZ{U$;k{+yC~- z+cBPMo_wgwh|!CjjlkosnPR9VSOcaQ4IZGn9FwyZ$r^MSD*{sh%%lr@YfFlb$wx;i Np_u*4(nlMe_yNlnCgA`8 literal 58384 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 diff --git a/Effects/FXB/ToneMapEffect.fxb b/Effects/FXB/ToneMapEffect.fxb index dde4f5fa1bde7d2572e995635e6ad9576a56e110..5085f087d90c1d8093fa7feab673f4078c56037f 100644 GIT binary patch literal 128 zcmWN{yAi`63;@ucDOe!pBMXDKF(3kWN$^G3MWzqcYu-&?Zv7+c7$@&aJ==J^3NO=o z#U=H(;**lPjp!{)u7^dpQ^`5Hl!8ZtHUK$Hr~wEzSBf?TJOTQUzy>mL7HwA8a`Bdq M*#);BN%X7u1DKU2yZ`_I literal 584 zcmZ9JzfQw25XR3*fPfHL8ITAYERYIj{^>EmFrkLZ!YyX8Hy^ zPY0f&=DXMt5f`2O^WA;+og?=3_LYnsVDaE5c3&+4pf4BmPbK5wH2aW>NFc^>uCE)% z*$&~4ax9gSqi(ZT)^pB5a^y$E!3kCpA;jB5{5i1IV}Y&N(aMD#RyQ|T-n&%^WHMcD4doiKhM zj-(1(=`@?*dBR3?*`W0^(GFgzAj -- 2.25.1