DistortionComponent.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295
  1. #region File Description
  2. //-----------------------------------------------------------------------------
  3. // DistortionComponent.cs
  4. //
  5. // Microsoft XNA Community Game Platform
  6. // Copyright (C) Microsoft Corporation. All rights reserved.
  7. //-----------------------------------------------------------------------------
  8. #endregion
  9. #region Using Statements
  10. using System;
  11. using Microsoft.Xna.Framework;
  12. using Microsoft.Xna.Framework.Content;
  13. using Microsoft.Xna.Framework.Graphics;
  14. using System.Collections.Generic;
  15. #endregion
  16. namespace DistortionSample
  17. {
  18. public class DistortionComponent : DrawableGameComponent
  19. {
  20. #region Enumerations
  21. public enum DistortionTechnique
  22. {
  23. DisplacementMapped,
  24. HeatHaze,
  25. PullIn,
  26. ZeroDisplacement,
  27. }
  28. #endregion
  29. #region Constant Data
  30. private const float blurAmount = 2f;
  31. private readonly static string[] distortionTechniqueFriendlyNames = new string[]
  32. {
  33. "Displacement-Mapped",
  34. "Heat-Haze",
  35. "Pull-In",
  36. "Zero Displacement",
  37. };
  38. public static string GetDistortionTechniqueFriendlyName(
  39. DistortionTechnique technique)
  40. {
  41. return distortionTechniqueFriendlyNames[(int)technique];
  42. }
  43. #endregion
  44. #region Fields
  45. SpriteBatch spriteBatch;
  46. RenderTarget2D sceneMap;
  47. RenderTarget2D distortionMap;
  48. Effect distortEffect;
  49. EffectTechnique distortTechnique;
  50. EffectTechnique distortBlurTechnique;
  51. public Matrix View;
  52. public Matrix Projection;
  53. public Distorter Distorter;
  54. public bool ShowDistortionMap = false;
  55. #endregion
  56. #region Initialization
  57. public DistortionComponent(Game game)
  58. : base(game) { }
  59. /// <summary>
  60. /// Load your graphics content.
  61. /// </summary>
  62. protected override void LoadContent()
  63. {
  64. spriteBatch = new SpriteBatch(GraphicsDevice);
  65. distortEffect = Game.Content.Load<Effect>("Distort");
  66. distortTechnique = distortEffect.Techniques["Distort"];
  67. distortBlurTechnique = distortEffect.Techniques["DistortBlur"];
  68. // update the projection matrix
  69. Projection = Matrix.CreatePerspectiveFieldOfView(1f, GraphicsDevice.Viewport.AspectRatio, 1f, 10000f);
  70. // look up the resolution and format of our main backbuffer
  71. PresentationParameters pp = GraphicsDevice.PresentationParameters;
  72. int width = pp.BackBufferWidth;
  73. int height = pp.BackBufferHeight;
  74. SurfaceFormat format = pp.BackBufferFormat;
  75. DepthFormat depthFormat = pp.DepthStencilFormat;
  76. // create textures for reading back the backbuffer contents
  77. sceneMap = new RenderTarget2D(GraphicsDevice, width, height, false, format, depthFormat);
  78. distortionMap = new RenderTarget2D(GraphicsDevice, width, height, false, format, depthFormat);
  79. // set the blur parameters for the current viewport
  80. SetBlurEffectParameters(1f / (float)width, 1f / (float)height);
  81. }
  82. /// <summary>
  83. /// Unload your graphics content.
  84. /// </summary>
  85. protected override void UnloadContent()
  86. {
  87. spriteBatch = null;
  88. distortEffect = null;
  89. distortTechnique = null;
  90. distortBlurTechnique = null;
  91. if (sceneMap != null)
  92. {
  93. sceneMap.Dispose();
  94. sceneMap = null;
  95. }
  96. if (distortionMap != null)
  97. {
  98. distortionMap.Dispose();
  99. distortionMap = null;
  100. }
  101. }
  102. #endregion
  103. #region Draw
  104. /// <summary>
  105. /// This should be called at the very start of scene rendering. The distortion
  106. /// component uses it to redirect drawing into its custom rendertarget, so it
  107. /// can capture the scene image ready to apply the distortion postprocess.
  108. /// </summary>
  109. public void BeginDraw()
  110. {
  111. if (Visible)
  112. {
  113. GraphicsDevice.SetRenderTarget(sceneMap);
  114. }
  115. }
  116. /// <summary>
  117. /// Grab a scene that has already been rendered,
  118. /// and add a distortion effect over the top of it.
  119. /// </summary>
  120. public override void Draw(GameTime gameTime)
  121. {
  122. // now draw the distortion map
  123. GraphicsDevice.SetRenderTarget(ShowDistortionMap ? null : distortionMap);
  124. GraphicsDevice.Clear(Color.Transparent);
  125. // draw the distorter
  126. if (Distorter != null)
  127. {
  128. Matrix worldView = Distorter.World * View;
  129. Matrix[] transforms = new Matrix[Distorter.Model.Bones.Count];
  130. Distorter.Model.CopyAbsoluteBoneTransformsTo(transforms);
  131. // make sure the depth buffering is on, so only parts of the scene
  132. // behind the distortion effect are affected
  133. GraphicsDevice.DepthStencilState = DepthStencilState.Default;
  134. foreach (ModelMesh mesh in Distorter.Model.Meshes)
  135. {
  136. Matrix meshWorldView = transforms[mesh.ParentBone.Index] *
  137. worldView;
  138. foreach (Effect effect in mesh.Effects)
  139. {
  140. effect.CurrentTechnique =
  141. effect.Techniques[Distorter.Technique.ToString()];
  142. effect.Parameters["WorldView"].SetValue(meshWorldView);
  143. effect.Parameters["WorldViewProjection"].SetValue(
  144. meshWorldView * Projection);
  145. effect.Parameters["DistortionScale"].SetValue(
  146. Distorter.DistortionScale);
  147. effect.Parameters["Time"].SetValue(
  148. (float)gameTime.TotalGameTime.TotalSeconds);
  149. }
  150. mesh.Draw();
  151. }
  152. }
  153. // if we want to show the distortion map, then the backbuffer is done.
  154. // if we want to render the scene distorted, then we need to resolve the
  155. // backbuffer as the distortion map and use it to distort the scene
  156. if (!ShowDistortionMap)
  157. {
  158. GraphicsDevice.SetRenderTarget(null);
  159. // draw the scene image again, distorting it with the distortion map
  160. GraphicsDevice.Textures[1] = distortionMap;
  161. GraphicsDevice.SamplerStates[1] = SamplerState.PointClamp;
  162. Viewport viewport = GraphicsDevice.Viewport;
  163. distortEffect.CurrentTechnique = Distorter.DistortionBlur ?
  164. distortBlurTechnique : distortTechnique;
  165. DrawFullscreenQuad(sceneMap, viewport.Width, viewport.Height,
  166. distortEffect);
  167. }
  168. }
  169. /// <summary>
  170. /// Helper for drawing a texture into the current rendertarget,
  171. /// using a custom shader to apply postprocessing effects.
  172. /// </summary>
  173. void DrawFullscreenQuad(Texture2D texture, int width, int height, Effect effect)
  174. {
  175. spriteBatch.Begin(0, BlendState.Opaque, null, null, null, effect);
  176. spriteBatch.Draw(texture, new Rectangle(0, 0, width, height), Color.White);
  177. spriteBatch.End();
  178. }
  179. #endregion
  180. #region Blur Calculation
  181. /// <summary>
  182. /// Computes sample weightings and texture coordinate offsets
  183. /// for one pass of a separable gaussian blur filter.
  184. /// </summary>
  185. /// <remarks>
  186. /// This function was originally provided in the BloomComponent class in the
  187. /// Bloom Postprocess sample.
  188. /// </remarks>
  189. void SetBlurEffectParameters(float dx, float dy)
  190. {
  191. // Look up the sample weight and offset effect parameters.
  192. EffectParameter weightsParameter, offsetsParameter;
  193. weightsParameter = distortEffect.Parameters["SampleWeights"];
  194. offsetsParameter = distortEffect.Parameters["SampleOffsets"];
  195. // Look up how many samples our gaussian blur effect supports.
  196. int sampleCount = weightsParameter.Elements.Count;
  197. // Create temporary arrays for computing our filter settings.
  198. float[] sampleWeights = new float[sampleCount];
  199. Vector2[] sampleOffsets = new Vector2[sampleCount];
  200. // The first sample always has a zero offset.
  201. sampleWeights[0] = ComputeGaussian(0);
  202. sampleOffsets[0] = new Vector2(0);
  203. // Maintain a sum of all the weighting values.
  204. float totalWeights = sampleWeights[0];
  205. // Add pairs of additional sample taps, positioned
  206. // along a line in both directions from the center.
  207. for (int i = 0; i < sampleCount / 2; i++)
  208. {
  209. // Store weights for the positive and negative taps.
  210. float weight = ComputeGaussian(i + 1);
  211. sampleWeights[i * 2 + 1] = weight;
  212. sampleWeights[i * 2 + 2] = weight;
  213. totalWeights += weight * 2;
  214. // To get the maximum amount of blurring from a limited number of
  215. // pixel shader samples, we take advantage of the bilinear filtering
  216. // hardware inside the texture fetch unit. If we position our texture
  217. // coordinates exactly halfway between two texels, the filtering unit
  218. // will average them for us, giving two samples for the price of one.
  219. // This allows us to step in units of two texels per sample, rather
  220. // than just one at a time. The 1.5 offset kicks things off by
  221. // positioning us nicely in between two texels.
  222. float sampleOffset = i * 2 + 1.5f;
  223. Vector2 delta = new Vector2(dx, dy) * sampleOffset;
  224. // Store texture coordinate offsets for the positive and negative taps.
  225. sampleOffsets[i * 2 + 1] = delta;
  226. sampleOffsets[i * 2 + 2] = -delta;
  227. }
  228. // Normalize the list of sample weightings, so they will always sum to one.
  229. for (int i = 0; i < sampleWeights.Length; i++)
  230. {
  231. sampleWeights[i] /= totalWeights;
  232. }
  233. // Tell the effect about our new filter settings.
  234. weightsParameter.SetValue(sampleWeights);
  235. offsetsParameter.SetValue(sampleOffsets);
  236. }
  237. /// <summary>
  238. /// Evaluates a single point on the gaussian falloff curve.
  239. /// Used for setting up the blur filter weightings.
  240. /// </summary>
  241. /// <remarks>
  242. /// This function was originally provided in the BloomComponent class in the
  243. /// Bloom Postprocess sample.
  244. /// </remarks>
  245. static float ComputeGaussian(float n)
  246. {
  247. return (float)((1.0 / Math.Sqrt(2 * Math.PI * blurAmount)) *
  248. Math.Exp(-(n * n) / (2 * blurAmount * blurAmount)));
  249. }
  250. #endregion
  251. }
  252. }