2D rendering in DirectX 8

Source: Internet
Author: User

Find another article on the Internet:

Http://bbs.studyget.com/showtopic-43122.html

Introduction]

 

Recently, I have seen many questions about DirectX8 abandoning DirectDraw in the latest API. Many people have returned to the previous DX7.1. I can understand why those who have a lot of development experience in DX7.1 do this, but there are many problems that come from beginners who have just learned DX and have not yet learned the previous APIs.

 

Body]

 

Background

 

Recently, I have seen many questions about DirectX8 abandoning DirectDraw in the latest API. Many people have returned to the previous DX7.1. I can understand why those who have a lot of development experience in DX7.1 do this, but there are many problems that come from beginners who have just learned DX and have not yet learned the previous APIs. People argue that many people do not have 3D hardware, so D3D is a wrong choice for DirectDraw. I don't believe it is true. In D3D 2D rendering, you only need to perform a vertex operation, and other things can be simplified to improve the filling rate. In short, using 2D hardware for 2D rendering in D3D can achieve the same performance as DirectDraw with a good filling rate. The advantage is that programmers can learn the latest API and get better performance in the updated hardware. This article will provide a framework for 2D rendering in DX8 to facilitate the transformation from DirectDraw to Direct3D. In each section, you will see something you don't like ("I Am a 2D programmer, I don't have to worry about vertices !"). However, please rest assured that as long as you implement this simple framework once, you will no longer consider that.

 

Start getting started

 

