Managed DirectX (Chapter 3)

xiaoxiao2021-03-06  44

Simple rendering technology

Translation: So far, our rendering work is very low. Every time you render the scene, you must allocate a list of new vertices and all things are stored in system memory. The modern graphics card integrates sufficient memory, putting the vertex data in memory to get a large new improvement: Store data in system memory, rendering each frame to copy to the graphics, which will bring great losses. This allocation is removed only to help us improve performance.

Use vertex buffer (Using Vertex Buffers)

Direct3D already contains this mechanism: Vertex Buffer. The vertex buffer, just like his name: a memory of the apex. The vertex buffer is perfectly realized to change the transform geometry in the shared scene. How to make us use the vertex buffer in the triangular program written in Chapter 1?

Creating a vertex buffer is equally simple, there are three constructor to complete this task, let's take a look at:

Public VertexBuffer (Device device, int sizeofbufferinbytes, usage usage, vertexformats vertexformat, pool pool);

Public VertexBuffer (Type TypevertEXTYPE, INT Numverts, Device Device, Usage Usage, VertexFormats VertexFormat, Pool Pool);

The following is the meaning of each parameter:

n Device - DEVICE used to create a vertex buffer, and the vertex buffer created can only be used by this Device;

n sizeofbufferinbytes - The vertex buffer size created, in bytes. The vertex buffer created with this parameter can be stored in any type of vertex;

n typevertextype - Use this parameter if the vertex buffer to create a type of vertex is stored. Its value can be the vertex structure type in the CustomvertEX class, or it can be a custom vertex type. And this value cannot be NULL;

n Numvert - After specifying the type of storage of the vertex buffer, you must also specify the maximum number of vertices of the buffer store. This value must be greater than 0;

n usage - Define how to use the vertex buffer. It will not be a member of all USAGE types, and only a few is the correct parameters:

DonotClip, Dynamic, Npatches, Points, Ptpatches, SoftwareProcessing, Writeonly

n vertexFormat - Defines the vertex format stored in the vertex buffer. If you create a universal buffer, use VertexFormat.none;

n pool - Positioned vertex buffer used memory pool location, you can specify several memory pool locations:

DEFAULT, Managed, SystemMemory, Scratch.

Observe the procedures in Chapter 1, move the triangular data to the vertex buffer should be easy. First, the declaration of the vertex buffer variable:

PRIVATE DEVICE DEVICE = NULL;

Private VertexBuffer VB = NULL;

Then add the code that creates a triangle:

Device = new (0, DeviceType.hardware, this.creatflags.softwrevertexproccessing, presentparams); customerTex.positionColored [] VERTS = New CustomvertEx. positioncolored [3];

VERTS [0] .SetPosition (New Vector3

0.0F

,

1.0f

,

1.0f

));

VERTS [0] .Color = system.drawing.color.aqua.toargb ();

Verts [1] `` `` `` `` `

Verts [2] `` `` `` `` `

