I believe that everyone has played or seen some brilliant 3D games, and all of them know that the characters and scenes are made using various 3D software, such as 3 DSMAX and Maya. How are these works used in programs? The well-known direct3d is used to do this. This article briefly introduces how to use 3D works through d3d. Don't worry about not accessing d3d. I will talk about how to create a 3D device in the simplest way.
Download source code
Note: In addition to directx9.0sdk, we also need a conv3ds converter in the d3d plug-in toolkit. A 3D object in d3d-a 3D object that contains vertex information, materials, textures, and animations is called a mesh object. Conv3ds is used to convert 3D files into standard mesh object files (*. X.
To get down to the truth, you must first have a decent 3D file.
Open your 3 DSMAX (or other 3D software such as Maya to export files that can be used by d3d, and other plug-ins are also required. I will not talk about it here ), make full use of your talents to create any model you want. However, the ugly saying goes before, friends familiar with 3D production know that exquisite 3D works rely on a large number of different types of textures, but d3d only recognizes diffuse (surface color) one channel. Although it also supports bump (concave and convex) specular color (highlighted color) and other items, it cannot be completed directly using 3D software, and additional code is required. This is why the rendering speed of the game is much faster than that of 3D files (of course there are other reasons) and why the quality of the game is not good enough. Well, try to do it well. Note that the length and width of the texture must be a power pixel of 2, which is in the same directory as your program. The author made a flying flag as an example and saved it as "C:/frontfree.3ds", and used a "C:/larglog.bmp" and a "C: /lakerrem2.jpg.
Use export or export Selecte in the menu file to export the selected object to the *. 3DS file. Then run conv3ds in the command line mode to convert it to *. X. In this way, a usable 3D file is available. Conv3ds has many useful parameters. Readers can find its self-report file in the package. Here we only introduce one-X (lower case ). Default *. X is binary. In this way, the text format is generated *. x: conv3ds-x *. x, open it with utraledit, and you will see how mesh objects are made up. Of course, such files are larger.
It's time to write the program. In general, the d3d program uses the following header files: d3dx9. H, d3d9. H, d3dtypes. H, the corresponding library files, and a winmm. Lib.
No matter what you want to do, creating a window, the main event loop, and so on are always the first and unchanged Windows programs. The following code completes these tasks:
Lresult callback winproc (hwnd, uint MSG, wparam, lparam)
{
Switch (MSG)
{
Case wm_destroy:
//... One is called here to release all interface pointers.
Postquitmessage (0 );
Destroywindow (hwnd );
Break;
Default:
Break;
}
Return (defwindowproc (hwnd, MSG, wparam, lparam ));
}
Int winapi winmain (hinstance, hinstance hprevinstance, lpstr lpcmdline, int nshowcmd)
{
// Create a main window:
Wndclassex mainwnd;
Mainwnd. cbsize = sizeof (wndclassex );
Mainwnd. cbclsextra = 0;
Mainwnd. cbwndextra = 0;
Mainwnd. hbrbackground = NULL; // This will be a non-Background
Mainwnd. hcursor = NULL; // The mouse is an hourglass.
Mainwnd. hicon = NULL; // No icon
Mainwnd. hiconsm = NULL;
Mainwnd. hinstance = hinstance;
Mainwnd. lpfnwndproc = winproc; // No title bar
Mainwnd. lpszclassname = "Main Window ";
Mainwnd. lpszmenuname = NULL; // no menu
Mainwnd. Style = cs_owndc; // window
Registerclassex (& mainwnd );
Hwnd = createmediawex (null, "Main Window", "only a demo ",
Ws_popup | ws_visible, 0, 0,800,600, null, null, hinstance, null );
// Initialize the d3d device and object here
// Init_d3d ();
// Main event loop:
MSG;
Zeromemory (& MSG, sizeof (MSG ));
While (msg. message! = Wm_quit)
{
// Proc message:
If (peekmessage (& MSG, hwnd, 0, 0, pm_remove ))
{
Translatemessage (& MSG );
Dispatchmessage (& MSG );
}
Else
{
// The rendering of each frame is here
// Render ();
}
}
// Unregiste the window:
Unregisterclass ("Main Window", mainwnd. hinstance );
Return (msg. wparam );
} // End winmain
Direct3d is an extremely important component of DirectX. It provides a complete set of methods for setting scenes, operating 3D objects, and rendering images. Let's review the general process of the d3d program: First, you have to have an object to be rendered, and you have to give its vertices, textures, and materials. Then, convert its coordinates (3D) to screen coordinates, add some light information, and call a rendering method, what it does is calculate the brightness based on the position of each vertex (already the screen coordinate), and fill in the texture with the light and material information, so that you look like a three-dimensional thing. The rendering method function caches the image in the backup buffer, so don't forget to change it to the front buffer. You must have a dirext3d interface as the basis for creating everything. Like this:
Lpdirect3d9 g_lpd3d9; // global
G_lpd3d9 = direct3dcreate9 (d3d_sdk_version); // The parameter represents your d3d SDK version. It must be written in this way
Then you can use it to create a direct3d device interface. All other methods, such as creating objects, lights, and rendering, must be called through this device interface pointer:
Lpdirect3ddevice9 g_lpd3ddevice; // global
Hresult initd3d (hwnd)
{
D3dpresent_parameters m_d3dpp;
// We want to read the information of the current adapter (video card) into this structure, which will be used to create a device
D3ddisplaymode m_d3ddm;
G_lpd3d9-> getadapterdisplaymode (d3dadapter_default, & m_d3ddm );
// D3dadapter_default indicates the default video card
Zeromemory (& m_d3dpp, sizeof (m_d3dpp ));
M_d3dpp.mongowed = false; // to create a full screen program, set this item to false.
M_d3dpp.backbufferwidth = m_d3ddm.width;
M_d3dpp.backbufferheight = m_d3ddm.height;
M_d3dpp.backbufferformat = m_d3ddm.format; // This item records the color depth.
M_d3dpp.swapeffect = d3dswapstmt_discard;
M_d3dpp.enableautodepthstencel = true; // enables d3d to manage deep buffering.
M_d3dpp.autodepthstencilformat = d3dfmt_d16; // set the depth buffer to 16 bits.
M_d3dpp.hdevicewindow = hwnd;
// Create the device:
G_lpd3d9-> createdevice (d3dadapter_default, // The adapter identifier. The default
D3ddevtype_hal, // Hardware Abstraction Layer
Hwnd, // This is the handle of the current window. Just use the returned value of createmediawex ().
D3dcreate_hardware_vertexprocessing, // vertex processing type. Hardware processing is used here.
& M_d3dpp, // describe the device's struct
& G_lpd3ddevice); // The Last pointer to the device to be created.
// Enable the Z buffer
G_lpd3ddevice-> setrenderstate (d3drs_zenable, true );
Return s_ OK;
}
The Z buffer is a type of deep buffer. The depth buffer is an area of the same size as the screen, recording how close the "nearest pixel" of each point on the screen is ". For example, a program wants to draw a point in a triangle, But it finds that the distance recorded in the depth buffer is closer than this point, that is, a point of other objects is in front of it, naturally, it will not be painted; otherwise, it will be painted, and then the depth of the point is written into the buffer. Now we open the Z-buffer, which is to hand over these tasks to the system for processing. Adjusting the number of buffered data digits is a trade-off between precise data and large space.
It's time to use the thing you just finished. In d3d, we can say that the shape and position of all objects are composed of vertices. The material determines how the objects reflect light. The texture is self-evident. The first thing any 3D program needs to do is to create a set of such information. As I said earlier, all this information is contained in ready-made mesh objects. What we need to do is extract it from the file. The idirect3dmesh interface in d3d provides these methods.
Lpd3dxmesh g_lpmesh; // global
Fixed Point Information of mesh objects is stored in g_lpmesh, and materials and textures must be extracted separately. In d3d, the material and texture are described on the d3dmaterial9 struct and id3dtexture9 interface respectively. In general, mesh contains a variety of textures and materials, we need to declare it as an array.
D3dmaterial9 * g_meshmtrl;
Lpdirect3dtexture9 * g_meshtex;
The size of the array is unknown beforehand, depending on the number of sebsets of mesh objects. A set of triangles with the same texture attributes in a mesh object is called a subset. Because the conversion of the current material and texture is very time-consuming, in d3d, it is rendered according to one subset group and the other, thus avoiding frequent changes of the material texture. We use a global variable g_meshmtrlnum of the dwod type to record the number of subsets in the mesh.
Hresult initgeometry ()
{
Lpd3dxbuffer lpmeshmtrlbuffer; // temporary buffer of the texture map
// Load a mesh object from a file:
D3dxloadmeshfromx ("C: //", // file path
D3dxmesh_systemmem, // g_lpmesh location where it is created, where it is created in system memory rather than Display memory
G_lpd3ddevice, null, & lpmeshmtrlbuffer, null,
& G_meshmtrlnum, // a variable pointer used to store the number of subsets
& G_lpmesh); // The mesh pointer to be created.
// We need to read all the materials and Texture Information at one time and store them here:
D3dxmaterial * pd3dxmtrl = (d3dxmaterial *) lpmeshmtrlbuffer-> getbufferpointer ();
// Further subdivided:
G_meshmtrl = new d3dmaterial9 [g_meshmtrlnum];
G_meshtex = new lpdirect3dtexture9 [g_meshmtrlnum];
// For each subset, we extract its material and texture respectively.
For (DWORD I = 0; I <g_meshmtrlnum; I ++)
{
G_meshmtrl [I] = pd3dxmtrl [I]. matd3d;
// In fact, pd3dxmtrl [I]. ptexturefilename is only a file name
Tchar texname [512]; // stores the complete path of the texture File
Strcpy (texname, "C ://");
Strcat (texname, pd3dxmtrl [I]. ptexturefilename );
D3dxcreatetexturefromfile (g_lpd3ddevice, texname, & g_meshtex [I]);
}
Lpmeshmtrlbuffer-> release ();
Lpmeshmtrlbuffer = NULL;
Return s_ OK;
}
Open the *. X file (in text mode) and you will see that the texture is only recorded in the file. The parameters required by createtexturefromfile () are a complete path. Therefore, if these graphs are not in the same directory of the program, you must process them in the code. Otherwise, I am sure you cannot see anything. Here, I simply use the absolute path.
Well, all the information is extracted from the file, but it is not the rendering time. No coordinate conversion is performed. Currently, all vertices use their local 3D coordinates, rather than the screen coordinates that can be directly used by rendering functions. The following three types of coordinate transformations are usually required:
Global conversion converts all objects to uniform global coordinates. You can also perform various operations on Object positions here.
Convert the view to the global coordinates seen from the observer (player) perspective. First, a camera is mounted at a specified position in the global coordinate system, specifying a watching point and a reference point. The coordinates are converted to the coordinate system in which the camera is taken as the origin, from the origin to the watching point as the Z axis, plus the reference point, the determined plane is the Y-Z plane. Actually, we screwed up the global coordinates.
There is no essential difference between the two coordinate transformations before perspective conversion. In some cases, they are combined into one. After perspective conversion, the X and Y values of each vertex represent the actual screen coordinates, and the Z value is the depth information used in Z-buffer.
Any basic coordinate coordinates (translation, rotation, and contraction) are multiplied by a 4x4 matrix by the point (a column vector) in a 3D program. The three Transformations above are actually a series of basic transformations. You do not need to fill in the matrix by yourself. d3d will do this. Then you need to call the corresponding three functions to tell d3d: You need to use this matrix to complete the global conversion, and then use that matrix to complete the view conversion. The Code is as follows:
Hresult setmatrix ()
{
D3dxmatrix matworld;
// The following code is used to turn an object:
Uint itime = timegettime () % 10000;
Float fangle = itime * (2.0f * d3dx_pi)/interval between F;
D3dxmatrixrotationx (& matworld, fangle); // fill matworld into a rotating matrix, and fangle is the Rotation Angle
G_lpd3ddevice-> settransform (d3dts_world, & matworld); // set matworld to the world conversion matrix.
D3dxmatrix matview;
D3dxvector3 d3dveye (0,100,-500); // the player's position, which is described by a d3dxvector3
D3dxvector3 d3dvla (0, 0); // Viewpoint
D3dxvector3 d3dvup (0, 0); // reference point
D3dxmatrixlookatlh (& matview, & d3dveye, & d3dvla, & d3dvup); // fill the matview Matrix
G_lpd3ddevice-> settransform (d3dts_view, & matview); // set it to an attempt to convert the Matrix
D3dxmatrix matproj; d3dxmatrixperspectivefovlh (& matproj,
D3dx_pi/4, // The angle of view, usually 45 degrees
Float (1280)/float (1024), // This is the aspect ratio of the view.
1.0f, 1_1_f); // these two numbers are the closest and farthest visible ranges.
G_lpd3ddevice-> settransform (d3dts_projection, & matproj); // you can specify the pivoting conversion matrix.
Return s_ OK;
}
It can be rendered, but it is best to put two lights first. In d3d, the lamp is represented by the d3dlight9 struct. Users familiar with 3D production can easily guess the members of these structures. I set a white primary light and a yellow secondary light. Note that the light will not rotate with the object. The Global conversion is only for the vertex of the object.
Hresult setlight ()
{
D3dlight9 light_r;
Zeromemory (& light_r, sizeof (light_r ));
Light_r.type = d3dlight_directional;
// D3d supports three types of light sources: bulb, spotlight, and parallel light.
Light_r.diffuse.r = light_r.diffuse.g = light_r.diffuse. B = 1;
Light_r.specular.r = light_r.specular.g = light_r.specular. B = 1;
Light_r.direction = d3dxvector3 (0.5f,-0.5f, 0.5f); // direction of parallel light
G_lpd3ddevice-> setlight (0, & light_r );
// You can set a lot of lights. The first parameter is the index number of the lamp you want to set, and the second parameter is to describe the structure of the lamp.
Lpd3ddevice-> lightenable (0, true );
// Then let the light work, as if to turn it on
D3dlight9 light_ B;
Zeromemory (& light_ B, sizeof (light_ B ));
Light_ B .type = d3dlight_directional;
Light_ B .diffuse.r = 0.5;
Light_ B .diffuse.g = 1;
Light_ B .direction = d3dxvector3 (-0.5f,-0.5f, 0.5f );
G_lpd3ddevice-> setlight (1, & light_ B );
G_lpd3ddevice-> lightenable (1, true );
G_lpd3ddevice-> setrenderstate (d3drs_lighting, true );
Return s_ OK;
}
In d3d, the highlights of an object do not depend on the surface color. Therefore, you can set the surface color and the height color respectively. The same is true for lighting. I set the surface color and high color of the first light to white, and the second light does not participate in the highlight operation, it is like ambient light. The color of light is set through RGB. The range of each color is 0-1.
The final rendering is actually very simple. First, you need to call clear () to clear the screen and the depth buffer, draw all the sebsets to the backup buffer, and then call present () to switch to the front buffer, then it can appear on the display.
Hresult render ()
{
G_lpd3ddevice-> clear (0, null, d3dclear_target | d3dclear_zbuffer,
// The surface to be rendered. Here is the target image and depth buffer.
0x00000000, // The color value used to clear the screen. Black is used here.
1, 0 );
G_lpd3ddevice-> settexturestagestate (0, d3dtss_colodrop, d3dtop_modulate );
G_lpd3ddevice-> beginscene (); // you must call this method before drawing a graph.
Setmatrix ();
For (DWORD I = 0; I <g_meshmtrlnum; I ++ ){
G_lpd3ddevice-> setmaterial (& g_meshmtrl [I]); // The parameter is the required material.
G_lpd3ddevice-> settexture (0,
// D3d supports multiple pasters. this parameter is the number of pasters to be set. Only the first pasters are used here.
G_meshtex [I]);
G_lpmesh-> drawsubset (I); // This method is used to start drawing. The parameter is the subobject serial number of the mesh.
}
G_lpd3ddevice-> endscene (); // This corresponds to beginscene ()
G_lpd3ddevice-> present (null, null); // flip the page. Generally, all four parameters are null.
Return 0;
}
This is just a simple example. The code is very simple, there is no error verification, and no interface pointer is released (the reader must think about release, or your program will not really end ), if no user operation is performed, ALT + F4 is disabled. The results are not complex:
This example is just intended to tell readers some basic practices. Due to the limited level of the author, many problems have not been resolved. You are welcome to share your experiences with me.
This article from the csdn blog, reproduced please indicate the source: http://blog.csdn.net/xu_haiqing/archive/2006/04/18/667878.aspx