Use MFC to implement OpenGL programming for mice who love shopping
1. Introduction to OpenGL
As we all know, OpenGL was originally used to develop high-quality image interfaces on their graphics workstation. But in recent years it has become an excellent open 3D graphic interface. In fact, it is an interface of graphics software and hardware. It includes more than 120 graphic functions. "Gl" is the abbreviation of "graphic library", meaning "graphics library ". The emergence of OpenGL enables most programmers to develop complex 3D images in C language on PCs. Microsoft has provided three OpenGL function libraries (glu32.lib, Glau. lib, opengl32.lib), which allows us to easily program and quickly generate beautiful and beautiful images. For example, the flower baskets and the maze in the screen saver in Windows NT are impressive.
Ii. Basic Steps and conditions for generating OpenGL programsThis article will give an example. This example is a Windows program that uses OpenGL to display images. Through this program, we can also understand the basic requirements of OpenGL programming. We know that GDI is drawn by device context ("DC"), while OpenGL needs to draw the environment (rendering context "). Each GDI Command needs to be passed to a DC. Unlike GDI, OpenGL uses the current rendering environment (RC ). Once a current RC is specified in a thread, all OpenGL commands in this thread use the same current RC. Although multiple RC can be used in a Single Window, there is only one current RC in a single thread. In this example, an OpenGL RC is generated and converted into the current RC, which consists of three steps: Setting the window pixel format, generating the RC, and setting it to the current RC.
1. Create a project first
Use Appwizard to generate an EXE file, select the project directory, and enter "OpenGL" in the project name to keep the other files unchanged. Step 1. Choose menu document (SDI ); step 2: do not support databases; Step 3: do not support OLE; Step 4: do not select the check boxes for floating toolbar, start status bar, print and preview support, and help support, select 3D control (3D controls). Step 5: Generate source file comments and use MFC as the shared dynamic library. Step 6: retain the default selection. Finish the project, as shown in figure 1.
2. Add the OpenGL files and libraries required for this project to the project.
In the project menu, select "Settings" under "build. Click the "Link" label, select the "General" directory, and enter "opengl32.lib glu32.lib Glaux. lib "(note that the content in double quotation marks is entered, and each database is separated by spaces; otherwise, a link error will occur), select" OK "to end. Then open the file "stdafx. H ", insert the following statement into the file (the underlined statement is the added statement): # define vc_extralean // exclude rarely-used stuff from Windows headers # include <afxwin. h> // MFC core and standard components # include <afxext. h> // MFC extensions
# Include <Gl/Gl. h> // header file of the opengl32 Library
# Include <Gl/Glu. h> // header file of the glu32 Library
# Include <Gl/Glaux. h> // header file of the Glaux Library# Ifndef _ afx_no_afxcmn_support # include <afxcmn. h> // MFC support for Windows 95 common controls # endif // _ afx_no_afxcmn_support
// You can also use the following method to connect to the Lib
# Pragma comment (Lib, "opengl32.lib") // opengl32 Connection Library
# Pragma comment (Lib, "glu32.lib") // glu32 Connection Library
# Pragma comment (Lib, "Glaux. lib") // Glaux Connection Library
3. Rewrite the onprecreate function and add member functions and member variables to the class.
OpenGL requires ws_clipchildren (Windows style used to create the parent window, used to crop the area covered by the Child Window during re-painting) and ws_clipsiblings (Windows style used to create the Child Window, used to crop the area covered by other subwindows during repainting. Rewrite onprecreate as follows:
Bool copenglview: precreatewindow (createstruct & CS ){
CS. Style | = (ws_clipchildren | ws_clipsiblings );Return cview: precreatewindow (CS );}
The first step to generate an RC is to define the pixel format of the window. The pixel format determines how the displayed image is displayed in the memory. Parameters controlled by the pixel format include: color depth, buffer mode, and supported painting interfaces. These parameters are set below. Add a protected member function bool setwindowpixelformat (HDC) to the copenglview class and edit the code.
Bool copenglview: setwindowpixelformat (HDC)
{
Pixelformatdescriptor pixeldesc;
Pixeldesc. nsize = sizeof (pixelformatdescriptor );
Pixeldesc. nversion = 1;
Pixeldesc. dwflags = pfd_draw_to_window |
Pfd_draw_to_bitmap |
Pfd_support_opengl |
Pfd_support_gdi |
Pfd_stereo_dontcare;
Pixeldesc. ipixeltype = pfd_type_rgba;
Pixeldesc. ccolorbits = 32;
Pixeldesc. credbits = 8;
Pixeldesc. credshift = 16;
Pixeldesc. cgreenbits = 8;
Pixeldesc. cgreenshift = 8;
Pixeldesc. cbluebits = 8;
Pixeldesc. cblueshift = 0;
Pixeldesc. calphabits = 0;
Pixeldesc. calphashift = 0;
Pixeldesc. caccumbits = 64;
Pixeldesc. caccumredbits = 16;
Pixeldesc. caccumgreenbits = 16;
Pixeldesc. caccumbluebits = 16;
Pixeldesc. caccumalphabits = 0;
Pixeldesc. cdepthbits = 32;
Pixeldesc. cstencilbits = 8;
Pixeldesc. cauxbuffers = 0;
Pixeldesc. ilayertype = pfd_main_plane;
Pixeldesc. breserved = 0;
Pixeldesc. dwlayermask = 0;
Pixeldesc. dwvisiblemask = 0;
Pixeldesc. dwdamagemask = 0;
// Right-click to add a protected member variable to copenglview:Int m_glpixelindex;
M_glpixelindex = choosepixelformat (HDC, & pixeldesc );
If (m_glpixelindex = 0) // Let's choose a default index.
{
M_glpixelindex = 1;
If (describepixelformat (HDC, m_glpixelindex,
Sizeof (pixelformatdescriptor), & pixeldesc) = 0)
{
Return false;
}
}
If (setpixelformat (HDC, m_glpixelindex, & pixeldesc) = false)
{
Return false;
}
Return true;
}
4. Use classwizard to add the message processing function oncreate of wm_create
The oncreate function is added as follows.
Int copenglview: oncreate (maid)
{
If (cview: oncreate (lpcreatestruct) =-1)
Return-1;
// Todo: add your specialized creation code here
Hwnd = getsafehwnd ();
HDC =: getdc (hwnd );
If (setwindowpixelformat (HDC) = false)
Return 0;
If (createviewglcontext (HDC) = false)
Return 0;
Return 0;
} Now, the basic framework of the OpenGL project has been built. However, if you are running this project now, it looks similar to the common MFC program.
5. Code explanationNow let's take a look at the several pixel formats provided by describe-pixelformat and explain the code: pixelformatdescriptor includes all the information that defines the pixel format. Dwflags defines devices and interfaces compatible with Pixel formats. Generally, OpenGL releases do not include all flags ). Wflags can receive the following flags: pfd_draw_to_window enables drawing in the window or other device windows; pfd_draw_to_bitmap enables drawing in the memory; pfd_support_gdi enables calling the GDI function (Note: If pfd_doublebuffer is specified, this option will be invalid); pfd_support_opengl enables it to call OpenGL functions; pfd_generic_format if this pixel format is supported by the Windows GDI function library or by a third-party hardware device driver, you need to specify this item; pfd_need_palette tells the buffer zone whether a palette is required. This program assumes that the color is a 24 or 32-bit color and does not overwrite the palette. The pfd_need_system_palette flag indicates whether the buffer zone regards the system palette as part of its own palette; pfd_doublebuffer indicates that the dual-buffer is used (note: Buffer window); pfd_stereo indicates whether the Left and Right buffers are organized by stereo images. Pixeltype defines the display color. Pfd_type_rgba indicates that each bit group represents the values of the red, green, and blue components. Pfd_type_colorindex indicates that each group represents the index value in the color search table. This example uses the pfd_type_rgba method. ● Ccolorbits defines the number of digits of a specified color. For rgba, the number of digits is the number of digits in the red, green, and blue components of the color. For the color index value, it refers to the number of colors in the table. ● Credbits, cgreenbits, cblue-bits, and calphabits are used to indicate the digits used by each corresponding component. ● Credshift, cgreenshift, cblue-shift, and calphashift are used to indicate the digits occupied by the offset of each component from the color. Once our structure is initialized, we want to know the most similar system pixel format. We can do this: m_hglpixelindex = choosepixelformat (HDC, & pixeldesc); choosepixelformat accepts two parameters: HDC and pixeldesc; this function returns the index value in this pixel format. If 0 is returned, it indicates that the request fails. If the function fails, we only set the index value to 1 and use describepixelformat to get the pixel format description. If you apply for an unsupported pixel format, the choose-pixelformat will return the closest value to the pixel format you requested. Once we get an index value in pixel format and the corresponding description, we can call setpixelformat to set the pixel format and set it only once. Now the pixel format has been set. Our next step is to generate the rendering environment (RC) and make it the current drawing environment. Add a protected member function bool createviewglcontext (HDC) to copenglview to make it as follows:Bool copenglview: createviewglcontext (HDC){M_hglcontext = wglcreate context (HDC); // use the current DC to generate a drawing environment (RC)If (m_hglcontext = NULL){Return false;}If (wglmakecurrent (HDC, m_hglcontext) = false){Return false;}Return true;}Add a protected member variable hglrc m_hglcontext; hglrc is a handle pointing to rendering context. Call this function in the oncreate function: int copenglview: oncreate (maid) {If (cview: oncreate (maid truct) =-1) Return-1;Hwnd = getsafehwnd ();HDC =: getdc (hwnd );If (setwindowpixelformat (HDC) = false)Return 0;If (createviewglcontext (HDC) = false)Return 0;Return 0;} Add the message processing function ondestroy () of wm_destroy to make it as follows: void copenglview: ondestroy (){If (wglgetcurrentcontext ()! = NULL){// Make the rendering context not currentWglmakecurrent (null, null );}If (m_hglcontext! = NULL){Wgldeletecontext (m_hglcontext );M_hglcontext = NULL;}// Now the associated DC can be released. cview: ondestroy ();} Finally, edit the constructor of copenglview to make it as follows: Cgltutor1view: cgltutor1view (){M_hglcontext = NULL;M_glpixelindex = 0;} So far, we have constructed a framework so that the program can draw Images Using OpenGL. You may have noticed that an RC is generated at the beginning of the program and is used from the beginning to the end. This is different from most GDI programs. In the GDI program, DC is generated only when needed, and is released immediately after painting. In fact, RC can do the same, but remember that it takes a lot of processing time to generate an RC. Therefore, to obtain high-performance and smooth images and graphics, it is best to generate only RC once and always use it until the program ends. Createviewglcontex generates RC and converts it into the current RC. Wglcreatecontext returns an RC handle. Before calling createviewglcontex, you must use setwindowpixelformat (HDC) to set the device-related pixel format. Wglmakecurrent sets RC to the current RC. The DC passed in this function is not necessarily the DC where you generate the RC, but the device context and pixel formats of the two must be consistent. If another RC already exists before wglmakeforcurrent is called, wglmakeforcurrent will overwrite the old RC and set the new RC to the current RC. In addition, you can use wglmakecurrent (null, null) to eliminate the current RC. We need to delete the rendering environment in ondestroy. Before deleting the RC, you must determine that it is not the current handle. We use wglgetcurrentcontext to check whether a current drawing environment exists. If so, use wglmakecurrent (null, null) to remove it. Then you can use wgldelete-context to delete the RC. In this case, it is safe to allow the class to delete the DC. Note: Generally, a single-threaded program is used, and the generated RC is the current RC of the thread. You do not need to pay attention to this. However, if a multi-threaded program is used, we need to pay special attention to this point. Otherwise, unexpected consequences may occur.Iii. InstancesThe following is an example of a simple two-dimensional image (this example is based on the above settings ).Use classwizard to add the onsize function of wmsize to copenglview, as shown below.Void copenglview: onsize (uint ntype, int CX, int CY){Cview: onsize (ntype, CX, CY );Glsizei width, height;Gldouble aspect;Width = Cx;Height = Cy;If (Cy = 0)Aspect = (gldouble) width;ElseAspect = (gldouble) width/(gldouble) height; Glviewport (0, 0, width, height );Glmatrixmode (gl_projection );Glloadidentity ();Gluortho2d (0.0, 500.0 * aspect, 0.0, 500.0 );Glmatrixmode (gl_modelview );Glloadidentity ();} Use classwizard to add the onpaint message processing function wm_paint to copenglview, as shown below.
Void copenglview: onpaint (){Cpaintdc DC (this); // device context for painting (Added by classwizard)Glloadidentity ();Glclear (gl_color_buffer_bit );Glbegin (gl_polygon );Glcolor4f (1.0f, 0.0f, 0.0f, 1.0f );Glvertex2f (1000000f, 500000f );Glcolor4f (0.0f, 1.0f, 0.0f, 1.0f );Glvertex2f (00000000f, 4000000f );Glcolor4f (0.0f, 0.0f, 1.0f, 1.0f );Glvertex2f (00000000f, 500000f );Glend ();Glflush ();}
The running result of this program is a colorful triangle in the black background (2 ). Here you can see that it is very easy to draw a graph using OpenGL. Only a few simple statements are required to implement powerful functions. If you scale the window, the triangle scales accordingly. This is because onsize defines the coordinates of the views and views through glviewport (0, 0, width, height. The first and second parameters of glviewport are the pixel coordinates in the lower left corner of the view, and the third and fourth parameters are the width and height of the view. The glmatrixmode in onsize is used to set the matrix mode. It has three options: gl_modelview, gl_projection, and gl_texture. Gl_modelview indicates that the object coordinate system is switched to the human eye coordinate system. Gl_projection indicates switching from the human eye coordinate system to the cropping coordinate system. Gl_texture indicates the transformation from the coordinate system defining the texture to the coordinate system pasting the texture. Glloadidentity initializes the Project Matrix. gluortho2d sets the Project Matrix to display a two-dimensional Cartesian display area. Here we need to talk about the naming principles of OpenGL commands. Most OpenGL commands start with "Gl. Some of them begin with "Glu" and come from OpenGL utility. Most "Gl" commands define the variable type in the name and perform corresponding operations. For example, glvertex2f defines a vertex. The parameter variables are two floating point numbers, representing the X and Y coordinates of the vertex respectively. Similar to glvertex2d, glvertex2f, glvertex3i, glvertex3s, glvertex2sv, glvertex3dv ...... And other functions. So how to draw a triangle? We first call glcolor4f (1.0f, 0.0f, 0.0f, 1.0f) and specify the red, green, and blue components as 1, 0, and 0 respectively. Then we use glvertex2f (1000000f, 500000f) to define a vertex at (100,50. In turn, we define green points at (450,400) and Blue Points. Then we end the triangle with glend. But the triangle hasn't been drawn yet. These commands are still in the buffer until you call the glflush function, and glflush triggers the execution of these commands. OpenGL automatically changes the color values between triangle vertices to make them colorful. You can also use glbegin to generate new images. Glbegin (glenum mode) parameters include gl_points, gl_lines, metrics, gl_line_loop, gl_triangles, metrics, metrics, gl_quads, metrics, and gl_polygon. Valid functions between glbegin and glend include glvertex, glcolor, glindex, glnormal, gltexcoord, glevalcoord, glevalpoint, glmaterial, gledgeflagIv. OpenGL programming Summary1. If you want to respond to the wm_size message, you must set the view and matrix mode. 2. Try to complete all your drawing work in response to the wm_paint message. 3. It takes a lot of CPU time to generate a rendering environment, so it is best to generate it only once in the program until the program ends. 4. Try to encapsulate your drawing commands in the document class so that you can use the same document in different visual classes to save your programming workload. 5. glbegin and glend must appear in pairs, which is a drawing statement for the elements. Glpushmatrix () and glpopmatrix () must also appear in pairs. Glpushmatrix () copies the current matrix to the stack. When glpopmatrix is called, the matrix pushed into the stack is restored to the current matrix. You can use glpushmatrix () to precisely Save the current matrix and restore it with glpopmatrix. In this way, we can use this technology to place other objects relative to an object. For example, the following statement uses only one matrix to generate two rectangles and place them at a certain angle. Glpushmatrix (); gltranslated (m_transx, m_transy, 0); glrotated (m_angle1, 0, 0, 1); glpushmatrix (); gltranslated (90, 0, 0 ); glrotated (m_angle2, 0, 0, 1); glcolor4f (0.0f, 1.0f, 0.0f, 1.0f); glcalllist (armpart); // armpart and contain invalid parameter values ?? Glpopmatrix (); glcolor4f (1.0f, 0.0f, 0.0f, 1.0f); glcalllist (armpart); glpopmatrix (); 6. Solve the Problem of screen flickering. We know that when you drag a graph in a window, the image will flash as the image is painted and displayed. Solving this problem in GDI is complicated. by generating a memory DC in the memory, the paint brush is painted in the memory DC, the Flash problem can be solved once bitblt is used to paste the memory DC to the display. In OpenGL, we solve this problem through dual cache. In general, dual cache is common in graphic work software. Dual-cache is a two-cache, one front-end cache and one back-end cache. The drawing is first drawn in the background cache. After the painting is finished, it is switched to the foreground cache so that there will be no flickering. The following steps can easily solve this problem: 1) Note that the GDI Command is not designed with dual-cache. Change invalidaterect (null) to invalidaterect (null, false ). In this way, the re-painting command of GDI is invalidated and the OpenGL command is used for re-painting; 2) the pixel format is defined to support dual-Cache (Note: pfd_doublebuffer and pfd_support_gdi can only take one, ). Pixeldesc. dwflags = inline | pfd_support_opengl | pfd_doublebuffer | pfd_stereo_dontcare; 3) We have to tell OpenGL to draw a picture in the background cache and add gldrawbuffer (gl_back) to the last line of the class onsize ); 4) Finally, we have to change the content cached in the background to the foreground cache, and add swapbuffers (DC. M _ PS. HDC ). 7. Generate a simple 3D image. We know that the 3D coordinate system is different from the 2D coordinate system, and the 3D coordinate is a zcoordinate more than the 2D coordinate. We use gluortho2d to generate a simple two-dimensional image. When we generate a three-dimensional image, we need two near-distance cropping planes to generate a perspective effect. In fact, two-dimensional images only support the near-cropping plane Z =-1 and the far-cropping plane Z = 1. In this way, the zcoordinate is always treated as 0, and there is no essential difference between the two. On the basis of the above, we can generate 3D objects only by making simple changes. 1) First, replace gluortho2d (0.0, 500.0 * aspect, 0.0, 500.0) with gluperspective (60, aspect, 1, 10.0) in onsize ); in this way, the three-dimensional perspective coordinate system is set. This statement indicates that the viewpoint is at the origin, the perspective angle is 60 degrees, the near cropping surface is at Z = 1, and the far cropping surface is at Z = 10.0. 2) generate a 3D image in renderscene (). In fact, it is composed of polygon. The following example shows a 3D polygon: glmaterialfv (gl_front_and_back, gl_ambient, redsurface) glbegin (gl_polygon); glnormal3d (1.0, 0.0, 0.0); glvertex3d (1.0, 1.0, 1.0 ); glvertex3d (1.0,-1.0, 1.0); glvertex3d (1.0,-1.0,-1.0); glvertex3d (1.0, 1.0,-1.0); glend (); 3) we use the glmaterialfv (GL _ front_and_back, gl_ambient, redsurface) function to define the surface attributes of a polygon and set the environment color for the front and back of each plane. Of course, we have to define the illumination model, so we only need to add glable (gl_lighting) at the end of onsize (); redsufface is a color Component Array, for example: redsufface [] = {1.0f, 0.0f, 0.0f}; to define the environment color of a plane, you only need to add glmaterialfv to the definition of the plane, as shown in the preceding example. 4) Z-buffer problem: To make three-dimensional objects appear smoother and the spatial relationships between the front and back sides are correct, the Z-buffer technology must be used; otherwise, the positions of the front and back sides overlap with each other, cannot be correctly displayed. The Z-buffer zone stores the value of each vertex of an object. This value indicates the distance between the vertex and the human eye. The Z buffer takes a lot of memory and CPU time. To enable the Z buffer, you only need to add the glable (gl_depth_test) at the end of onsize (). Remember: Use the glclear (gl_depth_buffer_bit) statement to clear the Z buffer before each re-painting. 5) Now we can correctly generate 3D objects, but we still need to beautify them to make them look brighter. We use the gllightfv function to define the property value of the light source. The following example defines a light source: gllightfv (gl_light0, linear, lightambient); gllightfv (gl_light0, gl_diffuse, lightdiffuse); gllightfv (gl_light0, gl_specular, lightspecular); gllightfv (linear, gl_position, lightposition); glable (gl_light0); gl_light0 is the identifier of the light source. The identifier consists of gl_lighti (from 0 to gl_max_lights ). Gl_ambient, gl_diffuse, gl_specular, and gl_position define the color intensity around the light source, the scattering intensity of the light source, the mirror reflection intensity of the light source, and the position of the light source respectively. The example in this article is relatively simple, and there are many examples in VC. With reference to the settings in this article, you will be able to experience the powerful graphics and image rendering functions of OpenGL.