VB = New Vertexbuffer (TypeOf (VustomvertEx.PositionColored), 2, Device, USAGE.Dynamic | usage.writeonly, CustomvertEx.PositionColored.Format, PLLO.DEFAULT;

Vb.setdata (VETS, 0, LOCKFLAGS.NONE);

The only change is to define two lines of code after the triangle. First, create a vertex buffer for saving three vertices. For performance, the created buffer is dynamic, read-only and is located in the default memory pool. Next, we put the top of the triangle into the buffer and use a simple setData method. This method receives any type of object as the first parameter, and the second parameter is a convenient amount of inexpensive data address in the vertex buffer. We intend to fill all the vertex buffers, so set to 0. The last parameter describes how to lock the buffer when writing data. We will discuss the latch mechanism later; now, don't care about how he is locked.

Now compiler, natural, get a compilation error: because DrawUserPrimities in the onpaint method need to get VERTS variables. There is a way to tell Direct3D, we want to draw the contents of the vertex buffer, not the array previously declared. Call the DEVICE SETSTREAMSource to read the vertex buffer when Direct3D draws. This method has the following two overloads:

Public Void SetStreamSource (int StreamNumber, VertexBuffer StreamData, int offsetinBytes, int Stride);

Public Void SetStreamSource (int StreamNumber, VertexBuffer StreamData, int offsetinbytes);

The difference between the two functions is that one of the two parameters indicating (data) flow rate size (Stride size of the stream). The first parameter is the number of streams used in this data. Now, set it to 0; we will discuss using multiple streams in the next chapter. The second parameter is a vertex buffer as a data source, and the third is the offset (in bytes) of the data that requires DirectX to be drawn by the vertex buffer. Stride is the size of each vertex in the buffer. This parameter is not required if the vertex buffer created with a particular type.

Now modify the method of drawing:

Device.SetStreamSource (0, VB, 0);

Device.drawPrimitives (PrimitiType.trianglelise, 0, 1);

As just described, we buffer the vertex as data stream 0, and set the offset to 0, and all data. It is worth noting that we also changed the function of the true drawing. Since all data is buffered in the vertex, there is no need to call the DrawUserPrimitives method. Because DrawUserPrimities is just user-defined data that is directly passed to it. More common DrawPrimities will draw geometry from the data stream source. DrawPrimitives has three parameters, the first one we have already discussed. The second representation of the starting top point in the stream, the last number of geometries you want to draw. Even this sample only draws a triangular sample brought 10% performance boost after using the vertex buffer (based on the screen update rate, the frame frequency frame rate). We will discuss performance and frame rate after a few later. Unfortunately, when you try to change the window size, the triangle will disappear immediately. (Note: The triangle is disappeared when the actual test is disappeared, but when the window is scaled into a certain ratio, the triangle will disappear)

There are several situations that will lead to this behavior, two of which we have already discussed. Recall the last chapter, we know that the device will automatically reset when changing the window size. However, when the resource created is at the default memory pool (such as a vertex buffer), the reset device will release the buffer. So when changing the window size, reset Device, release the vertex buffer. Managed DirectX has an excellent special purpose to reset the vertex buffer after resetting Device. However, this is no data in the vertex buffer, so there is no thing to draw.

We can capture a vertex buffer a event called "created", which will happen when rebuilding the vertex buffer, ready to fill the data. Now use this event to update our program, the modified code is as follows:

Private Void OnvertexBuffercreate (Object Sender, Eventargs E)

{

VertexBuffer Buffer = (VertexBuffer) Sender;

CustomvertEx.positionColored [] VERTS = New Customvertex. PositionColored [3];

VERTS [0] .SetPosition (New Vector3

0.0F

,

1.0f

,

1.0f

));

VERTS [0] .Color = system.drawing.color.aqua.toargb ();

Verts [1] `` `` `` `` `

Verts [2] `` `` `` `` `

Buffer.SetData (Verts, 0, Lockflags.none);

}

Subscribe to the event handler:

vb.created = new eventhandleer (this.onvertexbuffercreate);

OnvertexBuffercreate (VB, NULL);

This code buffers the event handler, and guarantees that the ONVERTEXBUFFERCREATE method is called in any case where the vertex buffer is created. Because the first time you create a vertex buffer, there is no subscription program, so you need to manually call once.

Ok, we have changed the original sample to an efficient program by using Video Memory and vertex buffering. Of course, it is still quite boring. So, let us create a box.

All geometry in the three-dimensional scene consists of a triangle, so how do you render a box or a cube? Well, each cube consists of six squares, and two triangles can form a square (huh, this is said that it seems that the mathematics of foreigners is really no way), we only need to get the 8 top of the cube. Yes. Add code: CustomvertEx.positionColored [] VERTS = New CustomvertEx.PositionColored [36];

// Front Face

VERTS [0] = New CustomvertEx.PositionColored (

-1.0F

,

1.0f

,

1.0f

Color.Red.Toargb ());

VERTS [2] = `` ``, Verts [3], VERTS [4], VERTS [5] = `` `` `` ``

// Back Face (Remember this is facing * away * from the camera, so vertices shopwise order)

VERTS [6] = New CustomvertEX.PositionColored (

-1.0F

,

1.0f

,

-1.0F

, Color.blue.toargb ());

