Use managed DirectX to write games (4) use managed DirectX to create 3D Terrain

Source: Internet
Author: User
Tags float max

Author: Clayman

Use height map as input
First, what is a height map? A height chart is actually a two-dimensional array. Why do we need a height chart to create a terrain? In this case, the terrain is actually a series of grids with different heights, so that the index values of each element in the array can be used to locate unused grids (x, y ), the stored value is the grid height (z ). It is precisely because of this simple ing relationship that the most common terrain generation methods use height graphs as input data. In addition, in order to reduce the size of the array, the byte type is usually used to save the height value. Therefore, the lowest point in the terrain is represented by 0, and the highest point is represented by 255 (of course, in this case, some problems may occur. For example, the height difference of most areas in the terrain is not big, but the height difference in some areas is very large, but in most cases, this system works well ). Another advantage of using a 2D byte array is that the height map can be represented by a grayscale bitmap. For each pixel in the bitmap, the value 0 ~~ is also used ~~ Between 255 to indicate a gray scale. In this way, we can map different gray scales to heights and use pixel indexes to represent different grids.

How can we create a height chart? There are two ways: directly use a program to create a 2D array or use other drawing software to create a grayscale bitmap. Let's take a look at the advantages and disadvantages of the two methods. Directly create an array and fill in the values of each element through a specific algorithm (assigning only the immediate value to each element is not feasible, which will make your ground look extremely unreal, discontinuous height values may create distorted terrain .), You can create a terrain without any additional tools. However, the terrain created using this method is basically random. Although you can adjust the parameters of the algorithm to control the approximate terrain shape, you cannot precisely control whether each point should be concave or convex. By using grayscale images, you do not have to master complex terrain generation algorithms. You can render a three-dimensional terrain model as a grayscale image, or use a satellite-sampled image as a grayscale image. Our sample program will use the latter method, but first, let's take a look at the algorithm that uses the program to generate the terrain completely.

Use the midpoint displacement method to generate a height chart
Here we introduce a common and simple terrain generation algorithm called the midpoint displacement midpoint offset algorithm. Using this method, we first create a flat height chart, and then increase or decrease the number of grids to create a random terrain. To avoid generating a value without rules, we first divide the entire plane into four square areas, and then repeat the same division of the four squares. At the same time, adjust the height of each square vertex. As the level of sub-parts increases, the vertex height adjustment margin is reduced accordingly.

