Jump to content

  • Log In with Google      Sign In   
  • Create Account

We're offering banner ads on our site from just $5!

1. Details HERE. 2. GDNet+ Subscriptions HERE. 3. Ad upload HERE.


MJP's CSM. Shadows moving


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
1 reply to this topic

#1 mgoss   Members   -  Reputation: 225

Like
0Likes
Like

Posted 28 April 2012 - 09:45 AM

I'm using MJP's CSM shadow code and I'm having major issues with the shadows moving with the camera. Here a video to demonstrate what's going on. Also, is there a way to fix the dueling frustum issue? It's annoying seeing them pop in and out of corners of the screen.

http://www.youtube.com/watch?v=bkfdq4sk4ec&feature=youtu.be

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;
   }
  }
}
}

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();
	}
}



Sponsor:

#2 csisy   Members   -  Reputation: 256

Like
0Likes
Like

Posted 30 June 2012 - 01:25 AM

Hi!

If the problem is with the matrices, you should check this topic. It's working for me now.
sorry for my bad english :)




Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS