SpriteEffects.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450
  1. //-----------------------------------------------------------------------------
  2. // SpriteEffects.cs
  3. //
  4. // Microsoft XNA Community Game Platform
  5. // Copyright (C) Microsoft Corporation. All rights reserved.
  6. //-----------------------------------------------------------------------------
  7. using System;
  8. using Microsoft.Xna.Framework;
  9. using Microsoft.Xna.Framework.Content;
  10. using Microsoft.Xna.Framework.Graphics;
  11. using Microsoft.Xna.Framework.Input;
  12. namespace SpriteEffects
  13. {
  14. /// <summary>
  15. /// Sample demonstrating how shaders can be used to
  16. /// apply special effects to sprite rendering.
  17. /// </summary>
  18. public class SpriteEffectsGame : Microsoft.Xna.Framework.Game
  19. {
  20. GraphicsDeviceManager graphics;
  21. KeyboardState lastKeyboardState = new KeyboardState ();
  22. GamePadState lastGamePadState = new GamePadState ();
  23. KeyboardState currentKeyboardState = new KeyboardState ();
  24. GamePadState currentGamePadState = new GamePadState ();
  25. // Enum describes all the rendering techniques used in this demo.
  26. enum DemoEffect
  27. {
  28. Desaturate,
  29. Disappear,
  30. RefractCat,
  31. RefractGlacier,
  32. Normalmap,
  33. }
  34. DemoEffect currentEffect = DemoEffect.RefractGlacier;
  35. // Effects used by this sample.
  36. Effect desaturateEffect;
  37. Effect disappearEffect;
  38. Effect normalmapEffect;
  39. Effect refractionEffect;
  40. // Textures used by this sample.
  41. Texture2D catTexture;
  42. Texture2D catNormalmapTexture;
  43. Texture2D glacierTexture;
  44. Texture2D waterfallTexture;
  45. // SpriteBatch instance used to render all the effects.
  46. SpriteBatch spriteBatch;
  47. public SpriteEffectsGame ()
  48. {
  49. graphics = new GraphicsDeviceManager (this);
  50. Content.RootDirectory = "Content";
  51. }
  52. /// <summary>
  53. /// Loads graphics content.
  54. /// </summary>
  55. protected override void LoadContent ()
  56. {
  57. spriteBatch = new SpriteBatch(graphics.GraphicsDevice);
  58. // Load Textures
  59. catTexture = Content.Load<Texture2D>("cat");
  60. catNormalmapTexture = Content.Load<Texture2D>("cat_normalmap");
  61. glacierTexture = Content.Load<Texture2D>("glacier");
  62. waterfallTexture = Content.Load<Texture2D>("waterfall");
  63. // Load Shaders
  64. desaturateEffect = Content.Load<Effect> ("desaturate");
  65. disappearEffect = Content.Load<Effect> ("disappear");
  66. normalmapEffect = Content.Load<Effect> ("normalmap");
  67. refractionEffect = Content.Load<Effect> ("refraction");
  68. }
  69. /// <summary>
  70. /// Allows the game to run logic.
  71. /// </summary>
  72. protected override void Update (GameTime gameTime)
  73. {
  74. HandleInput ();
  75. if (NextButtonPressed ()) {
  76. currentEffect++;
  77. if (currentEffect > DemoEffect.Normalmap)
  78. currentEffect = 0;
  79. }
  80. base.Update (gameTime);
  81. }
  82. /// <summary>
  83. /// This is called when the game should draw itself.
  84. /// </summary>
  85. protected override void Draw (GameTime gameTime)
  86. {
  87. switch (currentEffect) {
  88. case DemoEffect.Desaturate:
  89. DrawDesaturate (gameTime);
  90. break;
  91. case DemoEffect.Disappear:
  92. DrawDisappear (gameTime);
  93. break;
  94. case DemoEffect.Normalmap:
  95. DrawNormalmap (gameTime);
  96. break;
  97. case DemoEffect.RefractCat:
  98. DrawRefractCat (gameTime);
  99. break;
  100. case DemoEffect.RefractGlacier:
  101. DrawRefractGlacier (gameTime);
  102. break;
  103. }
  104. base.Draw (gameTime);
  105. }
  106. /// <summary>
  107. /// Effect dynamically changes color saturation.
  108. /// </summary>
  109. void DrawDesaturate (GameTime gameTime)
  110. {
  111. GraphicsDevice.Clear(Color.White);
  112. // Begin the sprite batch, using our custom effect.
  113. spriteBatch.Begin (0, null, null, null, null, desaturateEffect);
  114. // Draw four copies of the same sprite with different saturation levels.
  115. // The saturation amount is passed into the effect using the alpha of the
  116. // SpriteBatch.Draw color parameter. This isn't as flexible as using a
  117. // regular effect parameter, but makes it easy to draw many sprites with
  118. // a different saturation amount for each. If we had used an effect
  119. // parameter for this, we would have to end the sprite batch, then begin
  120. // a new one, each time we wanted to change the saturation setting.
  121. byte pulsate = (byte)Pulsate (gameTime, 4, 0, 255);
  122. // Use explicit byte casts to avoid ambiguity between int and byte overloads
  123. spriteBatch.Draw (glacierTexture,
  124. QuarterOfScreen (0, 0),
  125. new Color ((byte)255, (byte)255, (byte)255, (byte)0));
  126. spriteBatch.Draw (glacierTexture,
  127. QuarterOfScreen (1, 0),
  128. new Color ((byte)255, (byte)255, (byte)255, (byte)64));
  129. spriteBatch.Draw (glacierTexture,
  130. QuarterOfScreen (0, 1),
  131. new Color ((byte)255, (byte)255, (byte)255, (byte)255));
  132. spriteBatch.Draw (glacierTexture,
  133. QuarterOfScreen (1, 1),
  134. new Color ((byte)255, (byte)255, (byte)255, (byte)pulsate));
  135. // End the sprite batch.
  136. spriteBatch.End ();
  137. }
  138. /// <summary>
  139. /// Effect uses a scrolling overlay texture to make different parts of
  140. /// an image fade in or out at different speeds.
  141. /// </summary>
  142. void DrawDisappear (GameTime gameTime)
  143. {
  144. //GraphicsDevice.Clear(Color.White);
  145. // Draw the background image.
  146. spriteBatch.Begin ();
  147. spriteBatch.Draw (glacierTexture, GraphicsDevice.Viewport.Bounds, Color.White);
  148. spriteBatch.End ();
  149. // Set an effect parameter to make our overlay
  150. // texture scroll in a giant circle.
  151. //TextureSampler
  152. disappearEffect.Parameters ["OverlayScroll"].SetValue (
  153. MoveInCircle (gameTime, 0.8f) * 0.25f);
  154. // Set the overlay texture (as texture 1,
  155. // because 0 will be the main sprite texture).
  156. graphics.GraphicsDevice.Textures [1] = waterfallTexture;
  157. // Begin the sprite batch.
  158. spriteBatch.Begin (0, null, null, null, null, disappearEffect);
  159. // Draw the sprite, passing the fade amount as the
  160. // alpha of the SpriteBatch.Draw color parameter.
  161. byte fade = (byte)Pulsate (gameTime, 2, 0, 255);
  162. spriteBatch.Draw (catTexture,
  163. MoveInCircle (gameTime, catTexture, 1),
  164. new Color ((byte)255, (byte)255, (byte)255, (byte)fade));
  165. // End the sprite batch.
  166. spriteBatch.End ();
  167. }
  168. /// <summary>
  169. // Effect uses a scrolling displacement texture to offset the position of
  170. // the main texture.
  171. /// </summary>
  172. void DrawRefractCat (GameTime gameTime)
  173. {
  174. // Draw the background image.
  175. spriteBatch.Begin ();
  176. spriteBatch.Draw (glacierTexture, GraphicsDevice.Viewport.Bounds, Color.White);
  177. spriteBatch.End ();
  178. // Set an effect parameter to make the
  179. // displacement texture scroll in a giant circle.
  180. refractionEffect.Parameters ["DisplacementScroll"].SetValue (
  181. MoveInCircle (gameTime, 0.1f));
  182. // Set the displacement texture.
  183. graphics.GraphicsDevice.Textures [1] = waterfallTexture;
  184. // Begin the sprite batch.
  185. spriteBatch.Begin (0, null, null, null, null, refractionEffect);
  186. // Draw the sprite.
  187. spriteBatch.Draw (catTexture,
  188. MoveInCircle (gameTime, catTexture, 1),
  189. Color.White);
  190. // End the sprite batch.
  191. spriteBatch.End ();
  192. }
  193. /// <summary>
  194. // Effect uses a scrolling displacement texture to offset the position of
  195. // the main texture.
  196. /// </summary>
  197. void DrawRefractGlacier (GameTime gameTime)
  198. {
  199. // Set an effect parameter to make the
  200. // displacement texture scroll in a giant circle.
  201. refractionEffect.Parameters ["DisplacementScroll"].SetValue (
  202. MoveInCircle (gameTime, 0.2f));
  203. // Set the displacement texture.
  204. graphics.GraphicsDevice.Textures [1] = glacierTexture;
  205. // Begin the sprite batch.
  206. spriteBatch.Begin (0, null, null, null, null, refractionEffect);
  207. //spriteBatch.Begin();
  208. // Because the effect will displace the texture coordinates before
  209. // sampling the main texture, the coordinates could sometimes go right
  210. // off the edges of the texture, which looks ugly. To prevent this, we
  211. // adjust our sprite source region to leave a little border around the
  212. // edge of the texture. The displacement effect will then just move the
  213. // texture coordinates into this border region, without ever hitting
  214. // the edge of the texture.
  215. Rectangle croppedGlacier = new Rectangle (32, 32,
  216. glacierTexture.Width - 64,
  217. glacierTexture.Height - 64);
  218. spriteBatch.Draw (glacierTexture,
  219. GraphicsDevice.Viewport.Bounds,
  220. //croppedGlacier,
  221. Color.White);
  222. // End the sprite batch.
  223. spriteBatch.End ();
  224. }
  225. /// <summary>
  226. /// Effect uses a normalmap texture to apply realtime lighting while
  227. /// drawing a 2D sprite.
  228. /// </summary>
  229. void DrawNormalmap (GameTime gameTime)
  230. {
  231. // Draw the background image.
  232. spriteBatch.Begin ();
  233. spriteBatch.Draw (glacierTexture, GraphicsDevice.Viewport.Bounds, Color.White);
  234. spriteBatch.End ();
  235. // Animate the light direction.
  236. Vector2 spinningLight = MoveInCircle (gameTime, 1.5f);
  237. double time = gameTime.TotalGameTime.TotalSeconds;
  238. float tiltUpAndDown = 0.5f + (float)Math.Cos (time * 0.75) * 0.1f;
  239. Vector3 lightDirection = new Vector3 (spinningLight * tiltUpAndDown,
  240. 1 - tiltUpAndDown);
  241. lightDirection.Normalize ();
  242. normalmapEffect.Parameters ["LightDirection"].SetValue (lightDirection);
  243. // Set the normalmap texture.
  244. graphics.GraphicsDevice.Textures [1] = catNormalmapTexture;
  245. // Begin the sprite batch.
  246. spriteBatch.Begin (0, null, null, null, null, normalmapEffect);
  247. // Draw the sprite.
  248. spriteBatch.Draw (catTexture, CenterOnScreen (catTexture), Color.Azure);
  249. // End the sprite batch.
  250. spriteBatch.End ();
  251. // End the sprite batch.
  252. spriteBatch.End ();
  253. }
  254. /// <summary>
  255. /// Helper calculates the destination rectangle needed
  256. /// to draw a sprite to one quarter of the screen.
  257. /// </summary>
  258. Rectangle QuarterOfScreen (int x, int y)
  259. {
  260. Viewport viewport = graphics.GraphicsDevice.Viewport;
  261. int w = viewport.Width / 2;
  262. int h = viewport.Height / 2;
  263. return new Rectangle (w * x, h * y, w, h);
  264. }
  265. /// <summary>
  266. /// Helper calculates the destination position needed
  267. /// to center a sprite in the middle of the screen.
  268. /// </summary>
  269. Vector2 CenterOnScreen (Texture2D texture)
  270. {
  271. Viewport viewport = graphics.GraphicsDevice.Viewport;
  272. int x = (viewport.Width - texture.Width) / 2;
  273. int y = (viewport.Height - texture.Height) / 2;
  274. return new Vector2 (x, y);
  275. }
  276. /// <summary>
  277. /// Helper computes a value that oscillates over time.
  278. /// </summary>
  279. static float Pulsate (GameTime gameTime, float speed, float min, float max)
  280. {
  281. double time = gameTime.TotalGameTime.TotalSeconds * speed;
  282. return min + ((float)Math.Sin (time) + 1) / 2 * (max - min);
  283. }
  284. /// <summary>
  285. /// Helper for moving a value around in a circle.
  286. /// </summary>
  287. static Vector2 MoveInCircle (GameTime gameTime, float speed)
  288. {
  289. double time = gameTime.TotalGameTime.TotalSeconds * speed;
  290. float x = (float)Math.Cos (time);
  291. float y = (float)Math.Sin (time);
  292. return new Vector2 (x, y);
  293. }
  294. /// <summary>
  295. /// Helper for moving a sprite around in a circle.
  296. /// </summary>
  297. Vector2 MoveInCircle (GameTime gameTime, Texture2D texture, float speed)
  298. {
  299. Viewport viewport = graphics.GraphicsDevice.Viewport;
  300. float x = (viewport.Width - texture.Width) / 2;
  301. float y = (viewport.Height - texture.Height) / 2;
  302. return MoveInCircle (gameTime, speed) * 128 + new Vector2 (x, y);
  303. }
  304. /// <summary>
  305. /// Handles input for quitting the game.
  306. /// </summary>
  307. private void HandleInput ()
  308. {
  309. lastKeyboardState = currentKeyboardState;
  310. lastGamePadState = currentGamePadState;
  311. currentKeyboardState = Keyboard.GetState ();
  312. currentGamePadState = GamePad.GetState (PlayerIndex.One);
  313. // Check for exit.
  314. if (currentKeyboardState.IsKeyDown (Keys.Escape) ||
  315. currentGamePadState.Buttons.Back == ButtonState.Pressed) {
  316. Exit ();
  317. }
  318. }
  319. /// <summary>
  320. /// Checks whether the user wants to advance to the next rendering effect.
  321. /// </summary>
  322. bool NextButtonPressed ()
  323. {
  324. // Have they pressed the space bar?
  325. if (currentKeyboardState.IsKeyDown (Keys.Space) &&
  326. !lastKeyboardState.IsKeyDown (Keys.Space))
  327. return true;
  328. // Have they pressed the gamepad A button?
  329. if (currentGamePadState.Buttons.A == ButtonState.Pressed &&
  330. lastGamePadState.Buttons.A == ButtonState.Released)
  331. return true;
  332. return false;
  333. }
  334. }
  335. }