Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- [CODE]
- using System;
- using Microsoft.Xna.Framework;
- using Microsoft.Xna.Framework.Content;
- using Microsoft.Xna.Framework.Graphics;
- using Microsoft.Xna.Framework.Input;
- namespace Craft
- {
- public class CascadedShadowMap
- {
- static SpriteBatch spriteBatch = Engine.SpriteBatch;
- static int width = Engine.BackBufferWidth;
- static int height = Engine.BackBufferHeight;
- const int SHADOW_RESOLUTION = 1024;
- const int SPLITS = 4;
- static Effect depthEffect = Engine.Game.Content.Load<Effect>("Effects\\Shadows\\Depth");
- static Effect depthLightEffect = Engine.Game.Content.Load<Effect>("Effects\\Shadows\\DepthLight");
- static Effect shadowMapEffect = Engine.Game.Content.Load<Effect>("Effects\\Shadows\\ShadowMap");
- static RenderTarget2D depthTexture = new RenderTarget2D(Engine.Graphics.GraphicsDevice, width, height, false, SurfaceFormat.Rg32, DepthFormat.Depth24);
- static RenderTarget2D shadowMap = new RenderTarget2D(Engine.Graphics.GraphicsDevice, SHADOW_RESOLUTION * SPLITS, SHADOW_RESOLUTION, false, SurfaceFormat.Rg32, DepthFormat.Depth24);
- static RenderTarget2D shadowOcclusion = new RenderTarget2D(Engine.Graphics.GraphicsDevice, width, height, false, SurfaceFormat.Color, DepthFormat.Depth24);
- static Viewport splitViewport = new Viewport();
- static Vector3[] frustumCornersVS = new Vector3[8];
- static Vector3[] frustumCornersWS = new Vector3[8];
- static Vector3[] frustumCornersLS = new Vector3[8];
- static Vector3[] farFrustumCornersVS = new Vector3[4];
- static Vector3[] splitFrustumCornersVS = new Vector3[8];
- static OrthographicCamera[] lightCameras = new OrthographicCamera[SPLITS];
- static Matrix[] lightViewProjectionMatrices = new Matrix[SPLITS];
- static Vector2[] lightClipPlanes = new Vector2[SPLITS];
- static float[] splitDepths = new float[SPLITS + 1];
- const float nearClipOffset = 175.0f;
- static bool showCascadeSplits = false;
- static bool toggleJitter = true;
- static Vector3 lightDirection = new Vector3(-1, -1, -1);
- #region Properties
- public static RenderTarget2D ShadowOcclusion
- {
- get { return shadowOcclusion; }
- set { shadowOcclusion = value; }
- }
- public static bool ShowCascadeSplits
- {
- get { return showCascadeSplits; }
- set { showCascadeSplits = value; }
- }
- public static Vector3 LightDirection
- {
- get { return lightDirection; }
- set { lightDirection = value; }
- }
- #endregion
- public static void Initialize()
- {
- shadowMapEffect.Parameters["ScreenResolutionSize"].SetValue(new Vector2(width, height));
- shadowMapEffect.Parameters["ShadowMapSize"].SetValue(new Vector2(shadowMap.Width, shadowMap.Height));
- depthEffect.Parameters["FarPlane"].SetValue(Camera.FarPlaneDistance);
- splitViewport.MinDepth = 0;
- splitViewport.MaxDepth = 1;
- splitViewport.Width = SHADOW_RESOLUTION;
- splitViewport.Height = SHADOW_RESOLUTION;
- splitViewport.Y = 0;
- }
- private static void Render()
- {
- // Set our targets
- Engine.Graphics.GraphicsDevice.SetRenderTarget(shadowMap);
- Engine.Graphics.GraphicsDevice.Clear(ClearOptions.Target, Color.White, 1.0f, 0);
- // Get corners of the main camera's bounding frustum
- Matrix viewMatrix = Camera.View;
- Camera.BoundingFrustum.GetCorners(frustumCornersWS);
- Vector3.Transform(frustumCornersWS, ref viewMatrix, frustumCornersVS);
- for (int i = 0; i < 4; i++)
- {
- farFrustumCornersVS[i] = frustumCornersVS[i + 4];
- }
- // Calculate the cascade splits. We calculate these so that each successive
- // split is larger than the previous, giving the closest split the most amount
- // of shadow detail.
- float N = SPLITS;
- float near = 1, far = Camera.FarPlaneDistance;
- splitDepths[0] = near;
- splitDepths[SPLITS] = far;
- const float splitConstant = 0.95f; //0.95f
- for (int i = 1; i < splitDepths.Length - 1; i++)
- {
- splitDepths[i] = splitConstant * near * (float)Math.Pow(far / near, i / N) + (1.0f - splitConstant) * ((near + (i / N)) * (far - near));
- }
- // Render our scene geometry to each split of the cascade
- for (int i = 0; i < SPLITS; i++)
- {
- float minZ = splitDepths[i];
- float maxZ = splitDepths[i + 1];
- lightCameras[i] = CalculateFrustum(minZ, maxZ);
- RenderShadowMap(i);
- }
- RenderShadowOcclusion();
- }
- protected static OrthographicCamera CalculateFrustum(float minZ, float maxZ)
- {
- // Shorten the view frustum according to the shadow view distance
- Matrix cameraMatrix = Camera.InverseView;
- for (int i = 0; i < 4; i++)
- splitFrustumCornersVS[i] = frustumCornersVS[i + 4] * (minZ / Camera.FarPlaneDistance);
- for (int i = 4; i < 8; i++)
- splitFrustumCornersVS[i] = frustumCornersVS[i] * (maxZ / Camera.FarPlaneDistance);
- Vector3.Transform(splitFrustumCornersVS, ref cameraMatrix, frustumCornersWS);
- // Position the shadow-caster camera so that it's looking at the centroid,
- // and backed up in the direction of the sunlight
- Matrix viewMatrix = Matrix.CreateLookAt(Vector3.Zero - (lightDirection), Vector3.Zero, new Vector3(0, 1, 0));
- // Determine the position of the frustum corners in light space
- Vector3.Transform(frustumCornersWS, ref viewMatrix, frustumCornersLS);
- // Calculate an orthographic projection by sizing a bounding box
- // to the frustum coordinates in light space
- Vector3 mins = frustumCornersLS[0];
- Vector3 maxes = frustumCornersLS[0];
- for (int i = 0; i < 8; i++)
- {
- if (frustumCornersLS[i].X > maxes.X)
- maxes.X = frustumCornersLS[i].X;
- else if (frustumCornersLS[i].X < mins.X)
- mins.X = frustumCornersLS[i].X;
- if (frustumCornersLS[i].Y > maxes.Y)
- maxes.Y = frustumCornersLS[i].Y;
- else if (frustumCornersLS[i].Y < mins.Y)
- mins.Y = frustumCornersLS[i].Y;
- if (frustumCornersLS[i].Z > maxes.Z)
- maxes.Z = frustumCornersLS[i].Z;
- else if (frustumCornersLS[i].Z < mins.Z)
- mins.Z = frustumCornersLS[i].Z;
- }
- if (toggleJitter)
- {
- // We snap the camera to 1 pixel increments so that moving the camera does not cause the shadows to jitter.
- // This is a matter of integer dividing by the world space size of a texel
- float diagonalLength = (frustumCornersWS[0] - frustumCornersWS[6]).Length();
- diagonalLength += 2; //Without this, the shadow map isn't big enough in the world.
- float worldsUnitsPerTexel = diagonalLength / (float)SHADOW_RESOLUTION;
- Vector3 borderOffset = (new Vector3(diagonalLength, diagonalLength, diagonalLength) - (maxes - mins)) * 0.5f;
- maxes += borderOffset;
- mins -= borderOffset;
- mins /= worldsUnitsPerTexel;
- mins.X = (float)Math.Floor(mins.X);
- mins.Y = (float)Math.Floor(mins.Y);
- mins.Z = (float)Math.Floor(mins.Z);
- mins *= worldsUnitsPerTexel;
- maxes /= worldsUnitsPerTexel;
- maxes.X = (float)Math.Floor(maxes.X);
- maxes.Y = (float)Math.Floor(maxes.Y);
- maxes.Z = (float)Math.Floor(maxes.Z);
- maxes *= worldsUnitsPerTexel;
- }
- OrthographicCamera lightCamera = new OrthographicCamera(mins.X, maxes.X, mins.Y, maxes.Y, -maxes.Z - nearClipOffset, -mins.Z);
- lightCamera.SetViewMatrix(ref viewMatrix);
- return lightCamera;
- }
- private static void RenderShadowMap(int splitIndex)
- {
- splitViewport.X = splitIndex * SHADOW_RESOLUTION;
- Engine.Graphics.GraphicsDevice.Viewport = splitViewport;
- depthLightEffect.CurrentTechnique = depthLightEffect.Techniques["Depth"];
- depthLightEffect.Parameters["ViewProjection"].SetValue(lightCameras[splitIndex].ViewProjectionMatrix);
- BlockRender.Draw(depthLightEffect, lightCameras[splitIndex].BoundingFrustum);
- }
- /// <summary>
- /// Renders a texture containing the final shadow occlusion
- /// </summary>
- private static void RenderShadowOcclusion()
- {
- Engine.Graphics.GraphicsDevice.SetRenderTarget(shadowOcclusion);
- Engine.Graphics.GraphicsDevice.Clear(ClearOptions.Target, Color.White, 1.0f, 0);
- // We'll use these clip planes to determine which split a pixel belongs to
- for (int i = 0; i < SPLITS; i++)
- {
- lightClipPlanes[i].X = -splitDepths[i];
- lightClipPlanes[i].Y = -splitDepths[i + 1];
- lightCameras[i].GetViewProjMatrix(out lightViewProjectionMatrices[i]);
- }
- shadowMapEffect.Parameters["ViewProjection"].SetValue(Camera.ViewProjection);
- shadowMapEffect.Parameters["InverseView"].SetValue(Camera.InverseView);
- shadowMapEffect.Parameters["LightViewProjection"].SetValue(lightViewProjectionMatrices);
- shadowMapEffect.Parameters["FrustumCornersVS"].SetValue(farFrustumCornersVS);
- shadowMapEffect.Parameters["ClipPlanes"].SetValue(lightClipPlanes);
- shadowMapEffect.Parameters["ShadowMap"].SetValue(shadowMap);
- shadowMapEffect.Parameters["DepthTexture"].SetValue(depthTexture);
- shadowMapEffect.Parameters["ShowSplitColors"].SetValue(showCascadeSplits);
- shadowMapEffect.CurrentTechnique.Passes[0].Apply();
- DeferredRenderer.FullScreenQuad.Draw();
- }
- private static void CreateDepthTexture()
- {
- Engine.Graphics.GraphicsDevice.SetRenderTarget(depthTexture);
- Engine.Graphics.GraphicsDevice.Clear(ClearOptions.Target, Color.White, 1.0f, 0);
- depthEffect.Parameters["ViewProjection"].SetValue(Camera.ViewProjection);
- BlockRender.Draw(depthEffect);
- Engine.Graphics.GraphicsDevice.SetRenderTarget(null);
- }
- public static void Draw()
- {
- CreateDepthTexture();
- Render();
- //Blur.BlurShadow(Blur.shadowBlurTarg, shadowOcclusion, 0);
- //Blur.BlurShadow(shadowOcclusion, Blur.shadowBlurTarg, 1);
- }
- /// <summary>
- /// Draw the render targets to screen
- /// </summary>
- public static void DrawRenderTargets()
- {
- spriteBatch.Begin(0, BlendState.Opaque, SamplerState.PointClamp, null, null);
- {
- spriteBatch.Draw(shadowMap, new Rectangle(0, 0, 256, 256), Color.White);
- spriteBatch.Draw(depthTexture, new Rectangle(0, 256, 256, 256), Color.White);
- spriteBatch.Draw(shadowOcclusion, new Rectangle(0, 512, 256, 256), Color.White);
- }
- spriteBatch.End();
- }
- public static void HandleInput(GameTime gameTime, InputState inputState)
- {
- if (inputState.IsTriggered(Action.ToggleCascadeSplits)) //F7
- {
- showCascadeSplits = !showCascadeSplits;
- }
- if (inputState.IsTriggered(Action.ToggleJitter)) //J
- {
- toggleJitter = !toggleJitter;
- }
- }
- }
- }
- [/CODE]
- [CODE]
- float4x4 World;
- float4x4 ViewProjection;
- float4x4 InverseView;
- static const int SPLITS = 4;
- float4x4 LightViewProjection [SPLITS];
- float2 ClipPlanes[SPLITS];
- float2 ShadowMapSize;
- float2 ScreenResolutionSize;
- float3 FrustumCornersVS [4];
- bool ShowSplitColors = false;
- static const float SHADOW_BIAS = 0.01f;
- static const float DARKEN_FACTOR = 100.0f;
- texture DepthTexture;
- sampler DepthTextureSampler = sampler_state
- {
- Texture = <DepthTexture>;
- MinFilter = Point;
- MagFilter = Point;
- MipFilter = None;
- };
- texture ShadowMap;
- sampler ShadowMapSampler = sampler_state
- {
- Texture = <ShadowMap>;
- MinFilter = Point;
- MagFilter = Point;
- MipFilter = None;
- };
- float2 sampleShadowMap(float2 UV)
- {
- if (UV.x < 0 || UV.x > 1 || UV.y < 0 || UV.y > 1)
- {
- return float2(1, 1);
- }
- return tex2D(ShadowMapSampler, UV).rg;
- }
- struct VertexShaderInput
- {
- float4 Position : POSITION;
- float3 TexCoordAndCornerIndex : TEXCOORD0;
- };
- struct VertexShaderOutput
- {
- float4 Position : POSITION;
- float2 TexCoord : TEXCOORD0;
- float3 FrustumCornerVS : TEXCOORD1;
- };
- // Vertex shader for rendering the full-screen quad used for calculating
- // the shadow occlusion factor.
- VertexShaderOutput VertexShaderFunction (VertexShaderInput input)
- {
- VertexShaderOutput output;
- // Offset the position by half a pixel to correctly align texels to pixels
- output.Position.x = input.Position.x - (1.0f / ScreenResolutionSize.x);
- output.Position.y = input.Position.y + (1.0f / ScreenResolutionSize.y);
- output.Position.z = input.Position.z;
- output.Position.w = 1.0f;
- // Pass along the texture coordiante and the position of the frustum corner
- output.TexCoord = input.TexCoordAndCornerIndex.xy;
- output.FrustumCornerVS = FrustumCornersVS[input.TexCoordAndCornerIndex.z];
- return output;
- }
- // Pixel shader for computing the shadow occlusion factor
- float4 PixelShaderFunction(VertexShaderOutput input) : COLOR0
- {
- // Reconstruct view-space position from the depth buffer
- float pixelDepth = tex2D(DepthTextureSampler, input.TexCoord).r;
- float4 position = float4(pixelDepth * input.FrustumCornerVS, 1.0f);
- // Figure out which split this pixel belongs to, based on view-space depth.
- float4x4 lightViewProjection = LightViewProjection[0];
- float offset = 0;
- float3 splitColors [4];
- splitColors[0] = float3(1, 0, 0);
- splitColors[1] = float3(0, 1, 0);
- splitColors[2] = float3(0, 0, 1);
- splitColors[3] = float3(1, 1, 0);
- float3 color = splitColors[0];
- int currentSplit = 0;
- for (int i = 1; i < SPLITS; i++)
- {
- if (position.z <= ClipPlanes[i].x && position.z > ClipPlanes[i].y)
- {
- lightViewProjection = LightViewProjection[i];
- offset = i / (float)SPLITS;
- color = splitColors[i];
- currentSplit = i;
- }
- }
- // Determine the depth of the pixel with respect to the light
- float4x4 inverseLVP = mul(InverseView, lightViewProjection);
- float4 positionLight = mul(position, inverseLVP);
- float lightDepth = (positionLight.z / positionLight.w) - SHADOW_BIAS;
- // Transform from light space to shadow map texture space.
- float2 shadowTexCoord = 0.5 * positionLight.xy / positionLight.w + float2(0.5f, 0.5f);
- shadowTexCoord.x = shadowTexCoord.x / SPLITS + offset;
- shadowTexCoord.y = 1.0f - shadowTexCoord.y;
- // Offset the coordinate by half a texel so we sample it correctly
- shadowTexCoord += (0.5f / ShadowMapSize);
- float shadow = 1;
- lightDepth += 0.01f;
- if (lightDepth < 1)
- {
- float2 shadowMap = sampleShadowMap(shadowTexCoord);
- //// Check if we're in shadow
- //float lit_factor = (lightDepth < shadowMap.x);
- //// Variance shadow mapping
- //float E_x2 = shadowMap.y;
- //float Ex_2 = shadowMap.x * shadowMap.x;
- //float variance = min(max(E_x2 - Ex_2, 0.0) + 1.0f / 2000, 1);
- //float m_d = (shadowMap.x - lightDepth);
- //float p = variance / (variance + m_d * m_d);
- //shadow = clamp(max(lit_factor, p), 0.1, 1.0f);
- //Calculate the Shadow Factor
- float shadowFactor = exp((DARKEN_FACTOR * 0.5f) * (shadowMap - lightDepth));
- shadowFactor = clamp(shadowFactor, 0.45, 1.0);
- shadow = shadowFactor;
- }
- if (!ShowSplitColors)
- return shadow;
- else
- return float4(shadow + color, 1);
- }
- technique Shadow
- {
- pass p0
- {
- VertexShader = compile vs_3_0 VertexShaderFunction();
- PixelShader = compile ps_3_0 PixelShaderFunction();
- }
- }
- [/CODE]
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement