diff --git a/EffectInterfaces/ShadowCascadeEffect.cs b/EffectInterfaces/ShadowCascadeEffect.cs new file mode 100644 index 0000000..e55ba70 --- /dev/null +++ b/EffectInterfaces/ShadowCascadeEffect.cs @@ -0,0 +1,14 @@ +using Microsoft.Xna.Framework; + +namespace Kav +{ + public interface ShadowCascadeEffect + { + Matrix LightSpaceMatrixOne { get; set; } + Matrix LightSpaceMatrixTwo { get; set; } + Matrix LightSpaceMatrixThree { get; set; } + Matrix LightSpaceMatrixFour { get; set; } + + float[] CascadeFarPlanes { get; } + } +} diff --git a/Effects/DeferredPBR_DirectionalLightEffect.cs b/Effects/DeferredPBR_DirectionalLightEffect.cs index 5e1f919..a30c1fe 100644 --- a/Effects/DeferredPBR_DirectionalLightEffect.cs +++ b/Effects/DeferredPBR_DirectionalLightEffect.cs @@ -3,7 +3,7 @@ using Microsoft.Xna.Framework.Graphics; namespace Kav { - public class DeferredPBR_DirectionalLightEffect : Effect + public class DeferredPBR_DirectionalLightEffect : Effect, ShadowCascadeEffect { EffectParameter gPositionParam; EffectParameter gAlbedoParam; @@ -46,7 +46,7 @@ namespace Kav public Vector3 DirectionalLightDirection { get; set; } public Vector3 DirectionalLightColor { get; set; } - public readonly float[] CascadeFarPlanes; + public float[] CascadeFarPlanes { get; } public int ShadowMapSize { get; set; } @@ -147,7 +147,6 @@ namespace Kav directionalLightColorParam = Parameters["DirectionalLightColor"]; cascadeFarPlanesParam = Parameters["CascadeFarPlanes"]; - shadowMapSizeParam = Parameters["ShadowMapSize"]; lightSpaceMatrixOneParam = Parameters["LightSpaceMatrixOne"]; diff --git a/Effects/Deferred_ToonEffect.cs b/Effects/Deferred_ToonEffect.cs index 9196197..a55b678 100644 --- a/Effects/Deferred_ToonEffect.cs +++ b/Effects/Deferred_ToonEffect.cs @@ -3,30 +3,61 @@ using Microsoft.Xna.Framework.Graphics; namespace Kav { - public class Deferred_ToonEffect : Effect + public class Deferred_ToonEffect : Effect, ShadowCascadeEffect { EffectParameter gPositionParam; EffectParameter gAlbedoParam; EffectParameter gNormalParam; + EffectParameter shadowMapOneParam; + EffectParameter shadowMapTwoParam; + EffectParameter shadowMapThreeParam; + EffectParameter shadowMapFourParam; + EffectParameter eyePositionParam; EffectParameter directionalLightDirectionParam; EffectParameter directionalLightColorParam; EffectParameter softnessParam; + EffectParameter cascadeFarPlanesParam; + EffectParameter shadowMapSizeParam; + + 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 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 float Softness { get; set; } + public float[] CascadeFarPlanes { get; } + public float ShadowMapSize { 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 Deferred_ToonEffect(GraphicsDevice graphicsDevice) : base(graphicsDevice, Resources.Deferred_ToonEffect) { + CascadeFarPlanes = new float[4]; CacheEffectParameters(); } @@ -36,11 +67,26 @@ namespace Kav gAlbedoParam.SetValue(GAlbedo); gNormalParam.SetValue(GNormal); + shadowMapOneParam.SetValue(ShadowMapOne); + shadowMapTwoParam.SetValue(ShadowMapTwo); + shadowMapThreeParam.SetValue(ShadowMapThree); + shadowMapFourParam.SetValue(ShadowMapFour); + eyePositionParam.SetValue(EyePosition); directionalLightDirectionParam.SetValue(DirectionalLightDirection); directionalLightColorParam.SetValue(DirectionalLightColor); softnessParam.SetValue(Softness); + + cascadeFarPlanesParam.SetValue(CascadeFarPlanes); + shadowMapSizeParam.SetValue(ShadowMapSize); + + lightSpaceMatrixOneParam.SetValue(LightSpaceMatrixOne); + lightSpaceMatrixTwoParam.SetValue(LightSpaceMatrixTwo); + lightSpaceMatrixThreeParam.SetValue(LightSpaceMatrixThree); + lightSpaceMatrixFourParam.SetValue(LightSpaceMatrixFour); + + viewMatrixParam.SetValue(ViewMatrix); } void CacheEffectParameters() @@ -49,11 +95,26 @@ namespace Kav gAlbedoParam = Parameters["gAlbedo"]; gNormalParam = Parameters["gNormal"]; + shadowMapOneParam = Parameters["shadowMapOne"]; + shadowMapTwoParam = Parameters["shadowMapTwo"]; + shadowMapThreeParam = Parameters["shadowMapThree"]; + shadowMapFourParam = Parameters["shadowMapFour"]; + eyePositionParam = Parameters["EyePosition"]; directionalLightDirectionParam = Parameters["DirectionalLightDirection"]; directionalLightColorParam = Parameters["DirectionalLightColor"]; softnessParam = Parameters["Softness"]; + + cascadeFarPlanesParam = Parameters["CascadeFarPlanes"]; + shadowMapSizeParam = Parameters["ShadowMapSize"]; + + lightSpaceMatrixOneParam = Parameters["LightSpaceMatrixOne"]; + lightSpaceMatrixTwoParam = Parameters["LightSpaceMatrixTwo"]; + lightSpaceMatrixThreeParam = Parameters["LightSpaceMatrixThree"]; + lightSpaceMatrixFourParam = Parameters["LightSpaceMatrixFour"]; + + viewMatrixParam = Parameters["ViewMatrix"]; } } } diff --git a/Effects/FXB/Deferred_ToonEffect.fxb b/Effects/FXB/Deferred_ToonEffect.fxb index 4b5284e..f40c541 100644 --- a/Effects/FXB/Deferred_ToonEffect.fxb +++ b/Effects/FXB/Deferred_ToonEffect.fxb @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1e5bd52fb74a48a7165a50bce38428a5f6d84550910129a6a17c66e87b2d8b0d -size 2504 +oid sha256:6f1008f0dd9ab943b47e548d9f61d122e44785f7fbb9ea47b7d73448af681016 +size 21180 diff --git a/Effects/HLSL/Deferred_ToonEffect.fx b/Effects/HLSL/Deferred_ToonEffect.fx index 42251a4..9da8035 100644 --- a/Effects/HLSL/Deferred_ToonEffect.fx +++ b/Effects/HLSL/Deferred_ToonEffect.fx @@ -1,17 +1,37 @@ #include "Macros.fxh" +#include "Shadow.fxh" + +static const int NUM_SHADOW_CASCADES = 4; DECLARE_TEXTURE(gPosition, 0); DECLARE_TEXTURE(gAlbedo, 1); DECLARE_TEXTURE(gNormal, 2); +DECLARE_TEXTURE(shadowMapOne, 4); +DECLARE_TEXTURE(shadowMapTwo, 5); +DECLARE_TEXTURE(shadowMapThree, 6); +DECLARE_TEXTURE(shadowMapFour, 7); BEGIN_CONSTANTS -float3 EyePosition _ps(c0) _cb(c0); +float3 EyePosition _ps(c0) _cb(c0); -float3 DirectionalLightDirection _ps(c1) _cb(c1); -float3 DirectionalLightColor _ps(c2) _cb(c2); +float3 DirectionalLightDirection _ps(c1) _cb(c1); +float3 DirectionalLightColor _ps(c2) _cb(c2); -float Softness _ps(c3) _cb(c3); +float Softness _ps(c3) _cb(c3); + +float CascadeFarPlanes[NUM_SHADOW_CASCADES] _ps(c4) _cb(c4); + +float ShadowMapSize _ps(c8) _cb(c8); + +MATRIX_CONSTANTS + +float4x4 LightSpaceMatrixOne _ps(c9) _cb(c9); +float4x4 LightSpaceMatrixTwo _ps(c13) _cb(c13); +float4x4 LightSpaceMatrixThree _ps(c17) _cb(c17); +float4x4 LightSpaceMatrixFour _ps(c21) _cb(c21); + +float4x4 ViewMatrix _ps(c25) _cb(c25); END_CONSTANTS @@ -37,6 +57,88 @@ PixelInput main_vs(VertexInput input) return output; } +float ComputeShadow(float3 positionWorldSpace, float3 N, float3 L) +{ + float4 positionCameraSpace = mul(float4(positionWorldSpace, 1.0), ViewMatrix); + + int shadowCascadeIndex = 0; // 0 is closest + for (int i = 0; i < NUM_SHADOW_CASCADES; i++) + { + if (abs(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; + } + + // PCF + Poisson soft shadows + + if (shadowCascadeIndex == 0) + { + return PoissonShadow( + positionWorldSpace, + N, + L, + lightSpaceMatrix, + SAMPLER(shadowMapOne), + ShadowMapSize + ); + } + else if (shadowCascadeIndex == 1) + { + return PoissonShadow( + positionWorldSpace, + N, + L, + lightSpaceMatrix, + SAMPLER(shadowMapTwo), + ShadowMapSize + ); + } + else if (shadowCascadeIndex == 2) + { + return PoissonShadow( + positionWorldSpace, + N, + L, + lightSpaceMatrix, + SAMPLER(shadowMapThree), + ShadowMapSize + ); + } + else + { + return PoissonShadow( + positionWorldSpace, + N, + L, + lightSpaceMatrix, + SAMPLER(shadowMapFour), + ShadowMapSize + ); + } +} + +// FIXME: organize this float4 main_ps(PixelInput input) : SV_TARGET0 { float3 worldPosition = SAMPLE_TEXTURE(gPosition, input.TexCoord).rgb; @@ -76,7 +178,8 @@ float4 main_ps(PixelInput input) : SV_TARGET0 rimIntensity = smoothstep(rimAmount - 0.01, rimAmount + 0.01, rimIntensity); float3 rim = rimIntensity * rimColor; - float3 color = albedo * (light + specular + rim); + float shadow = ComputeShadow(worldPosition, N, L); + float3 color = albedo * (light + specular + rim) * shadow; return float4(color, 1.0); } diff --git a/Renderer.cs b/Renderer.cs index 4861f2d..a67e3b8 100644 --- a/Renderer.cs +++ b/Renderer.cs @@ -195,11 +195,7 @@ namespace Kav GraphicsDevice.Clear(Color.Black); AmbientLightRender(ambientLight); - - Deferred_ToonEffect.GPosition = gPosition; - Deferred_ToonEffect.GAlbedo = gAlbedo; - Deferred_ToonEffect.GNormal = gNormal; - DirectionalLightToonRender(camera, directionalLight); + DirectionalLightToonRender(camera, modelTransforms, directionalLight); GraphicsDevice.SetRenderTarget(null); GraphicsDevice.Clear(Color.Black); @@ -298,28 +294,7 @@ namespace Kav IEnumerable<(Model, Matrix)> modelTransforms, DirectionalLight directionalLight ) { - // render the individual shadow cascades - var previousFarPlane = camera.NearPlane; - for (var i = 0; i < NumShadowCascades; i++) - { - var farPlane = camera.FarPlane / (MathHelper.Max((NumShadowCascades - i - 1) * 2f, 1f)); - - // divide the view frustum - var shadowCamera = new PerspectiveCamera( - camera.Position, - camera.Forward, - camera.Up, - camera.FieldOfView, - camera.AspectRatio, - previousFarPlane, - farPlane - ); - - // TODO: This is tightly coupled to the effect and it sucks - RenderShadowMap(shadowCamera, modelTransforms, directionalLight, i); - - previousFarPlane = farPlane; - } + RenderShadows(camera, modelTransforms, directionalLight, DeferredDirectionalLightEffect); DeferredDirectionalLightEffect.GPosition = gPosition; DeferredDirectionalLightEffect.GAlbedo = gAlbedo; @@ -345,7 +320,7 @@ namespace Kav directionalLight.Color.ToVector3() * directionalLight.Intensity; DeferredDirectionalLightEffect.ViewMatrix = camera.View; - DeferredDirectionalLightEffect.EyePosition = Matrix.Invert(camera.View).Translation; + DeferredDirectionalLightEffect.EyePosition = camera.Position; GraphicsDevice.SetRenderTarget(ColorRenderTarget); GraphicsDevice.BlendState = BlendState.Additive; @@ -360,11 +335,18 @@ namespace Kav private void DirectionalLightToonRender( PerspectiveCamera camera, + IEnumerable<(Model, Matrix)> modelTransforms, DirectionalLight directionalLight ) { + RenderShadows(camera, modelTransforms, directionalLight, Deferred_ToonEffect); + GraphicsDevice.SetRenderTarget(ColorRenderTarget); GraphicsDevice.BlendState = BlendState.Additive; + Deferred_ToonEffect.GPosition = gPosition; + Deferred_ToonEffect.GAlbedo = gAlbedo; + Deferred_ToonEffect.GNormal = gNormal; + Deferred_ToonEffect.EyePosition = camera.Position; Deferred_ToonEffect.DirectionalLightDirection = directionalLight.Direction; Deferred_ToonEffect.DirectionalLightColor = @@ -372,6 +354,22 @@ namespace Kav Deferred_ToonEffect.Softness = 0.01f; + Deferred_ToonEffect.ShadowMapOne = ShadowRenderTargets[0]; + if (NumShadowCascades > 1) + { + Deferred_ToonEffect.ShadowMapTwo = ShadowRenderTargets[1]; + } + if (NumShadowCascades > 2) + { + Deferred_ToonEffect.ShadowMapThree = ShadowRenderTargets[2]; + } + if (NumShadowCascades > 3) + { + Deferred_ToonEffect.ShadowMapFour = ShadowRenderTargets[3]; + } + + Deferred_ToonEffect.ViewMatrix = camera.View; + foreach (EffectPass pass in Deferred_ToonEffect.CurrentTechnique.Passes) { pass.Apply(); @@ -380,10 +378,42 @@ namespace Kav } } + private void RenderShadows( + PerspectiveCamera camera, + IEnumerable<(Model, Matrix)> modelTransforms, + DirectionalLight directionalLight, + ShadowCascadeEffect effect + ) { + // render the individual shadow cascades + var previousFarPlane = camera.NearPlane; + for (var i = 0; i < NumShadowCascades; i++) + { + var farPlane = camera.FarPlane / (MathHelper.Max((NumShadowCascades - i - 1) * 2f, 1f)); + + // divide the view frustum + var shadowCamera = new PerspectiveCamera( + camera.Position, + camera.Forward, + camera.Up, + camera.FieldOfView, + camera.AspectRatio, + previousFarPlane, + farPlane + ); + + // TODO: This is tightly coupled to the effect and it sucks + RenderShadowMap(shadowCamera, modelTransforms, directionalLight, effect, i); + + effect.CascadeFarPlanes[i] = farPlane; + previousFarPlane = farPlane; + } + } + private void RenderShadowMap( PerspectiveCamera camera, IEnumerable<(Model, Matrix)> modelTransforms, DirectionalLight directionalLight, + ShadowCascadeEffect effect, int shadowCascadeIndex ) { GraphicsDevice.SetRenderTarget(ShadowRenderTargets[shadowCascadeIndex]); @@ -424,23 +454,21 @@ namespace Kav if (shadowCascadeIndex == 0) { - DeferredDirectionalLightEffect.LightSpaceMatrixOne = lightSpaceMatrix; + effect.LightSpaceMatrixOne = lightSpaceMatrix; } else if (shadowCascadeIndex == 1) { - DeferredDirectionalLightEffect.LightSpaceMatrixTwo = lightSpaceMatrix; + effect.LightSpaceMatrixTwo = lightSpaceMatrix; } else if (shadowCascadeIndex == 2) { - DeferredDirectionalLightEffect.LightSpaceMatrixThree = lightSpaceMatrix; + effect.LightSpaceMatrixThree = lightSpaceMatrix; } else if (shadowCascadeIndex == 3) { - DeferredDirectionalLightEffect.LightSpaceMatrixFour = lightSpaceMatrix; + effect.LightSpaceMatrixFour = lightSpaceMatrix; } - DeferredDirectionalLightEffect.CascadeFarPlanes[shadowCascadeIndex] = camera.FarPlane; - foreach (var (model, transform) in modelTransforms) { foreach (var modelMesh in model.Meshes)