zoukankan      html  css  js  c++  java
  • 3D Terrain Visualisation in Managed DirectX 9 and C#(转载)

    In this project I demonstrate how to write a simple 3D rendering application in a relatively small amount of code.

    Introduction

    GIS (Geographical Information System) is a computer support system that represents data using maps. It helps people access, display and analyse data that has geographic content and meaning. For those not familiar with GIS, it used to be a niche IT market dominated by the traditional GIS and CAD companies such as Intergraph, Bentley, MapInfo, Autodesk and ESRI. Nowadays global IT giants such as Microsoft, Google and Oracle are competing for their share of the pie through products such as Virtual Earth, Google Earth and Oracle Spatial. NASA has also recently released a free, open source GIS viewer application called World Wind.

    In this article, I will demonstrate how to build a standalone 3D terrain visualisation tool from scratch using C# and Managed DirectX 9.0c. The application will allow the user to rotate the point of view using the arrow keys and to change the rendering mode to (P) Point, (W) Wire frame and (S) Solid.

    Background

    I recently completed a GIS system implementation for a local City Council. During that project I developed a proof of concept application to demonstrate the technical feasibility of 3D visualisation using the available spot heights and aerial photography textures. The aim of this article is to share my knowledge and experience with all developers interested in GIS and .NET.

    Requirements

    Before we start, I would like to specify the software requirements for this project:

    • Visual Studio .NET IDE (I used 2005 beta 2)
    • Managed DirectX 9.0c SDK (I used August 2005 update)
    • .NET framework (I used v2.0 but v1.1 will work as well)

    3D rendering concepts

    First of all I will need to explain the general 3D programming concepts behind my code. Unfortunately, entire books are written on this topic and I won't be able to give a full explanation for every single line in my code, but instead will attempt to present the most important ideas behind 3D visualisation.

    In order to generate any 3D terrain model, you will need some grid based data with X, Y and Z values for each grid point. A very important consideration is how the Z value is stored, as DirectX uses left-handed coordinate system while OpenGL uses right-handed coordinate system (to learn about different coordinate systems, please search the Internet). I have chosen a grid size of 79x88 simply because that is how my source data is stored, but you can change this to any arbitrary grid size. Likewise, my data uses 20m resolution which means that the real distance between two adjacent points is 20 meters.

    Point Mode

    Once you read in all the points you will need to generate a "mesh". The mesh is an array of triangles constructed from the points you loaded in the previous step. All rendering in 3D is based on triangles and arrays of triangles.

    Wireframe Mode

    Even the meanest video cards on today's market have limited rendering capability in terms of how many triangles can be drawn per second. Therefore, the less work your video card needs to perform the faster your application runs. This is where optimisation algorithms come into play, such as ROAM or PLOD (the latter is built-in DirectX 9). These and other similar algorithms are aimed at reducing the level of detail and number of triangles located furthest from the view point. The other way to look at this is to say that we are reducing the level of detail where it matters least, while we are preserving the highest possible level of detail where it matters most. We won't be using these algorithms here, however you should be aware of what they are and what they are used for.

    Finally, textures are used to provide a more realistic look of the scene. Textures use their own coordinate system, with top left representing 0,0 and bottom right representing 1,1. Any texture point within this range (0,0 - 1,1) is referred to as a "texel".

    Textured Mode

    As an exercise left to the reader, further enhancements could include the SkyBox, Lighting, Shading or even Physics engine with collision detection etc. Managed DirectX also includes support for DirectPlay and DirectSound with advanced networking and sound APIs. Using your imagination, the sky is the limit!

    Using the code

    I have built a sample WinForm application using the Visual Studio 2005 IDE. You will need to have Managed DirectX 9.0c SDK installed on your PC for the project to compile and run correctly (I used the August 2005 update). However you can use a much smaller DirectX 9.0c redistributable if you wish to distribute your code to users who don't have Managed DirectX SDK installed on their PC.

    OK, let's dive into the code.

    First of all, we will import the necessary libraries:

    Collapse | Copy Code
    using System;
        using System.Drawing;
        using System.Collections;
        using System.ComponentModel;
        using System.Windows.Forms;
        using System.Data;
        using System.IO;
        using Microsoft.DirectX;
        using Microsoft.DirectX.Direct3D;
        using Microsoft.DirectX.DirectInput;

    Then, we will declare our grid width and height, screen and keyboard devices, VertexBuffer and IndexBuffer, Texture, Vertex and Triangle structs and a few other variables used throughout the project:

    Collapse | Copy Code
    private int GRID_WIDTH = 79;     // grid width
        private int GRID_HEIGHT = 88;    // grid height
        private Microsoft.DirectX.Direct3D.Device device = null;  // device object
        private Microsoft.DirectX.DirectInput.Device keyb = null; // keyboard
        private float angleZ = 0f;       // POV Z
        private float angleX = 0f;       // POV X
        private float[,] heightData;     // array storing our height data
        private int[] indices;           // indices array
        private IndexBuffer ib = null;
        private VertexBuffer vb = null;
        private Texture tex = null;
        //Points (Vertices)
        public struct dVertex
        {
        public float x;
        public float y;
        public float z;
        }
        //Created Triangles, vv# are the vertex pointers
        public struct dTriangle
        {
        public long vv0;
        public long vv1;
        public long vv2;
        }
        private System.ComponentModel.Container components = null;

    Now we're ready to initialise our Direct3D device object:

    Collapse | Copy Code
    // define parameters for our Device object
        PresentParameters presentParams = new PresentParameters();
        presentParams.Windowed = true;
        presentParams.SwapEffect = SwapEffect.Discard;
        presentParams.EnableAutoDepthStencil = true;
        presentParams.AutoDepthStencilFormat = DepthFormat.D16;
        // declare the Device object
        device = new Microsoft.DirectX.Direct3D.Device(0,
        Microsoft.DirectX.Direct3D.DeviceType.Hardware, this,
        CreateFlags.SoftwareVertexProcessing, presentParams);
        device.RenderState.FillMode = FillMode.Solid;
        device.RenderState.CullMode = Cull.None;
        // Hook the device reset event
        device.DeviceReset += new EventHandler(this.OnDeviceReset);
        this.OnDeviceReset(device, null);
        this.Resize += new EventHandler(this.OnResize);

    As you can see we have wired up the OnDeviceReset event which fires up every time the user resizes the application window. Our points are stored in a VertexBuffer:

    Collapse | Copy Code
    // create VertexBuffer to store the points
        vb = new VertexBuffer(typeof(CustomVertex.PositionTextured),
        GRID_WIDTH * GRID_HEIGHT, device, Usage.Dynamic | Usage.WriteOnly,
        CustomVertex.PositionTextured.Format, Pool.Default);
        vb.Created += new EventHandler(this.OnVertexBufferCreate);
        OnVertexBufferCreate(vb, null);

    Then, we need to instantiate an IndexBuffer, from which our triangular mesh is constructed. IndexBuffer stores an ordered list into the Vertex data:

    Collapse | Copy Code
    ib = new IndexBuffer(typeof(int), (GRID_WIDTH - 1) *
        (GRID_HEIGHT - 1) * 6, device, Usage.WriteOnly, Pool.Default);
        ib.Created += new EventHandler(this.OnIndexBufferCreate);
        OnIndexBufferCreate(ib, null);

    Also pay attention to InitialiseIndices() and LoadHeightData() functions in the source code attached, where we load and "triangulate" our data. Next, we initialise the keyboard device:

    Collapse | Copy Code
    public void InitialiseKeyboard()
        {
        keyb = new Microsoft.DirectX.DirectInput.Device(SystemGuid.Keyboard);
        keyb.SetCooperativeLevel(this, CooperativeLevelFlags.Background |
        CooperativeLevelFlags.NonExclusive);
        keyb.Acquire();
        }

    Then, we position our camera:

    Collapse | Copy Code
    private void CameraPositioning()
        {
        device.Transform.Projection =
        Matrix.PerspectiveFovLH((float)Math.PI/4,
        this.Width/this.Height, 1f, 350f);
        device.Transform.View =
        Matrix.LookAtLH(new Vector3(0, -70, -35), new Vector3(0, -5, 0),
        new Vector3(0, 1, 0));
        device.RenderState.Lighting = false;
        device.RenderState.CullMode = Cull.None;
        }

    Almost done, only a few steps left. Now we will override the OnPaint event and provide our own event handling code:

    Collapse | Copy Code
    protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
        {
        device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, Color.LightBlue , 1.0f, 0);
        // set the camera position
          CameraPositioning();
        // draw the scene
          device.BeginScene();
        device.SetTexture(0, tex);
        device.VertexFormat = CustomVertex.PositionTextured.Format;
        device.SetStreamSource(0, vb, 0);
        device.Indices = ib;
        device.Transform.World =
        Matrix.Translation(-GRID_WIDTH/2, -GRID_HEIGHT/2, 0) *
        Matrix.RotationZ(angleZ)*Matrix.RotationX(angleX);
        device.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, GRID_WIDTH *
        GRID_HEIGHT, 0, indices.Length/3);
        device.EndScene();
        device.Present();
        this.Invalidate();
        ReadKeyboard();
        }

    Finally, we need to write our main procedure and we're done:

    Collapse | Copy Code
    static void Main()
        {
        using (WinForm directx_form = new WinForm())
        {
        directx_form.LoadHeightData();
        directx_form.InitialiseIndices();
        directx_form.InitialiseDevice();
        directx_form.InitialiseKeyboard();
        directx_form.CameraPositioning();
        directx_form.Show();
        Application.Run(directx_form);
        }
        }

    Now compile and run. You should get the results as shown in the pictures at the top. Use the arrow keys on your keyboard to drive the application, and press P, W and S to switch between different rendering modes: Point, Wire frame and Solid. Cool, huh?

    Points to note

    One thing you'll quickly learn to appreciate is how time consuming 3D programming can be. Even the smallest detail or effect you wish to implement could take many painful days; however, the entire experience is very rewarding once you overcome the hurdles. My suggestion to everyone is to use the Internet in the first instance and search about the problem you're trying to solve. The chances are, someone has already done what you're trying to do, and better still, the problem may have been documented and solved. If you're lucky, a step-by-step tutorial or code snippets may be readily available showing you how to solve your problem.

    References

    • For those people who have some spare cash in their wallets, there is an excellent book available for Managed DirectX programming in C#:
      • Managed DirectX 9 - Graphics and Game Programming, by Tom Miller.
    • Also, as a starting point there is an excellent DirectX 9 tutorial using C# available here.
    • Another great site with lots of practical examples in DX9 and C# is available here.

    History

    • 19th September 2005: First release.

    License

    This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

    A list of licenses authors might use can be found here

    转载自:http://www.codeproject.com/KB/directx/3D_Terrain_Visualisation.aspx

  • 相关阅读:
    语音信号端点检测
    WEBPACK & BABEL 打包项目
    使用 Qt 获取 UDP 数据并显示成图片(2)
    QSS为Qt程序添加不一样的样式
    window 搜索大文件
    Idea java 编译发生 cannot find symbol
    JetBeans Tab键相关设置
    JetBean Rider 重命名 c# 程序集名
    Unity 导入其他工程
    列出当前文件夹下的以log结尾的文件名
  • 原文地址:https://www.cnblogs.com/wuhenke/p/1855147.html
Copyright © 2011-2022 走看看