XNA for C#
DirectX 9 for C#
DirectX 9 for C++
DirectX 9 for VB
Forum
   August 27: 2D-7: Writing text
My Book: Out Now!
      
       Go to section on this site

Additional info


Latest Forum posts

 DX vs XNA vs OGL
  Posted by: Rich_Zap
  When: 28/08/2008 at 10:54:39

 understanding of SetUpIndices
  Posted by: YoYoFreakCJ
  When: 28/08/2008 at 05:51:04

 billboard does not rotate after scaling
  Posted by: kigunda
  When: 28/08/2008 at 04:55:29

 exception error
  Posted by: Rich_Zap
  When: 28/08/2008 at 03:34:29

 exception error
  Posted by: besi
  When: 28/08/2008 at 00:14:11

 Having trouble with effect file
  Posted by: Anonymous
  When: 27/08/2008 at 22:15:15

 Polygon Clipping for Octree
  Posted by: lbmurali
  When: 27/08/2008 at 18:13:55

 understanding of SetUpIndices
  Posted by: vToMy
  When: 27/08/2008 at 18:05:45

 A Typo with loading the Font
  Posted by: riemer
  When: 27/08/2008 at 15:26:43

 mistake found
  Posted by: riemer
  When: 27/08/2008 at 15:22:31


Ads

Adding some color and the Z-Buffer

You might already have a rotating terrain, but it definitely would be better looking filled with some colors instead of just plain white lines. One idea to do this, is to use natural colors, like the ones that we find in the mountains. At the bottom we have blue lakes, then the green trees, the brown mountain and finally snow topped peaks. To keep this tutorial general for every image, you can’t expect every image to have a lake at height 0, and a mountain peak at height 255 (the maximum value for a .bmp pixel). Imagine an image with height values only between 50 and 200, this image would then probably produce a terrain without any lakes or snow topped peaks.
 
To stay general, we first have to detect the minimum and maximum heights in our image. We will store these in the following global variables, which we initialize with the opposite values:

  private int MinimumHeight = 255;
 private int MaximumHeight = 0;

 When you load your image, you are now going to check if the current pixel’s height is below the current MinimumHeight or above the current MaximumHeight :

 heightData[WIDTH-1-y,HEIGHT-1-i] = height;
 if (height < MinimumHeight)
 {
     MinimumHeight = height;
 }
 if (height > MaximumHeight)
 {
     MaximumHeight = height;
 }

 With these variables filled, you can specify the 4 regions of your colors:



 Now when you declare your vertices and their colors in the VertexDeclaration method, you are going to define the desired colors to the correct height regions as follows:

 vertices[x+y*WIDTH].Position = new Vector3(x, y, heightData[x,y]);
 if (heightData[x,y] < MinimumHeight + (MaximumHeight - MinimumHeight)/4)
 {
     vertices[x+y*WIDTH].Color = Color.Blue.ToArgb();
 }
 else if (heightData[x,y] < MinimumHeight + (MaximumHeight - MinimumHeight)*2/4)
 {
     vertices[x+y*WIDTH].Color = Color.Green.ToArgb();
 }
 else if (heightData[x,y] < MinimumHeight + (MaximumHeight - MinimumHeight)*3/4)
 {
     vertices[x+y*WIDTH].Color = Color.Brown.ToArgb();
 }
 else
 {
     vertices[x+y*WIDTH].Color = Color.White.ToArgb();
 }

When your run this code, you will indeed see a nicely colored network of lines. When we want to see the whole colored terrain, we just have to remove this line :

 device.RenderState.FillMode = FillMode.WireFrame;

When you execute this, take a few moments to rotate the terrain a couple of times. Pay special attention to the middle area. You will see that sometimes the middle peaks get overdrawn by the ‘invisible’ lake behind it. This is simple because we have not yet defined a ‘Z-buffer’! This is nothing more than an array where your video card keeps track of the depth coordinate of every pixel that should be drawn on your screen (so in our case, a 500x500 matrix!). Every time your card receives a triangle to draw, it checks whether the triangle’s pixels are closer to the screen than the pixels already present in the Z-buffer. If they are closer, the Z-buffer’s contents is updated with these pixels for that region.
 
Of course, this whole process if fully automated. All you have to do is to define the Z-buffer when you create your device. To do this, add these 2 lines to your PresentParameters :

 presentParams.AutoDepthStencilFormat = DepthFormat.D16;
 presentParams.EnableAutoDepthStencil = true;

 Here you create a Z-buffer with a precision of 16 bits. What this means in short: Every distance is presented between 0 and 1, with 0 being the near plane (1f in our case) and 1 being the far plane (250f in our case). With 16 bits, you have 2^16 = 65536 possible distances between them. Most cards support 32 bits, but my guess is 16bit precision will be more than enough for our application. The last line activates the previously defined Z-buffer. Run this code.
 
