XNA for C#
DirectX 9 for C#
DirectX 9 for C++
DirectX 9 for VB
Forum
   August 19: 2D - Chapter 3 posted
My Book: Out Now!
      
       Go to section on this site

Additional info


Latest Forum posts

 BSP Tree Tutorial
  Posted by: lbmurali
  When: 21/08/2008 at 13:41:47

 Loading compiled .xnb files
  Posted by: lbmurali
  When: 21/08/2008 at 13:33:00

 The city isn't very detailed and....
  Posted by: dom96
  When: 21/08/2008 at 12:52:47

 Height-map, low framerate
  Posted by: Rich_Zap
  When: 21/08/2008 at 11:45:00

 Vertex and Index
  Posted by: radulph
  When: 21/08/2008 at 08:33:06

 The city isn't very detailed and....
  Posted by: radulph
  When: 21/08/2008 at 08:26:03

 Vertex and Index
  Posted by: engine
  When: 21/08/2008 at 06:11:35

 What about Joypad Control
  Posted by: greyrat
  When: 20/08/2008 at 22:19:13

 The city isn't very detailed and....
  Posted by: radulph
  When: 20/08/2008 at 17:20:00

 I have a problem(with camera i think)
  Posted by: vToMy
  When: 20/08/2008 at 16:25:45


Ads

Drawing a simple skydome

Before we start drawing our water, let’s first draw something that can be reflected by the water. We already have a nice terrain, let’s do something about the solid black sky. We’ve already tackled this problem in Series 2 by using a skybox, which works quite well. However, later in this series we’ll be generating cloud maps. It would almost be impossible to generate these maps so they can be used to decorate the skybox, in such a way that the borders between the 6 sides wouldn’t be visible. One cloud stopping at an edge would show there’s simply a box surrounding our terrain.

The logical solution to this is to use a skysphere. This is a globe surrounding the terrain, so only one texture is needed that can cover the sphere continuously. There’s one huge mathematical issue with this: finding the correct texture coordinates. This complex problem took Mercator his whole life to solve, so I guess it is beyond the scope of these tuts.

The way I solve it is by using a skydome: it is the top of a sphere. I have created the model for you and saved it in a dome.x file, which you can download here. Below you can see the wireframe of the model.



We can simply put a cloud texture on top of this skydome. You can download a sample cloud map here. We’ll load this model, so add the variables:

 Texture2D cloudMap;
 Model skyDome;

Note that we’re not using an array of textures, because I know the model only has one texture.

We just need to put this code in our LoadContent method to load the Model:


skyDome = Content.Load<Model> ("dome");
skyDome.Meshes[0].MeshParts[0].Effect = effect.Clone(device);

We load the Model from file, and replace its effect with our own.

Also load the cloudmap into the correct variable by putting this code in the LoadTextures method:


cloudMap = Content.Load<Texture2D> ("cloudMap");
Let’s move on to the Draw part. We’ll create another method, that will draw the skydome. It’s pretty much a standard textured model drawing routine. I have chosen the model size to stretch from -1 to +1, so we clearly need to scale it up factor 500 so the near clipping plane isn’t creating problems.

We’ll use the same 2 tricks we’ve used with the skybox: we will reposition the skydome always around our camera, and we will disable Z buffer writing when rendering the skybox. Read Series 2 or Recipe 2-8 for more details on this.

 private void DrawSkyDome(Matrix currentViewMatrix)
 {
     device.RenderState.DepthBufferWriteEnable = false;
 
     Matrix[] modelTransforms = new Matrix[skyDome.Bones.Count];
     skyDome.CopyAbsoluteBoneTransformsTo(modelTransforms);
 
     Matrix wMatrix = Matrix.CreateTranslation(0, -0.3f, 0) * Matrix.CreateScale(100) * Matrix.CreateTranslation(cameraPosition);
     foreach (ModelMesh mesh in skyDome.Meshes)
     {
         foreach (Effect currentEffect in mesh.Effects)
         {
             Matrix worldMatrix = modelTransforms[mesh.ParentBone.Index] * wMatrix;
             currentEffect.CurrentTechnique = currentEffect.Techniques["SkyDome"];
             currentEffect.Parameters["xWorld"].SetValue(worldMatrix);
             currentEffect.Parameters["xView"].SetValue(currentViewMatrix);
             currentEffect.Parameters["xProjection"].SetValue(projectionMatrix);
             currentEffect.Parameters["xTexture0"].SetValue(cloudMap);
             currentEffect.Parameters["xEnableLighting"].SetValue(false);
         }
         mesh.Draw();
     }
     device.RenderState.DepthBufferWriteEnable = true;
 }

