| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429 |
- //-----------------------------------------------------------------------------
- // ShadowMapShader.cs
- //
- // Microsoft XNA Community Game Platform
- // Copyright (C) Microsoft Corporation. All rights reserved.
- //-----------------------------------------------------------------------------
- using Microsoft.Xna.Framework;
- using Microsoft.Xna.Framework.Graphics;
- using Microsoft.Xna.Framework.Input;
- using System;
- using System.IO;
- using RacingGame.GameLogic;
- using RacingGame.Graphics;
- using RacingGame.GameScreens;
- using Model = RacingGame.Graphics.Model;
- using Texture = RacingGame.Graphics.Texture;
- namespace RacingGame.Shaders
- {
- /// <summary>
- /// Shadow map shader
- /// </summary>
- public class ShadowMapShader : ShaderEffect
- {
- /// <summary>
- /// Shadow mapping shader filename
- /// </summary>
- const string ShaderFilename = "ShadowMap.fx";
- /// <summary>
- /// Shadow map texture we render to.
- /// </summary>
- internal RenderToTexture
- shadowMapTexture = null;
- /// <summary>
- /// Restrict near and far plane for much better depth resolution!
- /// </summary>
- internal float
- shadowNearPlane = 1.0f,
- shadowFarPlane = 1.0f * 28;
- /// <summary>
- /// Virtual point light parameters for directional shadow map lighting.
- /// Used to create a point light position for the directional light.
- /// </summary>
- internal float
- virtualLightDistance = 24,
- virtualVisibleRange = 23.5f;
- /// <summary>
- /// Shadow distance
- /// </summary>
- /// <returns>Float</returns>
- public float ShadowDistance
- {
- get
- {
- return virtualLightDistance;
- }
- }
- private Vector3 shadowLightPos = Vector3.Zero;
- /// <summary>
- /// Shadow light position
- /// </summary>
- /// <returns>Vector 3</returns>
- public Vector3 ShadowLightPos
- {
- get
- {
- return shadowLightPos;
- }
- }
- /// <summary>
- /// Texel width and height and offset for texScaleBiasMatrix,
- /// this way we can directly access the middle of each texel.
- /// </summary>
- float
- texelWidth = 1.0f / 1024.0f,
- texelHeight = 1.0f / 1024.0f,
- texOffsetX = 0.5f,
- texOffsetY = 0.5f;
- /// <summary>
- /// Compare depth bias
- /// </summary>
- internal float compareDepthBias = 0.00025f;
- /// <summary>
- /// Tex extra scale
- /// </summary>
- /// <returns>1.0f</returns>
- internal float texExtraScale = 1.0f;
- /// <summary>
- /// Shadow map depth bias value
- /// </summary>
- /// <returns>+</returns>
- internal float shadowMapDepthBiasValue = 0.00025f;
- /// <summary>
- /// The matrix to convert proj screen coordinates in the -1..1 range
- /// to the shadow depth map texture coordinates.
- /// </summary>
- Matrix texScaleBiasMatrix;
- /// <summary>
- /// Used matrices for the light casting the shadows.
- /// </summary>
- internal Matrix lightProjectionMatrix, lightViewMatrix;
- /// <summary>
- /// Additional effect handles
- /// </summary>
- private EffectParameter
- shadowTexTransform,
- worldViewProjLight,
- nearPlane,
- farPlane,
- depthBias,
- shadowMapDepthBias,
- shadowMap,
- shadowMapTexelSize,
- shadowDistanceFadeoutTexture;
- /// <summary>
- /// Shadow map blur post screen shader, used in RenderShadows
- /// to blur the shadow results.
- /// </summary>
- internal ShadowMapBlur shadowMapBlur = null;
- /// <summary>
- /// Calculate the texScaleBiasMatrix for converting proj screen
- /// coordinates in the -1..1 range to the shadow depth map
- /// texture coordinates.
- /// </summary>
- internal void CalcShadowMapBiasMatrix()
- {
- texelWidth = 1.0f / (float)shadowMapTexture.Width;
- texelHeight = 1.0f / (float)shadowMapTexture.Height;
- texOffsetX = 0.5f + (0.5f / (float)shadowMapTexture.Width);
- texOffsetY = 0.5f + (0.5f / (float)shadowMapTexture.Height);
- texScaleBiasMatrix = new Matrix(
- 0.5f * texExtraScale, 0.0f, 0.0f, 0.0f,
- 0.0f, -0.5f * texExtraScale, 0.0f, 0.0f,
- 0.0f, 0.0f, texExtraScale, 0.0f,
- texOffsetX, texOffsetY, 0.0f, 1.0f);
- }
- /// <summary>
- /// Shadow map shader
- /// </summary>
- public ShadowMapShader()
- : base(ShaderFilename)
- {
- // We use R32F, etc. and have a lot of precision
- compareDepthBias = 0.0001f;
- // Ok, time to create the shadow map render target
- shadowMapTexture = new RenderToTexture(
- RenderToTexture.SizeType.ShadowMap);
- CalcShadowMapBiasMatrix();
- shadowMapBlur = new ShadowMapBlur();
- }
- protected override void SetParameterDefaultValues()
- {
- // FilterTaps is hardcoded inside PS_UseShadowMap20 to avoid GLSL std140
- // float2 array padding (16 bytes/element on GPU vs 8 bytes on CPU),
- // which caused a BlockCopy overrun on DesktopGL.
- }
- /// <summary>
- /// Get parameters
- /// </summary>
- protected override void GetParameters()
- {
- // Can't get parameters if loading failed!
- if (effect == null)
- return;
- base.GetParameters();
- // Get additional parameters
- shadowTexTransform = effect.Parameters["shadowTexTransform"];
- worldViewProjLight = effect.Parameters["worldViewProjLight"];
- nearPlane = effect.Parameters["nearPlane"];
- farPlane = effect.Parameters["farPlane"];
- depthBias = effect.Parameters["depthBias"];
- shadowMapDepthBias = effect.Parameters["shadowMapDepthBias"];
- shadowMap = effect.Parameters["ShadowMap"];
- shadowMapTexelSize = effect.Parameters["shadowMapTexelSize"];
- shadowDistanceFadeoutTexture =
- effect.Parameters["shadowDistanceFadeoutTexture"];
- // Load shadowDistanceFadeoutTexture
- if (shadowDistanceFadeoutTexture != null)
- shadowDistanceFadeoutTexture.SetValue(
- new Texture("ShadowDistanceFadeoutMap").XnaTexture);
- }
- /// <summary>
- /// Update parameters
- /// </summary>
- public override void SetParameters(Material setMat)
- {
- // Can't set parameters if loading failed!
- if (effect == null)
- return;
- shadowNearPlane = 1.0f;
- shadowFarPlane = 6.25f * 28 * 1.25f;
- virtualLightDistance = 5.5f * 24 * 1.3f;
- virtualVisibleRange = 5.5f * 23.5f;
- compareDepthBias = 0.00065f;
- shadowMapDepthBiasValue = 0.00065f;
- base.SetParameters(setMat);
- // Set all extra parameters for this shader
- depthBias.SetValue(compareDepthBias);
- shadowMapDepthBias.SetValue(shadowMapDepthBiasValue);
- shadowMapTexelSize.SetValue(
- new Vector2(texelWidth, texelHeight));
- if (nearPlane != null)
- nearPlane.SetValue(shadowNearPlane);
- farPlane.SetValue(shadowFarPlane);
- }
- /// <summary>
- /// Calc simple directional shadow mapping matrix
- /// </summary>
- private void CalcSimpleDirectionalShadowMappingMatrix()
- {
- // Put light for directional mode away from origin (create virutal point
- // light). But adjust field of view to see enough of the visible area.
- float virtualFieldOfView = (float)Math.Atan2(
- virtualVisibleRange, virtualLightDistance);
- // Set projection matrix for light
- lightProjectionMatrix = Matrix.CreatePerspective(
- // Don't use graphics fov and aspect ratio in directional lighting mode
- virtualFieldOfView,
- 1.0f,
- shadowNearPlane,
- shadowFarPlane);
- // Calc light look pos, put it a little bit in front of our car
- Vector3 lightLookPos =
- RacingGameManager.InMenu ? RacingGameManager.Player.CarPosition :
- RacingGameManager.Player.CarPosition +
- RacingGameManager.Player.CarDirection * virtualVisibleRange / 6;
- // Well, this is how directional lights are done:
- lightViewMatrix = Matrix.CreateLookAt(
- // Use our current car position for our light look at origin!
- lightLookPos +
- BaseGame.LightDirection * virtualVisibleRange,//virtualLightDistance,
- lightLookPos,
- new Vector3(0, 0, 1));
- // Update light pos
- Matrix invView = Matrix.Invert(lightViewMatrix);
- shadowLightPos = new Vector3(invView.M41, invView.M42, invView.M43);
- }
- /// <summary>
- /// Update shadow world matrix.
- /// Calling this function is important to keep the shaders
- /// WorldMatrix and WorldViewProjMatrix up to date.
- /// </summary>
- /// <param name="setWorldMatrix">World matrix</param>
- internal void UpdateGenerateShadowWorldMatrix(Matrix setWorldMatrix)
- {
- Matrix world = setWorldMatrix;
- WorldMatrix = world;
- WorldViewProjMatrix =
- world * lightViewMatrix * lightProjectionMatrix;
- effect.CurrentTechnique.Passes[0].Apply();
- }
- /// <summary>
- /// Generate shadow
- /// </summary>
- internal void GenerateShadows(BaseGame.RenderHandler renderObjects)
- {
- // Can't generate shadow if loading failed!
- if (effect == null)
- return;
- // This method sets all required shader variables.
- this.SetParameters(null);
- Matrix remViewMatrix = BaseGame.ViewMatrix;
- Matrix remProjMatrix = BaseGame.ProjectionMatrix;
- CalcSimpleDirectionalShadowMappingMatrix();
- // Time to generate the shadow texture
- // Start rendering onto the shadow map
- shadowMapTexture.SetRenderTarget();
- // Make sure depth buffer is on
- BaseGame.Device.DepthStencilState = DepthStencilState.Default;
- // Disable alpha
- BaseGame.Device.BlendState = BlendState.Opaque;
- // Clear render target
- shadowMapTexture.Clear(Color.White);
- effect.CurrentTechnique = effect.Techniques["GenerateShadowMap20"];
- // Render shadows with help of the GenerateShadowMap shader
- RenderSinglePassShader(renderObjects);
- // Resolve the render target to get the texture (required for Xbox)
- shadowMapTexture.Resolve();
- // Set render target back to default
- BaseGame.ResetRenderTarget(false);
- BaseGame.ViewMatrix = remViewMatrix;
- BaseGame.ProjectionMatrix = remProjMatrix;
- }
- /// <summary>
- /// Update calc shadow world matrix, has to be done for each object
- /// we want to render in CalcShadows.
- /// </summary>
- /// <param name="setWorldMatrix">Set world matrix</param>
- internal void UpdateCalcShadowWorldMatrix(Matrix setWorldMatrix)
- {
- this.WorldMatrix = setWorldMatrix;
- this.WorldViewProjMatrix =
- setWorldMatrix * BaseGame.ViewMatrix * BaseGame.ProjectionMatrix;
- // Compute the matrix to transform from view space to light proj:
- // inverse of view matrix * light view matrix * light proj matrix
- Matrix lightTransformMatrix =
- setWorldMatrix *
- lightViewMatrix *
- lightProjectionMatrix *
- texScaleBiasMatrix;
- shadowTexTransform.SetValue(lightTransformMatrix);
- Matrix worldViewProjLightMatrix =
- setWorldMatrix *
- lightViewMatrix *
- lightProjectionMatrix;
- worldViewProjLight.SetValue(worldViewProjLightMatrix);
- effect.CurrentTechnique.Passes[0].Apply();
- }
- /// <summary>
- /// Calc shadows with help of generated light depth map,
- /// all objects have to be rendered again for comparing.
- /// We could save a pass when directly rendering here, but this
- /// has 2 disadvantages: 1. we can't post screen blur the shadow
- /// and 2. we can't use any other shader, especially bump and specular
- /// mapping shaders don't have any instructions left with ps_1_1.
- /// This way everything is kept simple, we can do as complex shaders
- /// as we want, the shadow shaders work seperately.
- /// </summary>
- /// <param name="renderObjects">Render objects</param>
- public void RenderShadows(BaseGame.RenderHandler renderObjects)
- {
- // Can't calc shadows if loading failed!
- if (effect == null)
- return;
- // Make sure z buffer and writing z buffer is on
- BaseGame.Device.DepthStencilState = DepthStencilState.Default;
- // Render shadows into our shadowMapBlur render target
- shadowMapBlur.RenderShadows(
- delegate
- {
- effect.CurrentTechnique = effect.Techniques["UseShadowMap20"];
- // This method sets all required shader variables.
- this.SetParameters(null);
- // Use the shadow map texture here which was generated in
- // GenerateShadows().
- shadowMap.SetValue(shadowMapTexture.XnaTexture);
- // Render shadows with help of the UseShadowMap shader
- RenderSinglePassShader(renderObjects);
- });
- // Start rendering the shadow map blur (pass 1, which messes up our
- // background), pass 2 can be done below without any render targets.
- shadowMapBlur.RenderShadows();
- // Kill background z buffer (else glass will not be rendered correctly)
- RacingGameManager.Device.Clear(ClearOptions.DepthBuffer, Color.Black, 1, 0);
- }
- /// <summary>
- /// Generates and renders shadows for all game objects
- /// </summary>
- public static void PrepareGameShadows()
- {
- if (BaseGame.AllowShadowMapping)
- {
- // Generate shadows
- ShaderEffect.shadowMapping.GenerateShadows(
- delegate
- {
- RacingGameManager.Landscape.GenerateShadow();
- RacingGameManager.CarModel.GenerateShadow(
- RacingGameManager.Player.CarRenderMatrix);
- });
- // Render shadows
- ShaderEffect.shadowMapping.RenderShadows(
- delegate
- {
- RacingGameManager.Landscape.UseShadow();
- RacingGameManager.CarModel.UseShadow(
- RacingGameManager.Player.CarRenderMatrix);
- });
- }
- }
- /// <summary>
- /// Show Shadows
- /// </summary>
- public void ShowShadows()
- {
- shadowMapBlur.ShowShadows();
- }
- }
- }
|