#region File Description //----------------------------------------------------------------------------- // SpriteEffects.cs // // Microsoft XNA Community Game Platform // Copyright (C) Microsoft Corporation. All rights reserved. //----------------------------------------------------------------------------- #endregion #region Using Statements using System; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Content; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input; #endregion namespace SpriteEffects { /// /// Sample demonstrating how shaders can be used to /// apply special effects to sprite rendering. /// public class SpriteEffectsGame : Microsoft.Xna.Framework.Game { #region Fields GraphicsDeviceManager graphics; KeyboardState lastKeyboardState = new KeyboardState(); GamePadState lastGamePadState = new GamePadState(); KeyboardState currentKeyboardState = new KeyboardState(); GamePadState currentGamePadState = new GamePadState(); // Enum describes all the rendering techniques used in this demo. enum DemoEffect { Desaturate, Disappear, RefractCat, RefractGlacier, Normalmap, } DemoEffect currentEffect = 0; // Effects used by this sample. Effect desaturateEffect; Effect disappearEffect; Effect normalmapEffect; Effect refractionEffect; // Textures used by this sample. Texture2D catTexture; Texture2D catNormalmapTexture; Texture2D glacierTexture; Texture2D waterfallTexture; // SpriteBatch instance used to render all the effects. SpriteBatch spriteBatch; #endregion #region Initialization public SpriteEffectsGame() { graphics = new GraphicsDeviceManager(this); Content.RootDirectory = "Content"; } /// /// Loads graphics content. /// protected override void LoadContent() { desaturateEffect = Content.Load("desaturate"); disappearEffect = Content.Load("disappear"); normalmapEffect = Content.Load("normalmap"); refractionEffect = Content.Load("refraction"); catTexture = Content.Load("cat"); catNormalmapTexture = Content.Load("cat_normalmap"); glacierTexture = Content.Load("glacier"); waterfallTexture = Content.Load("waterfall"); spriteBatch = new SpriteBatch(graphics.GraphicsDevice); } #endregion #region Update and Draw /// /// Allows the game to run logic. /// protected override void Update(GameTime gameTime) { HandleInput(); if (NextButtonPressed()) { currentEffect++; if (currentEffect > DemoEffect.Normalmap) currentEffect = 0; } base.Update(gameTime); } /// /// This is called when the game should draw itself. /// protected override void Draw(GameTime gameTime) { switch (currentEffect) { case DemoEffect.Desaturate: DrawDesaturate(gameTime); break; case DemoEffect.Disappear: DrawDisappear(gameTime); break; case DemoEffect.Normalmap: DrawNormalmap(gameTime); break; case DemoEffect.RefractCat: DrawRefractCat(gameTime); break; case DemoEffect.RefractGlacier: DrawRefractGlacier(gameTime); break; } base.Draw(gameTime); } /// /// Effect dynamically changes color saturation. /// void DrawDesaturate(GameTime gameTime) { // Begin the sprite batch, using our custom effect. spriteBatch.Begin(0, null, null, null, null, desaturateEffect); // Draw four copies of the same sprite with different saturation levels. // The saturation amount is passed into the effect using the alpha of the // SpriteBatch.Draw color parameter. This isn't as flexible as using a // regular effect parameter, but makes it easy to draw many sprites with // a different saturation amount for each. If we had used an effect // parameter for this, we would have to end the sprite batch, then begin // a new one, each time we wanted to change the saturation setting. byte pulsate = (byte)Pulsate(gameTime, 4, 0, 255); spriteBatch.Draw(glacierTexture, QuarterOfScreen(0, 0), new Color(255, 255, 255, 0)); spriteBatch.Draw(glacierTexture, QuarterOfScreen(1, 0), new Color(255, 255, 255, 64)); spriteBatch.Draw(glacierTexture, QuarterOfScreen(0, 1), new Color(255, 255, 255, 255)); spriteBatch.Draw(glacierTexture, QuarterOfScreen(1, 1), new Color(255, 255, 255, pulsate)); // End the sprite batch. spriteBatch.End(); } /// /// Effect uses a scrolling overlay texture to make different parts of /// an image fade in or out at different speeds. /// void DrawDisappear(GameTime gameTime) { // Draw the background image. spriteBatch.Begin(); spriteBatch.Draw(glacierTexture, GraphicsDevice.Viewport.Bounds, Color.White); spriteBatch.End(); // Set an effect parameter to make our overlay // texture scroll in a giant circle. disappearEffect.Parameters["OverlayScroll"].SetValue( MoveInCircle(gameTime, 0.8f) * 0.25f); // Set the overlay texture (as texture 1, // because 0 will be the main sprite texture). graphics.GraphicsDevice.Textures[1] = waterfallTexture; // Begin the sprite batch. spriteBatch.Begin(0, null, null, null, null, disappearEffect); // Draw the sprite, passing the fade amount as the // alpha of the SpriteBatch.Draw color parameter. byte fade = (byte)Pulsate(gameTime, 2, 0, 255); spriteBatch.Draw(catTexture, MoveInCircle(gameTime, catTexture, 1), new Color(255, 255, 255, fade)); // End the sprite batch. spriteBatch.End(); } /// // Effect uses a scrolling displacement texture to offset the position of // the main texture. /// void DrawRefractCat(GameTime gameTime) { // Draw the background image. spriteBatch.Begin(); spriteBatch.Draw(glacierTexture, GraphicsDevice.Viewport.Bounds, Color.White); spriteBatch.End(); // Set an effect parameter to make the // displacement texture scroll in a giant circle. refractionEffect.Parameters["DisplacementScroll"].SetValue( MoveInCircle(gameTime, 0.1f)); // Set the displacement texture. graphics.GraphicsDevice.Textures[1] = waterfallTexture; // Begin the sprite batch. spriteBatch.Begin(0, null, null, null, null, refractionEffect); // Draw the sprite. spriteBatch.Draw(catTexture, MoveInCircle(gameTime, catTexture, 1), Color.White); // End the sprite batch. spriteBatch.End(); } /// // Effect uses a scrolling displacement texture to offset the position of // the main texture. /// void DrawRefractGlacier(GameTime gameTime) { // Set an effect parameter to make the // displacement texture scroll in a giant circle. refractionEffect.Parameters["DisplacementScroll"].SetValue( MoveInCircle(gameTime, 0.2f)); // Set the displacement texture. graphics.GraphicsDevice.Textures[1] = waterfallTexture; // Begin the sprite batch. spriteBatch.Begin(0, null, null, null, null, refractionEffect); // Because the effect will displace the texture coordinates before // sampling the main texture, the coordinates could sometimes go right // off the edges of the texture, which looks ugly. To prevent this, we // adjust our sprite source region to leave a little border around the // edge of the texture. The displacement effect will then just move the // texture coordinates into this border region, without ever hitting // the edge of the texture. Rectangle croppedGlacier = new Rectangle(32, 32, glacierTexture.Width - 64, glacierTexture.Height - 64); spriteBatch.Draw(glacierTexture, GraphicsDevice.Viewport.Bounds, croppedGlacier, Color.White); // End the sprite batch. spriteBatch.End(); } /// /// Effect uses a normalmap texture to apply realtime lighting while /// drawing a 2D sprite. /// void DrawNormalmap(GameTime gameTime) { // Draw the background image. spriteBatch.Begin(); spriteBatch.Draw(glacierTexture, GraphicsDevice.Viewport.Bounds, Color.White); spriteBatch.End(); // Animate the light direction. Vector2 spinningLight = MoveInCircle(gameTime, 1.5f); double time = gameTime.TotalGameTime.TotalSeconds; float tiltUpAndDown = 0.5f + (float)Math.Cos(time * 0.75) * 0.1f; Vector3 lightDirection = new Vector3(spinningLight * tiltUpAndDown, 1 - tiltUpAndDown); lightDirection.Normalize(); normalmapEffect.Parameters["LightDirection"].SetValue(lightDirection); // Set the normalmap texture. graphics.GraphicsDevice.Textures[1] = catNormalmapTexture; // Begin the sprite batch. spriteBatch.Begin(0, null, null, null, null, normalmapEffect); // Draw the sprite. spriteBatch.Draw(catTexture, CenterOnScreen(catTexture), Color.White); // End the sprite batch. spriteBatch.End(); } /// /// Helper calculates the destination rectangle needed /// to draw a sprite to one quarter of the screen. /// Rectangle QuarterOfScreen(int x, int y) { Viewport viewport = graphics.GraphicsDevice.Viewport; int w = viewport.Width / 2; int h = viewport.Height / 2; return new Rectangle(w * x, h * y, w, h); } /// /// Helper calculates the destination position needed /// to center a sprite in the middle of the screen. /// Vector2 CenterOnScreen(Texture2D texture) { Viewport viewport = graphics.GraphicsDevice.Viewport; int x = (viewport.Width - texture.Width) / 2; int y = (viewport.Height - texture.Height) / 2; return new Vector2(x, y); } /// /// Helper computes a value that oscillates over time. /// static float Pulsate(GameTime gameTime, float speed, float min, float max) { double time = gameTime.TotalGameTime.TotalSeconds * speed; return min + ((float)Math.Sin(time) + 1) / 2 * (max - min); } /// /// Helper for moving a value around in a circle. /// static Vector2 MoveInCircle(GameTime gameTime, float speed) { double time = gameTime.TotalGameTime.TotalSeconds * speed; float x = (float)Math.Cos(time); float y = (float)Math.Sin(time); return new Vector2(x, y); } /// /// Helper for moving a sprite around in a circle. /// Vector2 MoveInCircle(GameTime gameTime, Texture2D texture, float speed) { Viewport viewport = graphics.GraphicsDevice.Viewport; float x = (viewport.Width - texture.Width) / 2; float y = (viewport.Height - texture.Height) / 2; return MoveInCircle(gameTime, speed) * 128 + new Vector2(x, y); } #endregion #region Handle Input /// /// Handles input for quitting the game. /// private void HandleInput() { lastKeyboardState = currentKeyboardState; lastGamePadState = currentGamePadState; currentKeyboardState = Keyboard.GetState(); currentGamePadState = GamePad.GetState(PlayerIndex.One); // Check for exit. if (currentKeyboardState.IsKeyDown(Keys.Escape) || currentGamePadState.Buttons.Back == ButtonState.Pressed) { Exit(); } } /// /// Checks whether the user wants to advance to the next rendering effect. /// bool NextButtonPressed() { // Have they pressed the space bar? if (currentKeyboardState.IsKeyDown(Keys.Space) && !lastKeyboardState.IsKeyDown(Keys.Space)) return true; // Have they pressed the gamepad A button? if (currentGamePadState.Buttons.A == ButtonState.Pressed && lastGamePadState.Buttons.A == ButtonState.Released) return true; return false; } #endregion } #region Entry Point /// /// The main entry point for the application. /// static class Program { static void Main() { using (SpriteEffectsGame game = new SpriteEffectsGame()) { game.Run(); } } } #endregion }