#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 = DemoEffect.RefractGlacier; // 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"); desaturateEffect = new DesaturateEffect(GraphicsDevice); disappearEffect = new DisappearEffect(GraphicsDevice); normalmapEffect = new NormalmapEffect(GraphicsDevice); refractionEffect = new RefractionEffect(GraphicsDevice); 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) { GraphicsDevice.Clear(Color.White); // 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) { //GraphicsDevice.Clear(Color.White); // 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. //TextureSampler 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] = glacierTexture; // Begin the sprite batch. spriteBatch.Begin (0, null, null, null, null, refractionEffect); //spriteBatch.Begin(); // 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.Azure); // End the sprite batch. spriteBatch.End (); // 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 } }