diff --git a/DirectionalShadowMapData.cs b/DirectionalShadowMapData.cs new file mode 100644 index 0000000..4eca964 --- /dev/null +++ b/DirectionalShadowMapData.cs @@ -0,0 +1,58 @@ +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; + +namespace Kav +{ + public class DirectionalShadowMapData + { + public static readonly int MAX_SHADOW_CASCADES = 4; + + public RenderTarget2D[] ShadowMaps { get; } + + public Matrix LightSpaceMatrixOne { get; set; } + public Matrix LightSpaceMatrixTwo { get; set; } + public Matrix LightSpaceMatrixThree { get; set; } + public Matrix LightSpaceMatrixFour { get; set; } + + public float[] CascadeFarPlanes { get; } + + public int ShadowMapSize { get; } + public int NumShadowCascades { get; } + + internal DirectionalShadowMapData( + GraphicsDevice graphicsDevice, + int shadowMapSize, + int numCascades + ) { + ShadowMapSize = shadowMapSize; + NumShadowCascades = (int)MathHelper.Clamp(numCascades, 1, MAX_SHADOW_CASCADES); + + ShadowMaps = new RenderTarget2D[NumShadowCascades]; + + for (var i = 0; i < NumShadowCascades; i++) + { + ShadowMaps[i] = new RenderTarget2D( + graphicsDevice, + shadowMapSize, + shadowMapSize, + false, + SurfaceFormat.Single, + DepthFormat.Depth24, + 0, + RenderTargetUsage.PreserveContents + ); + } + + CascadeFarPlanes = new float[MAX_SHADOW_CASCADES]; + } + + public void Clear(GraphicsDevice graphicsDevice) + { + foreach (var shadowMap in ShadowMaps) + { + graphicsDevice.SetRenderTarget(shadowMap); + graphicsDevice.Clear(Color.White); + } + } + } +} diff --git a/Renderer.cs b/Renderer.cs index b5e97b7..fb153dc 100644 --- a/Renderer.cs +++ b/Renderer.cs @@ -10,15 +10,10 @@ namespace Kav { private const int MAX_INSTANCE_VERTEX_COUNT = 1000000; private const int MAX_SHADOW_CASCADES = 4; - private int ShadowMapSize { get; } private GraphicsDevice GraphicsDevice { get; } private VertexBuffer FullscreenTriangle { get; } - private int NumShadowCascades { get; } - - private RenderTarget2D ColorRenderTarget { get; } - private RenderTarget2D[] ShadowRenderTargets { get; } private DeferredPBREffect DeferredPBREffect { get; } /* FIXME: these next two dont actually have anything to do with PBR */ @@ -33,8 +28,6 @@ namespace Kav private SkyboxEffect SkyboxEffect { get; } private DiffuseLitSpriteEffect DiffuseLitSpriteEffect { get; } - private RenderTargetCube PointShadowCubeMap { get; } - private Kav.Model UnitCube { get; } private SpriteBatch SpriteBatch { get; } @@ -43,50 +36,10 @@ namespace Kav private readonly GBufferInstanceVertex[] GBufferInstanceVertices = new GBufferInstanceVertex[MAX_INSTANCE_VERTEX_COUNT]; public Renderer( - GraphicsDevice graphicsDevice, - int renderDimensionsX, - int renderDimensionsY, - int numShadowCascades, - int shadowMapSize + GraphicsDevice graphicsDevice ) { GraphicsDevice = graphicsDevice; - ShadowMapSize = shadowMapSize; - - 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, - ShadowMapSize, - ShadowMapSize, - false, - SurfaceFormat.Single, - DepthFormat.Depth24 - ); - } - - ColorRenderTarget = new RenderTarget2D( - graphicsDevice, - renderDimensionsX, - renderDimensionsY, - false, - SurfaceFormat.Color, - DepthFormat.Depth24, - 0, - RenderTargetUsage.PreserveContents - ); - - PointShadowCubeMap = new RenderTargetCube( - GraphicsDevice, - shadowMapSize, - false, - SurfaceFormat.Single, - DepthFormat.Depth24 - ); - SimpleDepthEffect = new SimpleDepthEffect(GraphicsDevice); LinearDepthEffect = new LinearDepthEffect(GraphicsDevice); DeferredPBREffect = new DeferredPBREffect(GraphicsDevice); @@ -95,7 +48,6 @@ 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); Deferred_ToonEffect = new Deferred_ToonEffect(GraphicsDevice); SkyboxEffect = new SkyboxEffect(GraphicsDevice); @@ -123,6 +75,33 @@ namespace Kav ); } + public static RenderTargetCube CreateShadowCubeMap( + GraphicsDevice graphicsDevice, + int shadowMapSize + ) { + return new RenderTargetCube( + graphicsDevice, + shadowMapSize, + false, + SurfaceFormat.Single, + DepthFormat.Depth24, + 0, + RenderTargetUsage.PreserveContents + ); + } + + public static DirectionalShadowMapData CreateDirectionalShadowMaps( + GraphicsDevice graphicsDevice, + int shadowMapSize, + int numCascades + ) { + return new DirectionalShadowMapData( + graphicsDevice, + shadowMapSize, + numCascades + ); + } + // TODO: we could make this a lot more efficient probably // draws mesh sprites with a forward rendered diffuse lighting technique public void MeshSpriteRender( @@ -338,7 +317,7 @@ namespace Kav } } - public void RenderDepth( + public void RenderDepthIndexed( RenderTarget2D renderTarget, PerspectiveCamera camera, IEnumerable<(T, Matrix)> drawableTransforms @@ -488,18 +467,16 @@ namespace Kav RenderFullscreenEffect(DeferredAmbientLightEffect); } - public void RenderPointLight( + public void RenderPointLight( RenderTarget2D renderTarget, Texture2D gPosition, Texture2D gAlbedo, Texture2D gNormal, Texture2D gMetallicRoughness, + TextureCube shadowMap, PerspectiveCamera camera, - IEnumerable<(T, Matrix)> modelTransforms, PointLight pointLight - ) where T : ICullable, IIndexDrawable { - RenderPointShadows(PointShadowCubeMap, camera, modelTransforms, pointLight); - + ) { GraphicsDevice.SetRenderTarget(renderTarget); GraphicsDevice.DepthStencilState = DepthStencilState.DepthRead; GraphicsDevice.BlendState = BlendState.Additive; @@ -508,7 +485,7 @@ namespace Kav DeferredPointLightEffect.GAlbedo = gAlbedo; DeferredPointLightEffect.GNormal = gNormal; DeferredPointLightEffect.GMetallicRoughness = gMetallicRoughness; - DeferredPointLightEffect.ShadowMap = PointShadowCubeMap; + DeferredPointLightEffect.ShadowMap = shadowMap; DeferredPointLightEffect.EyePosition = camera.Position; @@ -530,10 +507,8 @@ namespace Kav PerspectiveCamera camera, IEnumerable<(T, Matrix)> modelTransforms, DirectionalLight directionalLight, - int numShadowCascades + DirectionalShadowMapData shadowMapData ) where T : ICullable, IIndexDrawable { - //RenderDirectionalShadows(camera, modelTransforms, directionalLight, DeferredDirectionalLightEffect); - GraphicsDevice.SetRenderTarget(renderTarget); GraphicsDevice.DepthStencilState = DepthStencilState.DepthRead; GraphicsDevice.BlendState = BlendState.Additive; @@ -543,18 +518,25 @@ namespace Kav DeferredDirectionalLightEffect.GNormal = gNormal; DeferredDirectionalLightEffect.GMetallicRoughness = gMetallicRoughness; - DeferredDirectionalLightEffect.ShadowMapOne = ShadowRenderTargets[0]; - if (numShadowCascades > 1) + DeferredDirectionalLightEffect.ShadowMapSize = shadowMapData.ShadowMapSize; + + DeferredDirectionalLightEffect.ShadowMapOne = shadowMapData.ShadowMaps[0]; + DeferredDirectionalLightEffect.LightSpaceMatrixOne = shadowMapData.LightSpaceMatrixOne; + + if (shadowMapData.NumShadowCascades > 1) { - DeferredDirectionalLightEffect.ShadowMapTwo = ShadowRenderTargets[1]; + DeferredDirectionalLightEffect.ShadowMapTwo = shadowMapData.ShadowMaps[1]; + DeferredDirectionalLightEffect.LightSpaceMatrixTwo = shadowMapData.LightSpaceMatrixTwo; } - if (numShadowCascades > 2) + if (shadowMapData.NumShadowCascades > 2) { - DeferredDirectionalLightEffect.ShadowMapThree = ShadowRenderTargets[2]; + DeferredDirectionalLightEffect.ShadowMapThree = shadowMapData.ShadowMaps[2]; + DeferredDirectionalLightEffect.LightSpaceMatrixThree = shadowMapData.LightSpaceMatrixThree; } - if (numShadowCascades > 3) + if (shadowMapData.NumShadowCascades > 3) { - DeferredDirectionalLightEffect.ShadowMapFour = ShadowRenderTargets[3]; + DeferredDirectionalLightEffect.ShadowMapFour = shadowMapData.ShadowMaps[3]; + DeferredDirectionalLightEffect.LightSpaceMatrixFour = shadowMapData.LightSpaceMatrixFour; } DeferredDirectionalLightEffect.DirectionalLightDirection = directionalLight.Direction; @@ -573,14 +555,12 @@ namespace Kav Texture2D gAlbedo, Texture2D gNormal, Texture2D gMetallicRoughness, + DirectionalShadowMapData shadowMapData, PerspectiveCamera camera, IEnumerable<(T, Matrix)> modelTransforms, DirectionalLight directionalLight, - int numShadowCascades, bool ditheredShadows ) where T : ICullable, IIndexDrawable { - //RenderDirectionalShadows(camera, modelTransforms, directionalLight, Deferred_ToonEffect); - GraphicsDevice.SetRenderTarget(renderTarget); GraphicsDevice.DepthStencilState = DepthStencilState.DepthRead; GraphicsDevice.BlendState = BlendState.Additive; @@ -598,18 +578,23 @@ namespace Kav Deferred_ToonEffect.DirectionalLightColor = directionalLight.Color.ToVector3() * directionalLight.Intensity; - Deferred_ToonEffect.ShadowMapOne = ShadowRenderTargets[0]; - if (numShadowCascades > 1) + Deferred_ToonEffect.ShadowMapOne = shadowMapData.ShadowMaps[0]; + Deferred_ToonEffect.LightSpaceMatrixOne = shadowMapData.LightSpaceMatrixOne; + + if (shadowMapData.NumShadowCascades > 1) { - Deferred_ToonEffect.ShadowMapTwo = ShadowRenderTargets[1]; + Deferred_ToonEffect.ShadowMapTwo = shadowMapData.ShadowMaps[1]; + Deferred_ToonEffect.LightSpaceMatrixTwo = shadowMapData.LightSpaceMatrixTwo; } - if (numShadowCascades > 2) + if (shadowMapData.NumShadowCascades > 2) { - Deferred_ToonEffect.ShadowMapThree = ShadowRenderTargets[2]; + Deferred_ToonEffect.ShadowMapThree = shadowMapData.ShadowMaps[2]; + Deferred_ToonEffect.LightSpaceMatrixThree = shadowMapData.LightSpaceMatrixThree; } - if (numShadowCascades > 3) + if (shadowMapData.NumShadowCascades > 3) { - Deferred_ToonEffect.ShadowMapFour = ShadowRenderTargets[3]; + Deferred_ToonEffect.ShadowMapFour = shadowMapData.ShadowMaps[3]; + Deferred_ToonEffect.LightSpaceMatrixFour = shadowMapData.LightSpaceMatrixFour; } Deferred_ToonEffect.ViewMatrix = camera.View; @@ -617,17 +602,17 @@ namespace Kav RenderFullscreenEffect(Deferred_ToonEffect); } - private void RenderDirectionalShadows( + public void RenderDirectionalShadowsIndexed( + DirectionalShadowMapData shadowMapData, PerspectiveCamera camera, - IEnumerable<(Model, Matrix)> modelTransforms, - DirectionalLight directionalLight, - ShadowCascadeEffect effect + IEnumerable<(T, Matrix)> drawableTransforms, + DirectionalLight directionalLight ) where T : ICullable, IIndexDrawable { // render the individual shadow cascades var previousFarPlane = camera.NearPlane; - for (var i = 0; i < NumShadowCascades; i++) + for (var i = 0; i < shadowMapData.NumShadowCascades; i++) { - var farPlane = camera.FarPlane / (MathHelper.Max((NumShadowCascades - i - 1) * 2f, 1f)); + var farPlane = camera.FarPlane / (MathHelper.Max((shadowMapData.NumShadowCascades - i - 1) * 2f, 1f)); // divide the view frustum var shadowCamera = new PerspectiveCamera( @@ -640,23 +625,27 @@ namespace Kav farPlane ); - // TODO: This is tightly coupled to the effect and it sucks - RenderDirectionalShadowMap(shadowCamera, modelTransforms, directionalLight, effect, i); + RenderDirectionalShadowMapIndexed( + shadowMapData, + i, + shadowCamera, + drawableTransforms, + directionalLight + ); - effect.CascadeFarPlanes[i] = farPlane; + shadowMapData.CascadeFarPlanes[i] = farPlane; previousFarPlane = farPlane; } } - private void RenderDirectionalShadowMap( + public void RenderDirectionalShadowMapIndexed( + DirectionalShadowMapData shadowMapData, + int shadowCascadeIndex, PerspectiveCamera camera, - IEnumerable<(Model, Matrix)> modelTransforms, - DirectionalLight directionalLight, - ShadowCascadeEffect effect, - int shadowCascadeIndex - ) { - GraphicsDevice.SetRenderTarget(ShadowRenderTargets[shadowCascadeIndex]); - GraphicsDevice.Clear(Color.White); + IEnumerable<(T, Matrix)> drawableTransforms, + DirectionalLight directionalLight + ) where T : ICullable, IIndexDrawable { + GraphicsDevice.SetRenderTarget(shadowMapData.ShadowMaps[shadowCascadeIndex]); GraphicsDevice.DepthStencilState = DepthStencilState.Default; GraphicsDevice.BlendState = BlendState.Opaque; @@ -693,53 +682,25 @@ namespace Kav if (shadowCascadeIndex == 0) { - effect.LightSpaceMatrixOne = lightSpaceMatrix; + shadowMapData.LightSpaceMatrixOne = lightSpaceMatrix; } else if (shadowCascadeIndex == 1) { - effect.LightSpaceMatrixTwo = lightSpaceMatrix; + shadowMapData.LightSpaceMatrixTwo = lightSpaceMatrix; } else if (shadowCascadeIndex == 2) { - effect.LightSpaceMatrixThree = lightSpaceMatrix; + shadowMapData.LightSpaceMatrixThree = lightSpaceMatrix; } else if (shadowCascadeIndex == 3) { - effect.LightSpaceMatrixFour = lightSpaceMatrix; + shadowMapData.LightSpaceMatrixFour = lightSpaceMatrix; } - var boundingFrustum = new BoundingFrustum(lightSpaceMatrix); - - foreach (var (model, transform) in FrustumCull(boundingFrustum, modelTransforms)) - { - foreach (var modelMesh in model.Meshes) - { - foreach (var meshPart in modelMesh.MeshParts) - { - GraphicsDevice.SetVertexBuffer(meshPart.VertexBuffer); - GraphicsDevice.Indices = meshPart.IndexBuffer; - - SimpleDepthEffect.World = transform; - - foreach (var pass in SimpleDepthEffect.CurrentTechnique.Passes) - { - pass.Apply(); - - GraphicsDevice.DrawIndexedPrimitives( - PrimitiveType.TriangleList, - 0, - 0, - meshPart.VertexBuffer.VertexCount, - 0, - meshPart.Triangles.Length - ); - } - } - } - } + CullAndRenderIndexed(GraphicsDevice, camera, drawableTransforms, SimpleDepthEffect); } - private void RenderPointShadows( + public void RenderPointShadowsIndexed( RenderTargetCube pointShadowCubeMap, PerspectiveCamera camera, IEnumerable<(T, Matrix)> modelTransforms,