VERTS [7], VERTS [8], VERTS [9], VERTS [10], VERTS [11] = `` `` `` `

(Note: See the source code in the attachment, pay attention to the order of the declaration)

As mentioned earlier, the box consists of 12 triangles, each triangle has three vertices, which make up a vertex collection. There are still a few places that need to be modified.

VB = New VertexBuffer (TypeOf (CustomvertEx.PositionColored), 36, Device, USAGE.Dynamic | usage.writeonly, CustomvertEx.positionColored.Format, pool.default;

Evice.Transform.World = Matrix.RotationYawpitchroll (Angle / (FLOAT) Math.pi, Angle / (Float) Math.pi *

2.0F

Angle / (float) Math.pi;

Device.drawPrimitives (PrimitiveType.trianglelist, 0, 12);

The biggest change here is to redefine the size of the vertex buffer. At the same time, we also changed the rotation angle of the box and let him turn more crazy. Finally, change the number of primitives to be rendered. In fact, since the box is completely three-dimensional, there is no need to see his back. Use the default culuch mode (counterclockwise) in Direct3D: Delete the row of the previously declared mode. Ok, now run the program.

Very can't afford, we have a color box that is crazy in the screen. But if you need to render a series of boxes, no one wants to declare a series of vertex buffers. There is a simple way to do this.

Now we have to draw three boxes with shoulders. Since the current camera settings make the first box to take the entire screen, we need to move his camera slightly:

Device.Transform.View = Matrix.lookatlh (New Vector3 (0, 0,

18.0F

), new vector3 (), new vector3 (0, 1, 0));

As you can see, we just have to move him to a little bit, you can see more scenes. In order to draw more boxes, we can use the existing vertex buffer again, just tell Direct3D to draw the same vertices again. Add code after Device.DrawPrimitives: device.transform.World = Matrix.RotationYawpitchroll (Angle / (FLOAT) Math.pi, Angle / (FLOAT) Math.pi /

2.0F

Angle / (float) math.pi *

4.0F

) * Matrix.translation

5.0f

,

0.0F

,

0.0F

);

Device.drawPrimitives (PrimitiveType.trianglelist, 0, 12);

Device.Transform.World = Matrix.RotationYawpitchroll (Angle / (Float) Math.pi, Angle / (FLOAT) Math.pi *

4.0F

Angle / (float) Math.pi /

2.0F

) * Matrix.translation

-5.0F

,

0.0F

,

0.0F

);

Device.drawPrimitives (PrimitiveType.trianglelist, 0, 12);

Ok, what do we have made this time? Since the VertExFormat property has been set when drawing the first box, Direct3D knows the vertex type that will be drawn. Similarly, it also knows where to get data. So what do you need to know about the second box Direct3D? It is only necessary to draw the position and what you can draw.

Setting World Transform to "move" to the world coordinate "World Space, what is used as a transform matrix? First, use a method similar to the setupcamera function; do a little change, let the box rotate in different angles. However, the other half in World Transform is new: multiplied a Matrix.Translation with the existing rotation matrix. The transform matrix can move one of the space to another. Our transform matrix moves the second box to move 5 units, and the third box moves 5 units to the right.

It should be noted that the cumulative effects obtained by the two transformation matrices are determined by the order of multiplying matrices. Here, our first rotating the box and moves again. If you move first, then the result will have a big difference. Remember that the order in transform is important.

Add texture for objects

Although it is very interesting to use color and lights, it is not true to use this technique. "Texture" is often used to describe the roughness of the object (Roughness of an Object) in a non-three-dimensional program. The texture of the three-dimensional scene is a 2D bitmap used to simulate the texture of geometry. Direct3D can render 8-layer texture for each primitive, but now we only solve the situation of each map original texture. Because Direct3D uses a normal bitmap as its texture format, any loaded bitmap can be used as a texture object. How to map 2D textures to 3D objects? Each object drawn into the scene has a texture coordinate that maps each TeXel to the screen specific location when rasterization. Textl is an abbreviation of Texture Element, or represents a specific color value of each Address in the texture. Address can imagine a number representing the rows and columns, called U, V coordinates, respectively. In general, these values ​​are scalar, with value range from 0.0 to 1.0. (0, 0) indicates the upper left corner of the texture, (1, 1) means that the coordinate of the center is (0.5, 0.5) in the lower right corner. In order to use texture to render the box, you must change the vertex format of the box and the data passed to the graphics card. Use texture coordinates to replace "Color" elements in the vertex data. Although it is effective while using color and texture, when it is exercised, we only use textures to define the color of the primitive. Modify code:

CustomvertEX.PositionTextured [] VERTS = New Microsoft.directx.direct3d.customvertEX.PositionTextured [36];

VERTS [0] = New CustomvertEx.PositionTextured (

-1.0F

,

1.0f

,

1.0f

,

0.0F

,

0.0F

);

Vert [1] `` `` `` `` `` `()

