XNA for C#
DirectX 9 for C#
DirectX 9 for C++
DirectX 9 for VB
Forum
   
My XNA Book
      
       Go to section on this site

Additional info


Latest Forum posts

 Account settings
  Posted by: Anonymous
  When: 07/05/2014 at 09:48:39

 forced subtitle
  Posted by: Applefly
  When: 07/05/2014 at 06:00:48

 convert DVD into PMS
  Posted by: Applefly
  When: 07/05/2014 at 05:55:25

 DVD to Digital Copy easily
  Posted by: VIKIVannessa
  When: 05/05/2014 at 06:52:29

 DVD on Xbox 360/Xbox One Console
  Posted by: VIKIVannessa
  When: 05/05/2014 at 06:51:47

 Extract .Srt Subtitles
  Posted by: Applefly
  When: 04/05/2014 at 03:54:38

 Encode Movie collection
  Posted by: Applefly
  When: 04/05/2014 at 03:52:41

 Convert DVD to WMV
  Posted by: Applefly
  When: 29/04/2014 at 05:53:50

 rip DVDs into digital files
  Posted by: Applefly
  When: 29/04/2014 at 05:51:20

 iTunes movies/music to Kindle Fire
  Posted by: ciciyu80
  When: 29/04/2014 at 05:10:20


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 - 2011 Riemer Grootjans
  • Translations

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

    Microsoft MVP Award



    2007 - 2011 MVP Award
    DirectX - XNA

    Contents

    News
    Home
    Forum
    XNA 2.0 Recipes Book (8)
    XNA 3.0 Recipes Book (8)
    Downloads
    Extra Reading (3)
    Matrices: geometrical
    Matrix Mathematics
    Homogenous matrices
    Community Projects (1)
    Tutorials (160)
    XNA 4.0 using C# (89)
    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!