The floating point value between 0,255 and is used to make sure that the last 8-bit gray value is used to represent all the heights. Each step generates a random value within a definite range as the vertex offset value. For the first step, the random value will be between [-128,128] (for convenience, we will mark this random value range as [-delta, Delta] to generate, and the four vertices A, B, C, and D on the left are assigned. Next, use the dotted line to divide it into four small areas, which will create five new vertices. Calculate the average value of the height of two vertices on the edge of each new vertex as the reference value of this vertex (for example, use the height average of vertex A and vertex B as the reference value of vertex 1, the reference value of Point 5 is determined by the average values of the four vertices A, B, C, and D. Calculate a random value between [-delta, Delta] and offset the reference value as the final value of this point. After the values of the five vertices are calculated, I will adjust to the next stage and use the same method to calculate the vertex values, as shown on the right.

In order to guide the generation of terrain, Delta is multiplied by a scaling factor. We call this factor roughness, which is a 1 ~ Value between 0. In this way, the Delta value is reduced in each stage.

Delta = delta * Roughness

The larger the roughness value, the more obvious the terrain is. The smaller the roughness, the smoother the terrain.

Use Perlin Noise to generate a height chart
Any terrain algorithms without discussion of noise functions are incomplete. The most important noise function is Perlin Noise. He is almost a modern graphic software package that generates various flame, clouds, strange rocks, as well as the basis for many applications such as trees and marble surfaces. Perlin Noise's theory is not described in detail here. We mainly look at how to use it to add noise to our terrain.

Perlin Noise can be applied to any dimension space, but here we only discuss two-dimensional situations. In essence, 2D Perlin Noise is an interpolation of the normal state of each grid vertex. Let's take a closer look at this technology.

First, use a grid to divide the entire image into several different parts. As shown in, we use a 4x4 mesh to divide the entire image. Here, the number of grids controls the complexity of noise. The more the grid, the more intense the noise (TIGER), similar to the snowflake point displayed when the TV does not have a signal; the less the grid, the more obvious the noise waveform, similar to the effect of clouds.

We assign a random normal (normal vector) to each mesh vertex ). These rectangles are actually unit vectors pointing to different directions. Here, a common method is to create 256 vector Lookup tables pointing to different directions (forming a circle. Then a vector is randomly assigned to each grid, as shown in.

For each pixel in an image, we first find the grid units that contain it. Then, create four Direction Vectors pointing from the mesh vertex to the pixel to be calculated, as shown in. Now, each mesh vertex has two vectors: a random unit vector and a direction vector pointing to a pixel. Calculate the dot product of each pair of vectors and use it as the gradient height value (scalar height value) of each mesh vertex ). Next, the mixing of the four values determines the height of the calculated pixel. Here, different mixing methods can produce different effects. The most common method is to calculate the weight of the target pixel and each vertex position.

We will perform three hybrid operations. Calculate the hybrid weight first. Use the following formula:

W = 6 t ^ 5-15 t ^ 4 + 10 t ^ 3 (^ symbol represents a Power Operation)

W indicates the weight, and T is replaced with X or Y as needed. This method is different from the formula (W = 3 t ^ 2-2 t ^ 3) proposed by Perlin. It is relatively slow in computing, but the effect is much better.
First, calculate the weight in the X direction, and use the formula:

V = Ca (w) + CB (1-W)

The two vertices above the hybrid mesh. CA and CB are the gradient height values of the preceding two vertices respectively, and W is the weight value calculated from the previous formula. Then, use the same method to mix the following two vertices. Finally, use the first two mixed results and the weight in the Y direction to re-mix. Finally, the height calculated for this pixel is located between [0, 1], and we scale it to the corresponding gray value.

For example, if the coordinates of the two vertices on the top of the grid are Ca [] and CB [], the gradient height values are H0 and H1, respectively, and the pixel position is [], then the vector of the two vertices pointing to this pixel is:

Vector2 D0 (4-2, 2-0)
Vector2 D1 (4-8, 2-0 );

The weight in the X axis direction is:

SX = 6 * d0.x ^ 5-15d0. x ^ 4 + 10d0. x ^ 3

The corresponding interpolation is:

Avgx0 = H0 * SX + H1 (1-SX)

If the interpolation of the following two vertices is avgx1, the final interpolation is:

Result = avgx0 * SY + avg2 (1-sy)

In general, in order to obtain the real terrain, different grid granularities are selected to perform multiple Perlin Noise processing on the image, and the processed images are added together to obtain the final result.

Generate terrain
Now let's take a look at how to convert a height chart into a polygon mesh. As mentioned at the beginning, convert the x and y values of pixels in the height graph to the X and Y values of the vertex, and convert the color values of pixels to the vertex height. We can scale these values to the desired size.

Each 2x2 pixel corresponds to 2x2 vertices and can form 2 triangles at the same time. You can store the vertex data as a simple (x, y, z) list, and the triangle data is saved as a list of three index values. The two lists are then converted into vertex buffer and index buffer.

Public class terrain
{
Private device;
Private vertexbuffer Vb;
Private indexbuffer Ib;
Private int numvertices, numindices, numtriangles;
// Save the data extracted from the height chart
Float [,] heights;
// Terrain size
Private float terrainsize;

Public unsafe terrain (Device D, float min, float Max, float terrainsize)
{
Device = D;
// Load the height chart
Bitmap heightmap = new Bitmap (@ ".../../heightmap.bmp ");
// Create an Array Based on the bitmap size
Heights = new float [heightmap. Width, heightmap. Height];
// Lock data
Bitmapdata DATA = heightmap. lockbits (New rectangle (0, 0, heightmap. Width, heightmap. Height, imagelockmode. readonly, pixelformat. format24bpprgb );
// Obtain the address of the first pixel in the bitmap.
Byte * P = (byte *) data. scan0;
// Traverse the bitmap to obtain the gray value of the highest and lowest points
Byte lowest = 255;
Byte hightest = 0;
For (INT I = 0; I {
For (Int J = 0; j {
If (* P <lowest)
Lowest = * P;
If (* P> hightest)
Hightest = * P;
// Because each pixel is 24 bits and the pointer is 8 bits, + 3 points to the next Pixel
P + = 3;
}
}
// Fill the array. Max indicates the highest point of the terrain, and Min indicates the lowest point.
P = (byte *) data. scan0;
For (INT I = 0; I {
For (Int J = 0; j {
Heights [I, j] = (float) (* P-lowest)/(float) (hightest-lowest) * (max-min) + min;
P + = 3;
}
}
Heightmap. unlockbits (data );
// Calculate the vertex, index, and number of triangles
Numvertices = heightmap. Width * heightmap. height;
Numindices = 6 * (heightmap. Width-1) * (heightmap. Height-1 );
Numtriangles = 2 * (heightmap. Width-1) * (heightmap. Height-1 );
// Create a vertex Array
Vector3 [] Verts = new vector3 [numvertices];
Int [] Index = new int [numindices];
Int x = 0;
Int n = 0;
Float dx = terrainsize/(float) heightmap. height;
Float DY = terrainsize/(float) heightmap. width;
// Fill in the vertex Array
For (INT I = 0; I {
For (Int J = 0; j {
Verts [I * heightmap. width + J] = new vector3 (float) J * DX-terrainsize/2f, heights [J, I], (float) I * dy-terrainsize/2f );
}
}
// Fill in the Index Array
For (INT I = 0; I {
For (Int J = 0; j {
X = I * heightmap. Width + J;
Index [n ++] = X;
Index [n ++] = x + 1;
Index [n ++] = x + heightmap. Width + 1;
Index [n ++] = X;
Index [n ++] = x + heightmap. width;
Index [n ++] = x + heightmap. Width + 1;
}
}
// Set the vertex and index Buffer
VB = new vertexbuffer (typeof (vector3), numvertices, device, usage. None, vertexformats. Position, pool. Default );
VB. setdata (Verts, 0, 0 );
IB = new indexbuffer (typeof (INT), numindices, device, usage. None, pool. Default );
Ib. setdata (index, 0, 0 );
}

Public void drawterrain ()
{
Device. vertexformat = vertexformats. position;
Device. setstreamsource (0, VB, 0 );
Device. Indices = Ib;
Device. Transform. World = matrix. Translation (0, 0 );
Device. drawindexedprimitives (primitivetype. trianglelist, 0, 0, numvertices, 0, numtriangles );
}
}

Okay. Let's take a look at our work. In the source code, we use a bitmap as the height chart.

Of course, this is just a basic terrain technology. We didn't apply textures to the terrain, and the vertex didn't have the normal information, so we couldn't use the light to illuminate him, and we didn't do anything with the level of detail. Next time, we will discuss these issues carefully.

Download matching routine

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.