Assume that you already have the DX8 SDK, and there is a set of instructions on how to create a D3D device and place a rendering loop, so I don't want to spend any more time on it. Based on the intent of this article, I will talk about the Guide in the [DX8SDK] samplesMultimediaDirect3DTutorialsTut01_CreateDevice Directory, which you may have stored anywhere. In that example, I will add the following function:-after all other things are set up, this function is called by the app, you have created your device and everything has been initialized. If you follow the code in the Guide, you will see that WinMain is like this:
...
If (SUCCEEDED (InitD3D (hWnd )))
{
PostInitialize (2000000f, 2000000f); // This is my added line. The values
// 200366f were chosen based on the sizes
// Used in the call to CreateWindow.

 

ShowWindow (hWnd, SW_SHOWDEFAULT );
...

 

Void Render2D ()-this function will be called when you Render your scenario. The Render function in the guide now looks like this: VOID Render ()
{
If (NULL = g_pd3dDevice)
Return;

 

// Clear the backbuffer to a blue color
G_pd3dDevice-> Clear (0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB (0, 0, 255), 1.0f, 0 );

 

// Begin the scene
G_pd3dDevice-> BeginScene ();

 

Render2D (); // My added line...

 

// End the scene
G_pd3dDevice-> EndScene ();

 

// Present the backbuffer contents to the display
G_pd3dDevice-> Present (NULL, NULL );
}
Okay, this is our program shell. Let's fill in the content we have prepared...

 

### StudyGet_Info_Pagination_SIGN ###

 

Setting up for 2D drawing in d3d is used to set 2D painting in d3d.

 

Note: From here on we will talk about some annoying mathematical knowledge related to D3D. Don't be intimidated-if you want to, you can choose a majority of details. Most Direct3D plotting is controlled by three matrices: projection matrix, world matrix, and observation matrix. The first matrix we will talk about is the projection matrix. You can think that the projection matrix defines the lens attributes of your camera. In 3D applications, it defines things like the perspective method and so on. But we don't need perspective-we're talking about 2D !! So we only talk about orthogonal projection. To put it simply, let's draw 2D without considering the attributes attached to 3D Painting. To create an orthogonal projection matrix, we need to call the D3DXMatrixOrthoLH function, which will create a matrix for us. Other matrices (observation matrices and world matrices) define the camera location and the location of the world (or an object in the world. For 2D rendering, we do not need to move the camera or the world, so we will use a matrix of units to place the camera and the world in the default position. We can use the D3DXMatrixIdentity function to create the unit matrix. We need to add the following header file to use the D3DX function. # Include and add d3dx8dt. lib to the Connection Library list. After those settings, the PostInitialize function is now like this: void PostInitialize (float implements wwidth, float implements wheight)
{
D3DXMATRIX Ortho2D;
D3DXMATRIX Identity;

 

D3DXMatrixOrthoLH (& Ortho2D, ~wwidth, ~wheight, 0.0f, 1.0f );
D3DXMatrixIdentity (& Identity );

 

G_pd3dDevice-> SetTransform (D3DTS_PROJECTION, & Ortho2D );
G_pd3dDevice-> SetTransform (D3DTS_WORLD, & Identity );
G_pd3dDevice-> SetTransform (D3DTS_VIEW, & Identity );
} We are now setting for 2D plotting. We need to plot something. After this is set, our drawing area is from-runtime wwidth/2 to runtime wwidth/2 and from-runtime wheight/2 to runtime wheight/2. One thing to note is that in the Code, the width and height are specified in pixels. This allows us to consider everything with pixels, but we can also set the width and height to 1.0, and then allow us to specify the size with the percentage of screen space, this makes it easy to support multiple resolutions. Changing the matrix can support a variety of clever things, but for simplicity, we will talk about pixels now.

 

Setting up a 2D 'panel 'to set a 2D "Panel"

 

When I draw a 2D image, I have a class named CDX8Panel, which holds everything I need to draw a 2D rectangle. For simplicity, it eliminates the C ++ instructions and I have taken out the code. In any case, when we build code for one of our panels, you may see the value of a class, or, if you do not use C ++, the value of a higher-level API. Similarly, with the ID3DXSprite interface, we can be more leisurely. I will explain the most basic things here to demonstrate how things work, but if the Sprite interface is suitable for your needs, you can also use it. My panel definition is a simple 2D Texture rectangle that we will plot to the screen. Drawing a panel is very similar to a 2D blit operation. Experienced 2D programmers may think that a blit operation will have a lot of work, but these work has completed a lot of allowed special effects. First, we have to consider the geometric structure of our rectangle. This includes the idea of vertices. If you have 3D hardware, the hardware will process these vertices very quickly. If you only have 2D hardware, so few vertices we are talking about will soon be processed by the CPU. First, let's define our vertex format. Place the following code near # include: struct PANELVERTEX
{
FLOAT x, y, z;
DWORD color;
FLOAT u, v;
};

 

# Define D3DFVF_PANELVERTEX (D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_TEX1) the structure and flexible vertex format (FVF) define the vertex where we are talking about the position, color, and a set of texture coordinates. Now we need a vertex buffer. Add the following code line to the global list. To make it simple, I make it global-this is not an example of good coding habits.
LPDIRECT3DVERTEXBUFFER8 g_pVertices = NULL; now, add the following code line to the PostInitialize function (which is described below): float PanelWidth = 50366f;
Float PanelHeight = 100366f;

 

G_pd3dDevice-> CreateVertexBuffer (4 * sizeof (PANELVERTEX), D3DUSAGE_WRITEONLY, D3DFVF_PANELVERTEX, D3DPOOL_MANAGED, & g_pVertices );

 

PANELVERTEX * pVertices = NULL;
G_pVertices-> Lock (0, 4 * sizeof (PANELVERTEX), (BYTE **) & pVertices, 0 );

 

// Set all the colors to white
PVertices [0]. color = pVertices [1]. color = pVertices [2]. color = pVertices [3]. color = 0 xffffffff;

 

// Set positions and texture coordinates
Pvertices [0]. x = pvertices [3]. x =-panelwidth/2.0f;
Pvertices [1]. x = pvertices [2]. x = panelwidth/2.0f;

 

Pvertices [0]. Y = pvertices [1]. Y = panelheight/2.0f;
Pvertices [2]. Y = pvertices [3]. Y =-panelheight/2.0f;

 

Pvertices [0]. z = pvertices [1]. z = pvertices [2]. z = pvertices [3]. z = 1.0f;

 

Pvertices [1]. U = pvertices [2]. U = 1.0f;
Pvertices [0]. U = pvertices [3]. U = 0.0f;

 

Pvertices [0]. V = pvertices [1]. V = 0.0f;
Pvertices [2]. V = pvertices [3]. V = 1.0f;

 

G_pVertices-> Unlock (); this is actually easier than it looks. First, I constructed the Panel size and we will use them for some work. Next, I request the device to create a buffer that contains enough memory for four vertices defined in my format. Then I lock the buffer so that I can set the vertex value. It is worth noting that locking the buffer is very expensive, so I will only do this once. We can operate on these vertices without locking them, but we will discuss them later. In this example, I set four vertices centered on (0, 0. Remember this and there will be derivative discussions in the future. In addition, I have set texture coordinates. The SDK has been well described, so I did not discuss it. In short, we set it to draw the entire texture. Now we have set the rectangle. The next step is to plot it?

 

### StudyGet_Info_Pagination_SIGN ###

 

 Drawing the Panel drawing panel

 

It is easy to draw a rectangle. Add the following code line to your Render2D function: g_pd3dDevice-> SetVertexShader (D3DFVF_PANELVERTEX );
G_pd3dDevice-> SetStreamSource (0, g_pVertices, sizeof (PANELVERTEX ));
G_pd3dDevice-> DrawPrimitive (D3DPT_TRIANGLEFAN, 0, 2); these lines tell the device how vertices are formatted, used, and used. I chose to Plot these as triangle fans because they are more compact than drawing two triangles. Note that we have not processed any vertex format or vertex buffer. We can move the first row to our PostInitialize function. I put them here to emphasize that you have to tell the device what it is going to process. If you do not, it assumes that the vertex is in different formats and causes a crash. Then you can compile and run the Code. If everything is normal, you will see a black rectangle in the blue background. This is not correct because the color of the vertex is white. The problem is that the device allows light, which is not needed. Add this line to the PostInitialize function to turn off the light: g_pd3dDevice-> SetRenderState (D3DRS_LIGHTING, FALSE); now, re-compile, the device will use the vertex color. If you like, you can change the color of the vertex to see the effect. So far, everything has been going well, but a game that shows a White Rectangle looks boring and we haven't touched bits yet. Therefore, we have to add textures.

 

  Texturing the Panel pasting texture

 

Texture is a basic bitmap that can be loaded from a file or generated through data. For simplicity, we only use files. Add the following variables to your global variables: LPDIRECT3DTXTURE8 g_pTexture = NULL; this is the texture object we will use. Add this line of code to the PostInitialize function to load the texture from the file. D3DXCreateTextureFromFileEx (g_pd3dDevice, [Some Image File], 0, 0, 0, 0,
D3DFMT_A8R8G8B8, D3DPOOL_MANAGED, D3DX_DEFAULT,
D3dx_default, 0, null, null, & g_ptexture); you can replace [some image file] with the file name you choose. The d3dx function can load many files in standard format. The pixel format we use has an alpha channel, so we load a format file with an alpha channel, such as A. DSS file. In addition, I have omitted the colorkey parameter, but you can also specify a colorkey for transparency. I will go back and discuss a little bit about transparency. Now we have a texture and an image. Then we will tell the device to use it. Add the following code line to the beginning of the render2d function: g_pd3ddevice-> settexture (0, g_ptexture); this tells the device to use textures to render triangles. The special thing to remember here is that I did not add an error check for simplicity. You should perform a correct error check to determine that the texture has been actually loaded before it is used. One possible error is that in many hardware, the texture size must be a power of 2, such as 64x64,128x512. For the latest NVIDIA hardware, this constraint is no longer correct, but for the sake of security, use the power of 2. This restriction is annoying, so I will tell you how to bypass it later. Now, compile and run. You can see that your image has been mapped to a triangle.

 

  Texture coordinates

 

Note that the texture is stretched and shortened to fit the rectangle. You can adjust the coordinates of the texture. For example, if you change the line u = 1.0 to u = 0.5, only half of the texture is used, and the other half is not compressed. Therefore, if you have a 640xlarge image, you want to put it in a 640xlarge window, put a 0.625x0.9375 image in a x texture and specify the texture coordinates as and. You can use the remaining part of the texture to place the child images mapped to other panels (through the corresponding texture coordinates ). Generally, you want to optimize the texture usage because they consume image memory and move in the bus. This looks like a lot of work in blit, but many of them will be processed by new graphics cards optimized for 3D. In addition, it is not a bad idea to think more about how to move large blocks of memory in the system. But let me start my speech. Let's see how far we are going. First, we write a lot of code to bits a simple bitmap. However, I hope you can see some benefits and opportunities. For example, texture coordinates are automatically scaled to adapt to our geometric definition. This has done a lot of work for us, but considering it later. If we use a vertical matrix based on percentage ing and specify a panel occupying 1/4 of the bottom of the screen (let's say it's the UI ), in addition, we also use the correct texture coordinates to specify its texture. In this way, our UI will be automatically and correctly drawn under any selected window/screen size. (Not exactly cold fusion), but this is only one of the many examples. Now we have made the texture work very well. Let's look back and talk about transparency.

 

  Transparent

 

As I mentioned earlier, a simple method to add transparency is to specify a ColorKey value in the function of calling D3DXCreateTextureFromFileEX. Another method is to use an image with an alpha channel. No matter which method is used to specify transparency for the texture (using the alpha channel or ColorKey) and then run it, you will not see any difference. This is because alpha mixing is not allowed yet. Add these rows to PostInitialize to allow alpha mixing: g_pd3dDevice-> SetRenderState (D3DRS_ALPHABLENDENABLE, TRUE );
G_pd3dDevice-> SetRenderState (D3DRS_SRCBLEND, D3DBLEND_SRCALPHA );
G_pd3dDevice-> SetRenderState (D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA );
G_pd3dDevice-> SetTextureStageState (0, D3DTSS_ALPHAOP, D3DTOP_MODULATE); the first line allows mixing. The next two rows specify how the hybrid operation works. There are many possibilities, but this is the most basic type. The last line makes some settings so that when the alpha of the vertex color is changed, the texture value is scaled to weaken the entire panel. For more information about available settings, see SDK. Once these rows are added, you will see the correct transparency. Try changing the color of the vertex to see how it affects the panel.

 

Moving the Panel mobile Panel

 

Now our panel already has a lot of visual properties we need, but it just sticks to the center of our viewport. In the game, you can want something to move. One obvious way is to re-lock the vertices and change their positions. Never do this! Locking is very expensive. It includes data movement and is unnecessary. A better way is to specify the world transformation matrix to move these points. For many people, the matrix seems a little scary, but in D3DX there is a large number of functions that make it very easy to use the matrix. For example, to move the panel, add the following code at the beginning of the Render2D function: D3DXMATRIX Position;
D3DXMatrixTranslation (& Position, 50366f, 0.0f, 0.0f );
G_pd3dDevice-> SetTransform (D3DTS_WORLD, & Position); a matrix of 50 pixels that can be moved to the Panel in the X direction is created here, and then the device is notified of the movement. This can be encapsulated into a function like MoveTo (X, Y), but I didn't actually provide such code. As mentioned above, we need to remember that the vertex is defined relative to the origin. Because we did this, we moved the center of the Panel. If you think it is more suitable to move the upper left corner or other corners, change the vertex definition method. You can also create different coordinate systems by passing the correct parameters to the MoveTo function. For example, the current view is from-100 to 100. If I want to use the MoveTo function as I think it is from 0 to 200, I can simply subtract 100 from the X coordinate when I call D3DXMatrixTranslation for correction. There are many ways to change quickly so that you can see what you want, but as an experiment, this will provide a good foundation.

 

  Other matrix operations other matrix operations

 

There are many other matrix operations that can affect the panel. The most interesting thing is scaling and rotating. Some D3DX functions can be used to create these matrices. I will leave these labs for you, but here are some tips. The Z axis will be rotated on the screen. The rotation of the X and Y axes will look as if the Y and X axes are contracting. In addition, the method for applying multiple operations is to multiply and then send the result matrix to the device: D3DXMATRIX M = M1 * M2 * M3 * M4;
G_pd3dDevice-> SetTransform (D3DTS_WORLD, & M); however, remember that the result of matrix multiplication depends on the sequence of operands. For example, Rotation * Position moves the Panel and then rotates it. Position * Rotation will result in a row along the track. If you have arranged several matrices, but you have not expected results, please take a closer look at the order.

 

When you become more relaxed, you can try something like a texture matrix that allows you to move texture coordinates. You can also move the observation matrix to influence your coordinate system. Remember: locking is very expensive. Before you lock your vertex buffer, you always look at something like a matrix. Wrapping up and check all the Code listed here. We have gone a long way to perform blit, but many of them can be encapsulated into some small functions or classes, in this way, we can do it once and for all. Please note that this is represented by a very general method without optimization. There are many ways to package these methods to maximize the benefits. This may be the best way to create 2D applications on the current and future hardware, and you can also get the benefits of achieving those effects easily on the hardware. This method can also help you mix 2D elements in 3D, because they are the same in front of the matrix. These codes can also be simple and suitable for 2D work in OpenGL, so you can even write an abstract assembly to support two APIs. My hope is that this will allow people to use dx8 for 2D work. I may discuss more techniques and effects in future articles.

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.