Besides scaling up the dome by factor 100 and positioning it over the camera, we also move it slightly downward, so it’s edges are below the camera. This makes sure the largest part of the camera’s view frustum is covered by the dome. Next, we pass the cloudMap texture and render the dome using the SkyDome technique, which we’ll define next.

Don’t forget to call this method from within our Draw method. However, as with the skybox, make sure you draw the skydome BEFORE you draw anything else, such as the terrain:

 DrawSkyDome(viewMatrix);

Running this code should give you the next image:




DirectX Tutorial 6 - Skydome

If you appreciate the amount of time I spend creating and updating
these pages, feel free to donate -- any amount is welcome !



Click here to go to the forum on this chapter!

Or click on one of the topics on this chapter to go there:
  • Skydome technique?
          Hey again Riemer. In the tutorial it says, that w...
  • error in clouds.jpg
          Hello everybody, when I try to compile the code, ...
  • error load dome.x
          hi riemer, thans for this fantastics tutorials. ...
  • Dynamic skydome
          Hi, I've been playing around to get a dynamic ...
  • RenderTarget
          I'm trying to use a Night sky for the game but al...
  • skydome
          Hi first of all i want to thank riemer for all his...
  • skydome
          first of all: Thanks for all your hard work on the...


    Not very pretty, but this way you’ll want to read the chapters on Perlin cloud generation ;) After we’ve talked about water, that is.

     using System;
     using System.Collections.Generic;
     using Microsoft.Xna.Framework;
     using Microsoft.Xna.Framework.Audio;
     using Microsoft.Xna.Framework.Content;
     using Microsoft.Xna.Framework.GamerServices;
     using Microsoft.Xna.Framework.Graphics;
     using Microsoft.Xna.Framework.Input;
     using Microsoft.Xna.Framework.Net;
     using Microsoft.Xna.Framework.Storage;
     
     namespace XNAseries4
     {
         public struct VertexMultitextured
         {
             public Vector3 Position;
             public Vector3 Normal;
             public Vector4 TextureCoordinate;
             public Vector4 TexWeights;
     
             public static int SizeInBytes = (3 + 3 + 4 + 4) * sizeof(float);
             public static VertexElement[] VertexElements = new VertexElement[]
             {
                 new VertexElement( 0, 0, VertexElementFormat.Vector3, VertexElementMethod.Default, VertexElementUsage.Position, 0 ),
                 new VertexElement( 0, sizeof(float) * 3, VertexElementFormat.Vector3, VertexElementMethod.Default, VertexElementUsage.Normal, 0 ),
                 new VertexElement( 0, sizeof(float) * 6, VertexElementFormat.Vector4, VertexElementMethod.Default, VertexElementUsage.TextureCoordinate, 0 ),
                 new VertexElement( 0, sizeof(float) * 10, VertexElementFormat.Vector4, VertexElementMethod.Default, VertexElementUsage.TextureCoordinate, 1 ),
             };
         }
     
         public class Game1 : Microsoft.Xna.Framework.Game
         {
             GraphicsDeviceManager graphics;
             GraphicsDevice device;
     
             int terrainWidth;
             int terrainLength;
             float[,] heightData;
     
             VertexBuffer terrainVertexBuffer;
             IndexBuffer terrainIndexBuffer;
             VertexDeclaration terrainVertexDeclaration;
     
             Effect effect;
             Matrix viewMatrix;
             Matrix projectionMatrix;
     
             Vector3 cameraPosition = new Vector3(130, 30, -50);
             float leftrightRot = MathHelper.PiOver2;
             float updownRot = -MathHelper.Pi / 10.0f;
             const float rotationSpeed = 0.3f;
             const float moveSpeed = 30.0f;
             MouseState originalMouseState;
     
             Texture2D grassTexture;
             Texture2D sandTexture;
             Texture2D rockTexture;
             Texture2D snowTexture;
             Texture2D cloudMap;
             
             Model skyDome;
     
             public Game1()
             {
                 graphics = new GraphicsDeviceManager(this);
                 Content.RootDirectory = "Content";
             }
     
             protected override void Initialize()
             {
                 graphics.PreferredBackBufferWidth = 500;
                 graphics.PreferredBackBufferHeight = 500;
     
                 graphics.ApplyChanges();
                 Window.Title = "Riemer's XNA Tutorials -- Series 4";
     
                 base.Initialize();
             }
     
             protected override void LoadContent()
             {
                 device = GraphicsDevice;

                effect = Content.Load<Effect> ("Series4Effects");
                UpdateViewMatrix();
                projectionMatrix = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4, device.Viewport.AspectRatio, 0.3f, 1000.0f);

                Mouse.SetPosition(device.Viewport.Width / 2, device.Viewport.Height / 2);
                originalMouseState = Mouse.GetState();



                skyDome = Content.Load<Model> ("dome");            skyDome.Meshes[0].MeshParts[0].Effect = effect.Clone(device);

     
                 LoadVertices();
                 LoadTextures();
             }
     
             private void LoadVertices()
             {

                Texture2D heightMap = Content.Load<Texture2D> ("heightmap");            LoadHeightData(heightMap);

                VertexMultitextured[] terrainVertices = SetUpTerrainVertices();
                int[] terrainIndices = SetUpTerrainIndices();
                terrainVertices = CalculateNormals(terrainVertices, terrainIndices);
                CopyToTerrainBuffers(terrainVertices, terrainIndices);
                terrainVertexDeclaration = new VertexDeclaration(device, VertexMultitextured.VertexElements);
            }

            private void LoadTextures()
            {

                grassTexture = Content.Load<Texture2D> ("grass");
                sandTexture = Content.Load<Texture2D> ("sand");
                rockTexture = Content.Load<Texture2D> ("rock");
                snowTexture = Content.Load<Texture2D> ("snow");

                cloudMap = Content.Load<Texture2D> ("cloudMap");
             }
     
             private void LoadHeightData(Texture2D heightMap)
             {
                 float minimumHeight = float.MaxValue;
                 float maximumHeight = float.MinValue;
     
                 terrainWidth = heightMap.Width;
                 terrainLength = heightMap.Height;
     
                 Color[] heightMapColors = new Color[terrainWidth * terrainLength];
                 heightMap.GetData(heightMapColors);
     
                 heightData = new float[terrainWidth, terrainLength];
                 for (int x = 0; x < terrainWidth; x++)
                     for (int y = 0; y < terrainLength; y++)
                     {
                         heightData[x, y] = heightMapColors[x + y * terrainWidth].R;
                         if (heightData[x, y] < minimumHeight) minimumHeight = heightData[x, y];
                         if (heightData[x, y] > maximumHeight) maximumHeight = heightData[x, y];
                     }
     
                 for (int x = 0; x < terrainWidth; x++)
                     for (int y = 0; y < terrainLength; y++)
                         heightData[x, y] = (heightData[x, y] - minimumHeight) / (maximumHeight - minimumHeight) * 30.0f;
             }
     
             private VertexMultitextured[] SetUpTerrainVertices()
             {
                 VertexMultitextured[] terrainVertices = new VertexMultitextured[terrainWidth * terrainLength];
     
                 for (int x = 0; x < terrainWidth; x++)
                 {
                     for (int y = 0; y < terrainLength; y++)
                     {
                         terrainVertices[x + y * terrainWidth].Position = new Vector3(x, heightData[x, y], -y);
                         terrainVertices[x + y * terrainWidth].TextureCoordinate.X = (float)x / 30.0f;
                         terrainVertices[x + y * terrainWidth].TextureCoordinate.Y = (float)y / 30.0f;
     
                         terrainVertices[x + y * terrainWidth].TexWeights.X = MathHelper.Clamp(1.0f - Math.Abs(heightData[x, y] - 0) / 8.0f, 0, 1);
                         terrainVertices[x + y * terrainWidth].TexWeights.Y = MathHelper.Clamp(1.0f - Math.Abs(heightData[x, y] - 12) / 6.0f, 0, 1);
                         terrainVertices[x + y * terrainWidth].TexWeights.Z = MathHelper.Clamp(1.0f - Math.Abs(heightData[x, y] - 20) / 6.0f, 0, 1);
                         terrainVertices[x + y * terrainWidth].TexWeights.W = MathHelper.Clamp(1.0f - Math.Abs(heightData[x, y] - 30) / 6.0f, 0, 1);
     
                         float total = terrainVertices[x + y * terrainWidth].TexWeights.X;
                         total += terrainVertices[x + y * terrainWidth].TexWeights.Y;
                         total += terrainVertices[x + y * terrainWidth].TexWeights.Z;
                         total += terrainVertices[x + y * terrainWidth].TexWeights.W;
     
                         terrainVertices[x + y * terrainWidth].TexWeights.X /= total;
                         terrainVertices[x + y * terrainWidth].TexWeights.Y /= total;
                         terrainVertices[x + y * terrainWidth].TexWeights.Z /= total;
                         terrainVertices[x + y * terrainWidth].TexWeights.W /= total;
                     }
                 }
     
                 return terrainVertices;
             }
     
             private int[] SetUpTerrainIndices()
             {
                 int[] indices = new int[(terrainWidth - 1) * (terrainLength - 1) * 6];
                 int counter = 0;
                 for (int y = 0; y < terrainLength - 1; y++)
                 {
                     for (int x = 0; x < terrainWidth - 1; x++)
                     {
                         int lowerLeft = x + y * terrainWidth;
                         int lowerRight = (x + 1) + y * terrainWidth;
                         int topLeft = x + (y + 1) * terrainWidth;
                         int topRight = (x + 1) + (y + 1) * terrainWidth;
     
                         indices[counter++] = topLeft;
                         indices[counter++] = lowerRight;
                         indices[counter++] = lowerLeft;
     
                         indices[counter++] = topLeft;
                         indices[counter++] = topRight;
                         indices[counter++] = lowerRight;
                     }
                 }
     
                 return indices;
             }
     
             private VertexMultitextured[] CalculateNormals(VertexMultitextured[] vertices, int[] indices)
             {
                 for (int i = 0; i < vertices.Length; i++)
                     vertices[i].Normal = new Vector3(0, 0, 0);
     
                 for (int i = 0; i < indices.Length / 3; i++)
                 {
                     int index1 = indices[i * 3];
                     int index2 = indices[i * 3 + 1];
                     int index3 = indices[i * 3 + 2];
     
                     Vector3 side1 = vertices[index1].Position - vertices[index3].Position;
                     Vector3 side2 = vertices[index1].Position - vertices[index2].Position;
                     Vector3 normal = Vector3.Cross(side1, side2);
     
                     vertices[index1].Normal += normal;
                     vertices[index2].Normal += normal;
                     vertices[index3].Normal += normal;
                 }
     
                 for (int i = 0; i < vertices.Length; i++)
                     vertices[i].Normal.Normalize();
     
                 return vertices;
             }
     
             private void CopyToTerrainBuffers(VertexMultitextured[] vertices, int[] indices)
             {
                 terrainVertexBuffer = new VertexBuffer(device, vertices.Length * VertexMultitextured.SizeInBytes, BufferUsage.WriteOnly);
                 terrainVertexBuffer.SetData(vertices);
     
                 terrainIndexBuffer = new IndexBuffer(device, typeof(int), indices.Length, BufferUsage.WriteOnly);
                 terrainIndexBuffer.SetData(indices);
             }
     
             protected override void UnloadContent()
             {
             }
     
             protected override void Update(GameTime gameTime)
             {
                 if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
                     this.Exit();
     
                 float timeDifference = (float)gameTime.ElapsedGameTime.TotalMilliseconds / 1000.0f;
                 ProcessInput(timeDifference);
     
                 base.Update(gameTime);
             }
     
             private void ProcessInput(float amount)
             {
                 MouseState currentMouseState = Mouse.GetState();
                 if (currentMouseState != originalMouseState)
                 {
                     float xDifference = currentMouseState.X - originalMouseState.X;
                     float yDifference = currentMouseState.Y - originalMouseState.Y;
                     leftrightRot -= rotationSpeed * xDifference * amount;
                     updownRot -= rotationSpeed * yDifference * amount;
                     Mouse.SetPosition(device.Viewport.Width / 2, device.Viewport.Height / 2);
                     UpdateViewMatrix();
                 }
     
                 Vector3 moveVector = new Vector3(0, 0, 0);
                 KeyboardState keyState = Keyboard.GetState();
                 if (keyState.IsKeyDown(Keys.Up) || keyState.IsKeyDown(Keys.W))
                     moveVector += new Vector3(0, 0, -1);
                 if (keyState.IsKeyDown(Keys.Down) || keyState.IsKeyDown(Keys.S))
                     moveVector += new Vector3(0, 0, 1);
                 if (keyState.IsKeyDown(Keys.Right) || keyState.IsKeyDown(Keys.D))
                     moveVector += new Vector3(1, 0, 0);
                 if (keyState.IsKeyDown(Keys.Left) || keyState.IsKeyDown(Keys.A))
                     moveVector += new Vector3(-1, 0, 0);
                 if (keyState.IsKeyDown(Keys.Q))
                     moveVector += new Vector3(0, 1, 0);
                 if (keyState.IsKeyDown(Keys.Z))
                     moveVector += new Vector3(0, -1, 0);
                 AddToCameraPosition(moveVector * amount);
             }
     
             private void AddToCameraPosition(Vector3 vectorToAdd)
             {
                 Matrix cameraRotation = Matrix.CreateRotationX(updownRot) * Matrix.CreateRotationY(leftrightRot);
                 Vector3 rotatedVector = Vector3.Transform(vectorToAdd, cameraRotation);
                 cameraPosition += moveSpeed * rotatedVector;
                 UpdateViewMatrix();
             }
     
             private void UpdateViewMatrix()
             {
                 Matrix cameraRotation = Matrix.CreateRotationX(updownRot) * Matrix.CreateRotationY(leftrightRot);
     
                 Vector3 cameraOriginalTarget = new Vector3(0, 0, -1);
                 Vector3 cameraOriginalUpVector = new Vector3(0, 1, 0);
     
                 Vector3 cameraRotatedTarget = Vector3.Transform(cameraOriginalTarget, cameraRotation);
                 Vector3 cameraFinalTarget = cameraPosition + cameraRotatedTarget;
     
                 Vector3 cameraRotatedUpVector = Vector3.Transform(cameraOriginalUpVector, cameraRotation);
     
                 viewMatrix = Matrix.CreateLookAt(cameraPosition, cameraFinalTarget, cameraRotatedUpVector);
             }
     
             protected override void Draw(GameTime gameTime)
             {
                 float time = (float)gameTime.TotalGameTime.TotalMilliseconds / 100.0f;
                 device.RenderState.CullMode = CullMode.None;
     
                 device.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, Color.Black, 1.0f, 0);
                 DrawSkyDome(viewMatrix);
                 DrawTerrain(viewMatrix);            
     
                 base.Draw(gameTime);
             }
     
             private void DrawTerrain(Matrix currentViewMatrix)
             {
                 effect.CurrentTechnique = effect.Techniques["MultiTextured"];
                 effect.Parameters["xTexture0"].SetValue(sandTexture);
                 effect.Parameters["xTexture1"].SetValue(grassTexture);
                 effect.Parameters["xTexture2"].SetValue(rockTexture);
                 effect.Parameters["xTexture3"].SetValue(snowTexture);
     
                 Matrix worldMatrix = Matrix.Identity;
                 effect.Parameters["xWorld"].SetValue(worldMatrix);
                 effect.Parameters["xView"].SetValue(currentViewMatrix);
                 effect.Parameters["xProjection"].SetValue(projectionMatrix);            
     
                 effect.Parameters["xEnableLighting"].SetValue(true);
                 effect.Parameters["xAmbient"].SetValue(0.4f);
                 effect.Parameters["xLightDirection"].SetValue(new Vector3(-0.5f, -1, -0.5f));
     
                 effect.Begin();
                 foreach (EffectPass pass in effect.CurrentTechnique.Passes)
                 {
                     pass.Begin();
     
                     device.Vertices[0].SetSource(terrainVertexBuffer, 0, VertexMultitextured.SizeInBytes);
                     device.Indices = terrainIndexBuffer;
                     device.VertexDeclaration = terrainVertexDeclaration;
     
                     int noVertices = terrainVertexBuffer.SizeInBytes / VertexMultitextured.SizeInBytes;
                     int noTriangles = terrainIndexBuffer.SizeInBytes / sizeof(int) / 3;
                     device.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, noVertices, 0, noTriangles);
     
                     pass.End();
                 }
                 effect.End();
             }
     
             private void DrawSkyDome(Matrix currentViewMatrix)
             {
                 device.RenderState.DepthBufferWriteEnable = false;
     
                 Matrix[] modelTransforms = new Matrix[skyDome.Bones.Count];
                 skyDome.CopyAbsoluteBoneTransformsTo(modelTransforms);
     
                 Matrix wMatrix = Matrix.CreateTranslation(0, -0.3f, 0) * Matrix.CreateScale(100) * Matrix.CreateTranslation(cameraPosition);
                 foreach (ModelMesh mesh in skyDome.Meshes)
                 {
                     foreach (Effect currentEffect in mesh.Effects)
                     {
                         Matrix worldMatrix = modelTransforms[mesh.ParentBone.Index] * wMatrix;
                         currentEffect.CurrentTechnique = currentEffect.Techniques["Textured"];
                         currentEffect.Parameters["xWorld"].SetValue(worldMatrix);
                         currentEffect.Parameters["xView"].SetValue(currentViewMatrix);
                         currentEffect.Parameters["xProjection"].SetValue(projectionMatrix);
                         currentEffect.Parameters["xTexture"].SetValue(cloudMap);
                         currentEffect.Parameters["xEnableLighting"].SetValue(false);
                     }
                     mesh.Draw();
                 }
                 device.RenderState.DepthBufferWriteEnable = true;
             }
         }
     }
     


    Google
     
    Webwww.riemers.net


    If you appreciate the amount of time I spend creating and updating
    these pages, feel free to donate -- any amount is welcome !



    - Website design & XNA + DirectX code : Riemer Grootjans -
    ©2003 - 2008 Riemer Grootjans
  • Translations

    This site in English
    This site in Korean
    This site in Czech

    Microsoft MVP Award



    2007 - 2008 MVP Award
    DirectX - XNA

    Contents

    News
    Home
    Forum
    XNA 2.0 Recipes Book (8)
    Downloads
    Extra Reading (3)
    Matrices: geometrical
    Matrix Mathematics
    Homogenous matrices
    Tutorials (141)
    XNA 2.0 using C# (70)
    2D Series: Shooters! (3)
    3D Series 1: Terrain (13)
    3D Series 2: Flightsim (14)
    3D Series 3: HLSL (18)
    3D Series 4: Adv. terrain (19)
    Starting code
    Mouse camera
    Textured terrain
    Multitexturing
    Adding detail
    Skydome
    The water technique
    Refraction map
    Reflection map
    Perfect mirror
    Ripples
    The Fresnel term
    Moving water
    Specular highlights
    Billboarding
    Region growing
    Billboarding renderstates
    Perlin noise
    Gradient skybox
    Short Tuts (3)
    DirectX using C# (54)
    DirectX using C++ (15)
    DirectX using VB (2)
    -- Expand all --


    Thank you!

    Support this site --
    any amount is welcome !

    Stay up-to-date

    I don't have the time to keep a News section, so stay informed about the updates by clicking on this RSS file!