ShadowMapShader.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429
  1. //-----------------------------------------------------------------------------
  2. // ShadowMapShader.cs
  3. //
  4. // Microsoft XNA Community Game Platform
  5. // Copyright (C) Microsoft Corporation. All rights reserved.
  6. //-----------------------------------------------------------------------------
  7. using Microsoft.Xna.Framework;
  8. using Microsoft.Xna.Framework.Graphics;
  9. using Microsoft.Xna.Framework.Input;
  10. using System;
  11. using System.IO;
  12. using RacingGame.GameLogic;
  13. using RacingGame.Graphics;
  14. using RacingGame.GameScreens;
  15. using Model = RacingGame.Graphics.Model;
  16. using Texture = RacingGame.Graphics.Texture;
  17. namespace RacingGame.Shaders
  18. {
  19. /// <summary>
  20. /// Shadow map shader
  21. /// </summary>
  22. public class ShadowMapShader : ShaderEffect
  23. {
  24. /// <summary>
  25. /// Shadow mapping shader filename
  26. /// </summary>
  27. const string ShaderFilename = "ShadowMap.fx";
  28. /// <summary>
  29. /// Shadow map texture we render to.
  30. /// </summary>
  31. internal RenderToTexture
  32. shadowMapTexture = null;
  33. /// <summary>
  34. /// Restrict near and far plane for much better depth resolution!
  35. /// </summary>
  36. internal float
  37. shadowNearPlane = 1.0f,
  38. shadowFarPlane = 1.0f * 28;
  39. /// <summary>
  40. /// Virtual point light parameters for directional shadow map lighting.
  41. /// Used to create a point light position for the directional light.
  42. /// </summary>
  43. internal float
  44. virtualLightDistance = 24,
  45. virtualVisibleRange = 23.5f;
  46. /// <summary>
  47. /// Shadow distance
  48. /// </summary>
  49. /// <returns>Float</returns>
  50. public float ShadowDistance
  51. {
  52. get
  53. {
  54. return virtualLightDistance;
  55. }
  56. }
  57. private Vector3 shadowLightPos = Vector3.Zero;
  58. /// <summary>
  59. /// Shadow light position
  60. /// </summary>
  61. /// <returns>Vector 3</returns>
  62. public Vector3 ShadowLightPos
  63. {
  64. get
  65. {
  66. return shadowLightPos;
  67. }
  68. }
  69. /// <summary>
  70. /// Texel width and height and offset for texScaleBiasMatrix,
  71. /// this way we can directly access the middle of each texel.
  72. /// </summary>
  73. float
  74. texelWidth = 1.0f / 1024.0f,
  75. texelHeight = 1.0f / 1024.0f,
  76. texOffsetX = 0.5f,
  77. texOffsetY = 0.5f;
  78. /// <summary>
  79. /// Compare depth bias
  80. /// </summary>
  81. internal float compareDepthBias = 0.00025f;
  82. /// <summary>
  83. /// Tex extra scale
  84. /// </summary>
  85. /// <returns>1.0f</returns>
  86. internal float texExtraScale = 1.0f;
  87. /// <summary>
  88. /// Shadow map depth bias value
  89. /// </summary>
  90. /// <returns>+</returns>
  91. internal float shadowMapDepthBiasValue = 0.00025f;
  92. /// <summary>
  93. /// The matrix to convert proj screen coordinates in the -1..1 range
  94. /// to the shadow depth map texture coordinates.
  95. /// </summary>
  96. Matrix texScaleBiasMatrix;
  97. /// <summary>
  98. /// Used matrices for the light casting the shadows.
  99. /// </summary>
  100. internal Matrix lightProjectionMatrix, lightViewMatrix;
  101. /// <summary>
  102. /// Additional effect handles
  103. /// </summary>
  104. private EffectParameter
  105. shadowTexTransform,
  106. worldViewProjLight,
  107. nearPlane,
  108. farPlane,
  109. depthBias,
  110. shadowMapDepthBias,
  111. shadowMap,
  112. shadowMapTexelSize,
  113. shadowDistanceFadeoutTexture;
  114. /// <summary>
  115. /// Shadow map blur post screen shader, used in RenderShadows
  116. /// to blur the shadow results.
  117. /// </summary>
  118. internal ShadowMapBlur shadowMapBlur = null;
  119. /// <summary>
  120. /// Calculate the texScaleBiasMatrix for converting proj screen
  121. /// coordinates in the -1..1 range to the shadow depth map
  122. /// texture coordinates.
  123. /// </summary>
  124. internal void CalcShadowMapBiasMatrix()
  125. {
  126. texelWidth = 1.0f / (float)shadowMapTexture.Width;
  127. texelHeight = 1.0f / (float)shadowMapTexture.Height;
  128. texOffsetX = 0.5f + (0.5f / (float)shadowMapTexture.Width);
  129. texOffsetY = 0.5f + (0.5f / (float)shadowMapTexture.Height);
  130. texScaleBiasMatrix = new Matrix(
  131. 0.5f * texExtraScale, 0.0f, 0.0f, 0.0f,
  132. 0.0f, -0.5f * texExtraScale, 0.0f, 0.0f,
  133. 0.0f, 0.0f, texExtraScale, 0.0f,
  134. texOffsetX, texOffsetY, 0.0f, 1.0f);
  135. }
  136. /// <summary>
  137. /// Shadow map shader
  138. /// </summary>
  139. public ShadowMapShader()
  140. : base(ShaderFilename)
  141. {
  142. // We use R32F, etc. and have a lot of precision
  143. compareDepthBias = 0.0001f;
  144. // Ok, time to create the shadow map render target
  145. shadowMapTexture = new RenderToTexture(
  146. RenderToTexture.SizeType.ShadowMap);
  147. CalcShadowMapBiasMatrix();
  148. shadowMapBlur = new ShadowMapBlur();
  149. }
  150. protected override void SetParameterDefaultValues()
  151. {
  152. // FilterTaps is hardcoded inside PS_UseShadowMap20 to avoid GLSL std140
  153. // float2 array padding (16 bytes/element on GPU vs 8 bytes on CPU),
  154. // which caused a BlockCopy overrun on DesktopGL.
  155. }
  156. /// <summary>
  157. /// Get parameters
  158. /// </summary>
  159. protected override void GetParameters()
  160. {
  161. // Can't get parameters if loading failed!
  162. if (effect == null)
  163. return;
  164. base.GetParameters();
  165. // Get additional parameters
  166. shadowTexTransform = effect.Parameters["shadowTexTransform"];
  167. worldViewProjLight = effect.Parameters["worldViewProjLight"];
  168. nearPlane = effect.Parameters["nearPlane"];
  169. farPlane = effect.Parameters["farPlane"];
  170. depthBias = effect.Parameters["depthBias"];
  171. shadowMapDepthBias = effect.Parameters["shadowMapDepthBias"];
  172. shadowMap = effect.Parameters["ShadowMap"];
  173. shadowMapTexelSize = effect.Parameters["shadowMapTexelSize"];
  174. shadowDistanceFadeoutTexture =
  175. effect.Parameters["shadowDistanceFadeoutTexture"];
  176. // Load shadowDistanceFadeoutTexture
  177. if (shadowDistanceFadeoutTexture != null)
  178. shadowDistanceFadeoutTexture.SetValue(
  179. new Texture("ShadowDistanceFadeoutMap").XnaTexture);
  180. }
  181. /// <summary>
  182. /// Update parameters
  183. /// </summary>
  184. public override void SetParameters(Material setMat)
  185. {
  186. // Can't set parameters if loading failed!
  187. if (effect == null)
  188. return;
  189. shadowNearPlane = 1.0f;
  190. shadowFarPlane = 6.25f * 28 * 1.25f;
  191. virtualLightDistance = 5.5f * 24 * 1.3f;
  192. virtualVisibleRange = 5.5f * 23.5f;
  193. compareDepthBias = 0.00065f;
  194. shadowMapDepthBiasValue = 0.00065f;
  195. base.SetParameters(setMat);
  196. // Set all extra parameters for this shader
  197. depthBias.SetValue(compareDepthBias);
  198. shadowMapDepthBias.SetValue(shadowMapDepthBiasValue);
  199. shadowMapTexelSize.SetValue(
  200. new Vector2(texelWidth, texelHeight));
  201. if (nearPlane != null)
  202. nearPlane.SetValue(shadowNearPlane);
  203. farPlane.SetValue(shadowFarPlane);
  204. }
  205. /// <summary>
  206. /// Calc simple directional shadow mapping matrix
  207. /// </summary>
  208. private void CalcSimpleDirectionalShadowMappingMatrix()
  209. {
  210. // Put light for directional mode away from origin (create virutal point
  211. // light). But adjust field of view to see enough of the visible area.
  212. float virtualFieldOfView = (float)Math.Atan2(
  213. virtualVisibleRange, virtualLightDistance);
  214. // Set projection matrix for light
  215. lightProjectionMatrix = Matrix.CreatePerspective(
  216. // Don't use graphics fov and aspect ratio in directional lighting mode
  217. virtualFieldOfView,
  218. 1.0f,
  219. shadowNearPlane,
  220. shadowFarPlane);
  221. // Calc light look pos, put it a little bit in front of our car
  222. Vector3 lightLookPos =
  223. RacingGameManager.InMenu ? RacingGameManager.Player.CarPosition :
  224. RacingGameManager.Player.CarPosition +
  225. RacingGameManager.Player.CarDirection * virtualVisibleRange / 6;
  226. // Well, this is how directional lights are done:
  227. lightViewMatrix = Matrix.CreateLookAt(
  228. // Use our current car position for our light look at origin!
  229. lightLookPos +
  230. BaseGame.LightDirection * virtualVisibleRange,//virtualLightDistance,
  231. lightLookPos,
  232. new Vector3(0, 0, 1));
  233. // Update light pos
  234. Matrix invView = Matrix.Invert(lightViewMatrix);
  235. shadowLightPos = new Vector3(invView.M41, invView.M42, invView.M43);
  236. }
  237. /// <summary>
  238. /// Update shadow world matrix.
  239. /// Calling this function is important to keep the shaders
  240. /// WorldMatrix and WorldViewProjMatrix up to date.
  241. /// </summary>
  242. /// <param name="setWorldMatrix">World matrix</param>
  243. internal void UpdateGenerateShadowWorldMatrix(Matrix setWorldMatrix)
  244. {
  245. Matrix world = setWorldMatrix;
  246. WorldMatrix = world;
  247. WorldViewProjMatrix =
  248. world * lightViewMatrix * lightProjectionMatrix;
  249. effect.CurrentTechnique.Passes[0].Apply();
  250. }
  251. /// <summary>
  252. /// Generate shadow
  253. /// </summary>
  254. internal void GenerateShadows(BaseGame.RenderHandler renderObjects)
  255. {
  256. // Can't generate shadow if loading failed!
  257. if (effect == null)
  258. return;
  259. // This method sets all required shader variables.
  260. this.SetParameters(null);
  261. Matrix remViewMatrix = BaseGame.ViewMatrix;
  262. Matrix remProjMatrix = BaseGame.ProjectionMatrix;
  263. CalcSimpleDirectionalShadowMappingMatrix();
  264. // Time to generate the shadow texture
  265. // Start rendering onto the shadow map
  266. shadowMapTexture.SetRenderTarget();
  267. // Make sure depth buffer is on
  268. BaseGame.Device.DepthStencilState = DepthStencilState.Default;
  269. // Disable alpha
  270. BaseGame.Device.BlendState = BlendState.Opaque;
  271. // Clear render target
  272. shadowMapTexture.Clear(Color.White);
  273. effect.CurrentTechnique = effect.Techniques["GenerateShadowMap20"];
  274. // Render shadows with help of the GenerateShadowMap shader
  275. RenderSinglePassShader(renderObjects);
  276. // Resolve the render target to get the texture (required for Xbox)
  277. shadowMapTexture.Resolve();
  278. // Set render target back to default
  279. BaseGame.ResetRenderTarget(false);
  280. BaseGame.ViewMatrix = remViewMatrix;
  281. BaseGame.ProjectionMatrix = remProjMatrix;
  282. }
  283. /// <summary>
  284. /// Update calc shadow world matrix, has to be done for each object
  285. /// we want to render in CalcShadows.
  286. /// </summary>
  287. /// <param name="setWorldMatrix">Set world matrix</param>
  288. internal void UpdateCalcShadowWorldMatrix(Matrix setWorldMatrix)
  289. {
  290. this.WorldMatrix = setWorldMatrix;
  291. this.WorldViewProjMatrix =
  292. setWorldMatrix * BaseGame.ViewMatrix * BaseGame.ProjectionMatrix;
  293. // Compute the matrix to transform from view space to light proj:
  294. // inverse of view matrix * light view matrix * light proj matrix
  295. Matrix lightTransformMatrix =
  296. setWorldMatrix *
  297. lightViewMatrix *
  298. lightProjectionMatrix *
  299. texScaleBiasMatrix;
  300. shadowTexTransform.SetValue(lightTransformMatrix);
  301. Matrix worldViewProjLightMatrix =
  302. setWorldMatrix *
  303. lightViewMatrix *
  304. lightProjectionMatrix;
  305. worldViewProjLight.SetValue(worldViewProjLightMatrix);
  306. effect.CurrentTechnique.Passes[0].Apply();
  307. }
  308. /// <summary>
  309. /// Calc shadows with help of generated light depth map,
  310. /// all objects have to be rendered again for comparing.
  311. /// We could save a pass when directly rendering here, but this
  312. /// has 2 disadvantages: 1. we can't post screen blur the shadow
  313. /// and 2. we can't use any other shader, especially bump and specular
  314. /// mapping shaders don't have any instructions left with ps_1_1.
  315. /// This way everything is kept simple, we can do as complex shaders
  316. /// as we want, the shadow shaders work seperately.
  317. /// </summary>
  318. /// <param name="renderObjects">Render objects</param>
  319. public void RenderShadows(BaseGame.RenderHandler renderObjects)
  320. {
  321. // Can't calc shadows if loading failed!
  322. if (effect == null)
  323. return;
  324. // Make sure z buffer and writing z buffer is on
  325. BaseGame.Device.DepthStencilState = DepthStencilState.Default;
  326. // Render shadows into our shadowMapBlur render target
  327. shadowMapBlur.RenderShadows(
  328. delegate
  329. {
  330. effect.CurrentTechnique = effect.Techniques["UseShadowMap20"];
  331. // This method sets all required shader variables.
  332. this.SetParameters(null);
  333. // Use the shadow map texture here which was generated in
  334. // GenerateShadows().
  335. shadowMap.SetValue(shadowMapTexture.XnaTexture);
  336. // Render shadows with help of the UseShadowMap shader
  337. RenderSinglePassShader(renderObjects);
  338. });
  339. // Start rendering the shadow map blur (pass 1, which messes up our
  340. // background), pass 2 can be done below without any render targets.
  341. shadowMapBlur.RenderShadows();
  342. // Kill background z buffer (else glass will not be rendered correctly)
  343. RacingGameManager.Device.Clear(ClearOptions.DepthBuffer, Color.Black, 1, 0);
  344. }
  345. /// <summary>
  346. /// Generates and renders shadows for all game objects
  347. /// </summary>
  348. public static void PrepareGameShadows()
  349. {
  350. if (BaseGame.AllowShadowMapping)
  351. {
  352. // Generate shadows
  353. ShaderEffect.shadowMapping.GenerateShadows(
  354. delegate
  355. {
  356. RacingGameManager.Landscape.GenerateShadow();
  357. RacingGameManager.CarModel.GenerateShadow(
  358. RacingGameManager.Player.CarRenderMatrix);
  359. });
  360. // Render shadows
  361. ShaderEffect.shadowMapping.RenderShadows(
  362. delegate
  363. {
  364. RacingGameManager.Landscape.UseShadow();
  365. RacingGameManager.CarModel.UseShadow(
  366. RacingGameManager.Player.CarRenderMatrix);
  367. });
  368. }
  369. }
  370. /// <summary>
  371. /// Show Shadows
  372. /// </summary>
  373. public void ShowShadows()
  374. {
  375. shadowMapBlur.ShowShadows();
  376. }
  377. }
  378. }