From a55e33f9a9097a59eb8dfafd36f6c75e95841916 Mon Sep 17 00:00:00 2001 From: cosmonaut Date: Fri, 18 Sep 2020 04:00:53 -0700 Subject: [PATCH] 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