SpriteEffects.cs 15 KB

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