Not as expected, indeed. What could be wrong? We didn’t initialize our buffer. Imagine the whole buffer filled with zeroes. When we would draw a triangle, it would be further away from the viewer than what was previously defined in the Z-buffer, so our triangle is discarded. So in fact, we have to first fill our buffer with ones. To do this automatically every update of our screen, start with it in the device.Clear method :

 device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, Color.Black , 1.0f, 0);

 (The | is a bitwise OR operator, in this case it means both have to be cleared) Now you’ll see the terrain rotating as expected!




DirectX Tutorial 11 - Colored vertices

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:
  • normalize
          Hi all! for (int y = 0; y < WIDTH; y++) ...
  • Displaying Colour Issue
          Hi, I am just querying something strange. I have b...
  • Continue using .raw
          Hi... I was wondering how i coulde continue usi...
  • Very Slow
          Hi, My system taks about a second or two to do ...
  • System.OutOfMemoryException
          when the program goes to the line " heightData...



    Althought now we’ve got some colors in our terrain, it doesn’t really look any better than it did last chapter.. What we need at this moment are shadows. No shadows without lights, so let’s move to the next chapter!

    The code for this chapter:

     using System;
     using System.Drawing;
     using System.Collections;
     using System.ComponentModel;
     using System.Windows.Forms;
     using System.Data;
     using Microsoft.DirectX;
     using Microsoft.DirectX.Direct3D;
     using System.IO;
     using Microsoft.DirectX.DirectInput;
     
     namespace DirectX_Tutorial
     {
         public class WinForm : System.Windows.Forms.Form
         {
             private int WIDTH = 64;
             private int HEIGHT = 64;
             private Microsoft.DirectX.Direct3D.Device device;
             private System.ComponentModel.Container components = null;
             private float angle = 0f;
             private CustomVertex.PositionColored[] vertices;
             private int[,] heightData;
             private int[] indices;
             private IndexBuffer ib;
             private VertexBuffer vb;
             private Microsoft.DirectX.DirectInput.Device keyb;
             private int MinimumHeight = 255;
             private int MaximumHeight = 0;
     
             public WinForm()
             {
                 InitializeComponent();
                 this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.Opaque, true);
             }
     
             public void InitializeDevice()
             {
                 PresentParameters presentParams = new PresentParameters();
                 presentParams.Windowed = true;
                 presentParams.SwapEffect = SwapEffect.Discard;
                 presentParams.AutoDepthStencilFormat = DepthFormat.D16;
                 presentParams.EnableAutoDepthStencil = true;
                 device = new Microsoft.DirectX.Direct3D.Device(0, Microsoft.DirectX.Direct3D.DeviceType.Hardware, this, CreateFlags.SoftwareVertexProcessing, presentParams);
             }
     
             public void InitializeKeyboard()
             {
                 keyb = new Microsoft.DirectX.DirectInput.Device(SystemGuid.Keyboard);
                 keyb.SetCooperativeLevel(this, CooperativeLevelFlags.Background | CooperativeLevelFlags.NonExclusive);
                 keyb.Acquire();
             }
     
             private void CameraPositioning()
             {
                 device.Transform.Projection = Matrix.PerspectiveFovLH((float)Math.PI / 4, this.Width / this.Height, 1f, 250f);
                 device.Transform.View = Matrix.LookAtLH(new Vector3(80, 0, 120), new Vector3(-20, 0, 0), new Vector3(0, 0, 1));
                 device.RenderState.Lighting = false;
                 device.RenderState.CullMode = Cull.None;
             }
     
             private void VertexDeclaration()
             {
                 vb = new VertexBuffer(typeof(CustomVertex.PositionColored), WIDTH*HEIGHT, device, Usage.Dynamic | Usage.WriteOnly, CustomVertex.PositionColored.Format, Pool.Default);
                 vertices = new CustomVertex.PositionColored[WIDTH*HEIGHT];

                for (int x=0;x< WIDTH;x++)            {

                    for (int y=0; y< HEIGHT;y++)                {

                         vertices[x + y * WIDTH].Position = new Vector3(x, y, heightData[x,y]);
                         if (heightData[x, y] < MinimumHeight + (MaximumHeight - MinimumHeight) / 4)
                         {
                             vertices[x + y * WIDTH].Color = Color.Blue.ToArgb();
                         }
                         else if (heightData[x, y] < MinimumHeight + (MaximumHeight - MinimumHeight) * 2 / 4)
                         {
                             vertices[x + y * WIDTH].Color = Color.Green.ToArgb();
                         }
                             else if (heightData[x, y] < MinimumHeight + (MaximumHeight - MinimumHeight) * 3 / 4)
                         {
                             vertices[x + y * WIDTH].Color = Color.Brown.ToArgb();
                         }
                         else
                         {
                             vertices[x + y * WIDTH].Color = Color.White.ToArgb();
                         }
                     }
                 }
     
                 vb.SetData(vertices, 0 ,LockFlags.None);
             }
     
             private void IndicesDeclaration()
             {
                 ib = new IndexBuffer(typeof(int), (WIDTH-1)*(HEIGHT-1)*6, device, Usage.WriteOnly, Pool.Default);
                 indices = new int[(WIDTH-1)*(HEIGHT-1)*6];

                for (int x=0;x< WIDTH-1;x++)            {

                    for (int y=0; y< HEIGHT-1;y++)                {
                        indices[(x+y*(WIDTH-1))*6] = (x+1)+(y+1)*WIDTH;
                        indices[(x+y*(WIDTH-1))*6+1] = (x+1)+y*WIDTH;
                        indices[(x+y*(WIDTH-1))*6+2] = x+y*WIDTH;

                        indices[(x+y*(WIDTH-1))*6+3] = (x+1)+(y+1)*WIDTH;
                        indices[(x+y*(WIDTH-1))*6+4] = x+y*WIDTH;
                        indices[(x+y*(WIDTH-1))*6+5] = x+(y+1)*WIDTH;
                    }
                }
                ib.SetData(indices, 0, LockFlags.None);
            }

            protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
            {
                device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, Color.Black, 1.0f, 0);

                device.BeginScene();
                device.VertexFormat = CustomVertex.PositionColored.Format;
                device.SetStreamSource(0, vb, 0);
                device.Indices = ib;

                device.Transform.World = Matrix.Translation(-HEIGHT/2, -WIDTH/2, 0)*Matrix.RotationZ(angle) ;
                device.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, WIDTH*HEIGHT, 0, indices.Length/3);
                device.EndScene();

                device.Present();

                this.Invalidate();

                ReadKeyboard();
            }

            private void ReadKeyboard()
            {
                KeyboardState keys = keyb.GetCurrentKeyboardState();
                if (keys[Key.Delete])
                {
                    angle+=0.03f;
                }
                if (keys[Key.Next])
                {
                    angle-=0.03f;
                }
            }

            private void LoadHeightData()
            {
                int offset;
                byte dummy;

                FileStream fs = new FileStream("heightmap.bmp", FileMode.Open, FileAccess.Read);
                BinaryReader r = new BinaryReader(fs);

                for (int i = 0; i < 10; i++)
                {
                    dummy = r.ReadByte();
                }

                offset = r.ReadByte();
                offset += r.ReadByte() * 256;
                offset += r.ReadByte() * 256 * 256;
                offset += r.ReadByte() * 256 * 256 * 256;

                for (int i = 0; i < 4; i++)
                {
                    dummy = r.ReadByte();
                }

                WIDTH = r.ReadByte();
                WIDTH += r.ReadByte() * 256;
                WIDTH += r.ReadByte() * 256 * 256;
                WIDTH += r.ReadByte() * 256 * 256 * 256;

                HEIGHT = r.ReadByte();
                HEIGHT += r.ReadByte() * 256;
                HEIGHT += r.ReadByte() * 256 * 256;
                HEIGHT += r.ReadByte() * 256 * 256 * 256;

                heightData = new int[WIDTH, HEIGHT];
                for (int i = 0; i < (offset - 26); i++)
                {
                    dummy = r.ReadByte();
                }

                for (int i = 0; i < HEIGHT; i++)
                {
                    for (int y = 0; y < WIDTH; y++)
                    {

                        int height = (int)(r.ReadByte());
                        height += (int)(r.ReadByte());
                        height += (int)(r.ReadByte());
                        height /= 8;
                        heightData[WIDTH - 1 - y, HEIGHT - 1 - i] = height;

     
                         if (height < MinimumHeight)
                         {
                             MinimumHeight = height;
                         }
                         if (height > MaximumHeight)
                         {
                             MaximumHeight = height;
                         }
                     }
                 }
             }
     
             protected override void Dispose (bool disposing)
             {
                 if (disposing)
                 {
                     if (components != null)
                     {
                         components.Dispose();
                     }
                 }
                 base.Dispose(disposing);
             }
     
             private void InitializeComponent()
             {
                 this.components = new System.ComponentModel.Container();
                 this.Size = new System.Drawing.Size(500,500);
                 this.Text = "DirectX Tutorial";
             }
     
             static void Main()
             {
                 using (WinForm our_directx_form = new WinForm())
                 {
                     our_directx_form.LoadHeightData();
                     our_directx_form.InitializeDevice();
                     our_directx_form.CameraPositioning();
                     our_directx_form.VertexDeclaration();
                     our_directx_form.IndicesDeclaration();
                     our_directx_form.InitializeKeyboard();
                     our_directx_form.Show();
                     Application.Run(our_directx_form);
                 }
             }
         }
     }


    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 (145)
    XNA 2.0 using C# (74)
    DirectX using C# (54)
    Series 1:Terrain (14)
    Opening a window
    Linking to the Device
    Drawing a triangle
    Camera
    Rotation - Translation
    Indices
    Terrain creation
    Terrain from file
    DirectInput
    Importing bmp files
    Colored vertices
    DirectX Light basics
    Mesh creation
    Mesh lighting
    Series 2: Flightsim (19)
    Series 3: HLSL (19)
    Short Tuts (2)
    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!