Obviously, the biggest change is the data type of the stock set. The last two FLOAT values ​​in each vertex store the texture U, V value used to render the diversity. It should be a square for each surface and texture of the box, so it is possible to map the texture directly to each face. Note that the left upper corner map texture of the element is mapped to (1, 1) Textl in the lower right corner. At the same time, we must also modify places where creating a vertex buffer:

VB = New VertexBuffer (TypeOf (CustomvertEx.PositionTexture), 36, Device, USAGE.Dynamic | usage.writeonly, CustomVertex.positionTexTured.Format, pool.default;

There are so many duplicate code, now let us use a simple way to draw the box and add a function to complete this task.

Private Void Drawbox (Float Yaw, Float Potch, Float Roll, Float X, Float Y, Float Z, Texture T)

{

Ngle =

0.01F

;

Device.Transform.World = Matrix.RotationYawpitchroll (YAW, Pitch, Roll) * Matrix.Translation (X, Y, Z);

Device.Settexture (0, t);

Device.drawPrimitives (PrimitiveType.triangLELIST, 0, 12);

The first six parameters are the same as we used, and the last new parameter represents the texture used when rendering. We also called which texture used when the SetTexture tells Direct3D rendering. Its first parameter is this texture "Stage". I still remember that I have previously rendered 8 layer textures for a primitive, this parameter is the index of these textures. Because there is only one texture, we use the first index, 0. At the same time, we should notice that we have modified Angle variables and World Transform, you can delete the same lines like SetupCamera.

Before calling a new method rendering, you should first declare the texture to be used, and a resource file containing three textures is included in the source code. Puck.BMP, GROUND.BMP, BANANA.BMP, respectively. Add the following code:

Private texture

TEX

= NULL;

PRIVATE TEXTURE TEX1 = NULL;

Private texture tex2 = NULL;

This is the three textures we are about to use. However, it is also necessary to "assemble" three bitmaps as resource embedded. Add the following code after creating a vertex buffer:

TEX

= New TEXTURE (Device, New Bitmap (this.gettype (), "puck.bmp"), 0, pool.managed;

TEX1 = · ····· ()

Texture's constructor accepts four parameters. The first is a device for rendering texture. All resources in the scene (texture, vertex buffer, etc.) have to contact DEVICE. The next parameter Bitmap is where we get textured data. The third parameter USAGE has previously discussed it. The last parameter is the memory pool location of the texture. Convenience, now use the hosted memory pool. TEXTURE Other constructor includes:

(Note: This is a slightly one in DX

9C

The constructor that has not existed)

Public Texture (Device Device, Int Width, Int Height, Int Numlevels, Usage Usage, Format Format, Pool Pool;

Public Texture (Device Device, Stream Data, USAGE, POOL)

The first method allows us to create a texture from "blank" to specify its height, width, and detail. The last one is very similar to us, but uses the flow instead of bitmap objects. Of course, the data in the stream should be converted to a bitmap. There are also some interesting methods of loading bitmaps in the TextureLoad class, we will discuss next.

Ok, now defined and loaded, it is time to update the drawing code, use the following code to replace the previous drawing code;

DrawBox (Angle / (Float) Math.pi, Angle / (Float) Math.pi *

2.0F

Angle / (float) Math.pi /

4.0F

,

0.0F

,

0.0F

,

0.0F

,

TEX

);

DrawBox (Angle / (Float) Math.pi, Angle / (Float) Math.pi /

2.0F

Angle / (float) math.pi *

4.0F

,

5.0f

,

0.0F

,

0.0F

, TEX1);

DrawBox (angle / (float) Math.pi, Angle / (FLOAT) Math.pi * 4.0F

Angle / (float) Math.pi /

2.0F

,

-5.0F

,

0.0F

,

0.0F

, TEX2);

转载请注明原文地址:https://www.9cbs.com/read-51251.html

New Post(0)