Implement OpenGL programming Liu Dong Yuyi with Visual C 5, OpenGL is known, OpenGL is originally Silicon Graphics Incorporated (SGI) to develop high-quality images on their graphics workstation. But in recent years it is a very good open three-dimensional graphics interface. In fact, it is a graphics software and hardware interface, which includes more than 120 graphics functions, "GL" is the abbreviation of "Graphic Library", meaning "graphic library". OpenGL's emergence makes most of the programmers to develop complex three-dimensional graphics in C language on the PC. Microsoft has provided three OpenGL libraries in Visual C 5 (Glu32.lib, gau.lib, OpenGL32.LIB), which allows us to easily program, simple, and quickly generate beautiful, beautiful graphics. For example, the basket and maze in the screen saver in Windows NT have left a deep impression. Second, the basic steps and conditions of generating OpenGL programs will give an example. This example is a Windows program that uses OpenGL display images. We can also know the basic requirements of OpenGL programming through this program. We know that GDI draws through the device handle (DC "below Device Context, while OpenGL needs to draw the environment (Rendering Context, hereinafter referred to as" RC "). Each GDI command needs to be transmitted to it a DC, different from GDI, OpenGL uses the current drawing environment (RC). Once a current RC is specified in a thread, all OpenGL commands in this thread use the same current RC. Although multiple RCs can be used in a single window, there is only one current RC in a single thread. This example will first generate an OpenGL RC and make it a current RC, divided into three steps: set the window pixel format; generate RC; set to the current RC. 1. First create an engineer to generate an Exe file using AppWizard, select the project directory, and enter "GLSAMPLE1" in the project name, keep other unchanged; first step, monitless document (SDI); second step, do not support databases; The third step, does not support OLE; fourth steps, unsearched floating tool bars, start state strips, print and preview support, help support check box (optional, this article only explains the minimum requirements), select 3D control (3D) Controls); Step 5, select the generated source file annotation and use MFC to share dynamic libraries; sixth step, maintain the default. Press Finish to end, the engineering is created, as shown in Figure 1. (Figure VC-1) Figure 1 2, add the OpenGL files and libraries required for this project to the project in the engineering menu, select the "Settings" item under "Build". Click the "LINK" tab, select "General" directory, enter "OpenGL32.Lib Glu32.lib Glaux.lib" in the Object / Library Modules edit box (note, enter the contents in double quotes, each library is separated by spaces; Otherwise, a link error will appear), select "OK" to end.
Then open the file "stdafx.h", insert the following statement into the file (set the underlined statement for the plot "): #define vc_extralean // Exclude rarely-useed stuff from Windows Headers #include
BOOL CGLSample1View :: SetWindowPixelFormat (HDC 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.ilyertype = pfd_main_plane; pixeldesc.breserved = 0; pixeldesc.dwlayermask = 0; P ixelDesc.dwVisibleMask = 0; pixelDesc.dwDamageMask = 0; 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;} Next right added protection type in CGLSample1View in Member variable: int m_glpixelindex; 4, add the oncreate function with the ClassWizard to add the oncreate function after the ONCREATE function is shown.
At this point, the basic framework of OpenGL project is built. But if you are now running this project, it doesn't seem to have something different from the general MFC program. 5. Code Interpretation Now we can take a look at what kind of pixel format for Describe-Pixelformat and explain some explanations for the code: PixelformATDescriptor includes all information defining pixel formats. DWFlags defines devices and interfaces compatible with pixel formats. The usual OpenGL release does not include all flags (FLAG). WFLAGS can receive the following flag: PFD_DRAW_TO_WINDOW can be drawn in a window or other device window; PFD_DRAW_TO_BITMAP enables bit drawing drawing in memory; PFD_support_gdi makes it able to call GDI functions (Note: If pfd_doublebuffer is specified, this option will be invalid) PFD_support_opengl make it call OpenGL function; pfd_gener_format, if this pixel format is supported by the Windows GDI function library or by third-party hardware device driver, you need to specify this; PFD_NEED_PALETTE tells the buffer whether you need palette, this program Suppose the color uses 24 or 32-bit color, and does not overwrite the palette; the flag indicates whether the buffer is part of its own palette as part of its own palette; PFD_Doublebuffer specifies the use of a buffer (Note: GDI cannot draw in a window using a double buffer); PFD_STEREO indicates whether the left and right buffers are organized by stereoscopic images. PixelType Defines a method of displaying a color. Pfd_type_rgba means that each (bit) group represents the value of red, green, and blue. PFD_type_colorindex means that each group represents an index value in a color lookup table. This example uses a PFD_TYPE_RGBA mode. ● CColorBits defines the number of bits that specify a color. For RGBA, the number of digits is the number of digits of red, green, and blue in color. For the index value of the color, it refers to the number of colors in the table. ● Credbits, CGreenBits, CBLUE-BITS, CALPHABITS are used to indicate the number of bits used by each corresponding component. ● Credshift, CGreenshift, CBLUE-SHIFT, CALPHASHIFT is used to indicate the number of bits that each component is from the start of the color. Once initialized our structure, we want to know the most similar system pixel format. We can do this: m_hglpixelindex = choosepixelformat (HDC, & Pixeldesc); Choosepixelformat accepts two parameters: one is HDC, the other is a pointer to the PixelformATDescriptor structure (this function returns the index value of this pixel format. If it returns 0, it means a failure. If the function fails, we just set the index value to 1 and use Describepixelformat to get a pixel format description. If you apply for a pixel format that is not supported, Choose-Pixelformat will return a value closest to the pixel format you require. Once we get an index value of a pixel format and the corresponding description, we can call setpixelformat to set the pixel format and simply set it once. Now the pixel format has been set, and our next step is to generate a drawing environment (RC) and make it a current drawing environment.
Was added a protection type of the member function in CGLSample1View BOOL CreateViewGLContext (HDC hDC), so that it follows: BOOL CGLSample1View :: CreateView GLContext (HDC hDC) {m_hGLContext = wglCreate Context (hDC); // DC current generated by drawing ambient (RC) IF (m_hglcontext == null) {return false;} if (WGLmakecurrent (hdc, m_hglcontext) == false) {return false;} return true;} and add a protected member variable hglrc m_hglcontext; hglrc is a Point the handle of Rendering Context. OnCreate function call this function: int CGLSample1View :: OnCreate (LPCREATESTRUCT lpCreateStruct) {if (CView :: OnCreate (lpCreateS truct) == -1) return -1; HWND hWnd = GetSafeHwnd (); HDC hDC = :: GetDC (hWnd); setwindowpixelformat (hdc) == false) return 0; if (createViewGlContext (HDC) == false) return 0; return 0;} Add WM_DESTROY message processing function onDestroy (), make it as follows: void CGLSample1View :: OnDestroy () {if (! wglGetCurrentContext () = NULL) {// make the rendering context not current wglMakeCurrent (NULL, NULL);} if (m_hGLContext = NULL!) {wglDeleteContext (m_hGLContext); m_hGLContext = NULL ;} // Now the associated DC can be released CView :: OnDestroy ();} finally, edited CGLSample1View constructor, so that the following:. CGLTutor1View :: CGLTutor1View () {m_hGLContext = NULL; m_GLPixelIndex = 0;} At this point, we have constructed the framework so that the program can take advantage of OpenGL. You may have already noticed that we have an RC at the beginning of the program and use it from beginning to end. This is different from most GDI programs. In the GDI program, DC is generated when needed and is released immediately. In fact, RC can also do this; but remember that a RC requires a lot of processor time. Therefore, to get high performance smooth images and graphics, it is best to generate RC once and always use it until the program ends. CreateViewGlContex generates RC and makes it a current RC.
WGLCReateContext returns a Handle of the RC. Before you call CREATEVIEWGLCONTEX, you must use SetWindowPixelformat (HDC) to set the pixel format related to the device. WGLmakecurrent sets RC to current RC. The DC that is incorporated into this function is not necessarily that the DC of the RC, but the device handle (Device Context) and the pixel format must be consistent. If you already have another RC existed before calling WGLMakeForcurrent, WGLmakeForcurrent will turn 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 have to delete the drawing environment in OnDestroy. But before deleting the RC, it must be determined that it is not the current handle. We understand whether there is a current drawing environment via WGlgetCurrentContext. If you exist, you remove it with WGLmakecurrent (null, null). Then you can delete the RC through WGLDELETE-Context. At this time, it is safe to delete the DC. Note: In general, it is a single-threaded program. The generated RC is the current RC of the thread, and does not need to pay attention to the above. But if you are using multi-threaded programs, then we need to pay attention to this, otherwise you will have unexpected consequences. Third, an example will be given an example of a simple two-dimensional graphic (this example is based on the above). Add the WMSPLE2VIEW to CGLSample2View with ClassWizard to add a message to the program 2. (Figure GetPwd2) Figure 2 Adds WM_PAINT messages to CGLSAMPLE2VIEW to make it as shown in the program 3. The result of this program is a colorful triangle in black background (as shown in Figure 2). Here you can see that using OpenGL drawing graphics is very easy, only a few simple statements can achieve powerful features. If you zoom the window, the triangle will also be scaled. This is because Onsize defines the viewport and viewport coordinates via GLVIEWPORT (0, 0, Width, Height). The first and two parameters of GLVIEWPORT are pixel coordinates in the lower left corner of the viewport, third, four parameters are the width and height of the viewport. The GlmatrixMode in Onsize is used to set the matrix mode, which has three options: GL_ModelView, GL_Projection, GL_Texture. GL_MODELVIEW indicates to the human eye coordinate system from the entity coordinate system. GL_Projection indicates that the human eye coordinate system is turned to the cut coordinate system. Gl_text indicates the transformation of the coordinate system from the defined texture to the coordinate system of the paste texture. GLLoadIdentity Initialization Engineering Matrix; Gluortho2D sets the engineering matrix to display a two-dimensional right angle display area. Here we need to say the naming principle of the OpenGL command. Most OpenGL commands are starting with "GL". There are also some beginning to start with "Glu", they come from OpenGL Utility. Most "GL" commands define the type of variables in the name and perform the corresponding operation. For example: GlvertEx2f is defined a vertex, the parameter variable is two floating point numbers, representing the X, Y coordinates of this vertex, respectively. Similar to GlvertEX2D, Glvertex2f, GlvertEX3i, Glvertex3s, GlvertEX2SV, GLVERTEX3DV ... and other functions.
So, how do you draw a triangle? We first call Glcolor4f (1.0F, 0.0F, 0.0F, 1.0F), specify red, green, and blue components to 1, 0, 0, respectively. Then we define a point in (100, 50) with Glvertex2f (100.0F, 50.0F). Send, we define green dots at (450, 400), define the blue dots at (450, 50). Then we end the triangle with Glend. But at this time, the triangle has not been drawn, these orders are only in the buffer until you call the GLFLUSH function, triggered these commands by GLFLUSH. OpenGL automatically changes the color value between the triangular vertices, making it colorful. New graphics can also be generated via Glbegin. glBegin (GLenum mode) parameters: GL_POINTS, GL_LINES, GL_LINE_STRIP, GL_LINE_LOOP, GL_TRIANGLES, GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN, GL_QUADS, GL_QUAD_STRIP, GL_POLYGON glBegin function between effective and have glEnd: glVertex, glColor, glIndex, glNormal, glTexCoord, glEvalCoord, GlevalPoint, GLMATERIAL, GLEDGEFLAG 4, OpenGL programming Skindle 1. If you want to respond to the WM_SIZE message, you must set the viewport and matrix mode. 2. Try to do all your paintings when you respond to WM_PAINT messages. 3, generating a plotting environment to consume a lot of CPU time, so it is best to generate only once in the program until the program ends. 4. Try to package your drawing command in the document class so you can use the same document in different views, save your programming workload. 5, Glbegin and Glend must be paired, this is a plot statement to the element. GLPUSHMATRIX () and GLPopMatrix () must also be paired. GLPUSHMAMATRIX () copies the current matrix to the stack. When we call GLPOPMAtrix, the final matrix pressing the stack is restored to the current matrix. Use GLPUSHMAMATRIX () to exact the current matrix and restore it with GLPopmatrix. This way we can use this technology to place other objects relative to an object. For example, the following statement only uses one matrix, you can generate two rectangles, and place them into 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 鼍卣 鼍卣 GLPopmatrix (); Glcolor4f (1.0F, 0.0F, 0.0F, 1.0F); GlcallList (armpart) GLPopmatrix (); 6, solve the flashing problem of the screen. We know that when dragging a graphic in the window, a flashing phenomenon is blinking because of the edge screen display. Solving this problem in GDI is more complicated. By generating a memory DC in memory, letting the brush in the memory DC in memory. After the painting, use Bitblt to "stick" to the display, you can solve the flashing problem. .
In OpenGL, we solve this problem by dual cache. In general, the dual-buffet is very common in graphics work software. Dual cache is two cache, a front cache, a background cache. Drawing first in the background, after painting, swap to the front desk cache, so there will be no flash. This problem can be easily solved by the following steps: 1) Be careful that the GDI command is not designed with dual cash. We first change to InvalIDateRect (NULL, FALSE) where INVALIDATERECT (NULL) is used. This is to make the GDI's heavy blow command, and the OpenGL command will be redrawed; 2) Define the pixel format into support dual cache (Note: pfd_doublebuffer and pfd_support_gdi can only take one, both of which conflict with each other). pixelDesc.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER | PFD_STEREO_DONTCARE; 3) we have to tell OpenGL drawing in the back buffer, the last line in view OnSize class () added: glDrawBuffer (GL_BACK); 4) Finally, we have to back buffer Content is changed to the front cache, add: swapBuffers (dc.m_ ps.hdc) in the last line of ONPAINT () on the view class: swapbuffers. 7, generate simple three-dimensional graphics. We know that the three-dimensional and two-dimensional coordinate systems are different, and the three-dimensional graphics are more than one Z coordinate than the two-dimensional graphics. When we generate simple two-dimensional graphics, we use gluortho2d; we need two distal cropping planes to generate perspective effects when generating three-dimensional graphics. In fact, the two-dimensional graphics is just the close-cut cut plane Z = -1, the long cut-cutting plane z = 1; this Z coordinate is always treated as 0, the two have no essential difference. On the above basis, we only do simple changes, you can generate a three-dimensional object. 1) First, in OnSize (), GluortHO2D (0.0, 500.0 * Aspect, 0.0, 500.0) is replaced with Gluperspective (60, aspect, 1, 10.0); this is the setting of the three-dimensional perspective coordinate system. This statement shows that the perspective is in the origin, the perspective angle is 60 degrees, and the cut surface is at z = 1, the long cut surface is in Z = 10.0. 2) Generate a three-dimensional graphic in Renderscene; in fact, it consists of polygon. The following is an example of a three-dimensional 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 GLMATERIALFV (GL_FRONT_AAD_BACK, GL_AMBIENT, RedSurface) to define the surface properties of the polygon, for each plane The front and rear surface set the environment color.
Of course, we have to define light models, this only needs to add Glenable (GL_Lighting) in OnSIZE (); redSufface is an array of color components, such as redsufface [] = {1.0F, 0.0F, 0.0F}; A plane environment color, just put the GLMATERIALIALFV to the plane definition, as shown in the above example. 4) Z buffer problem: To make the three-dimensional object more smooth, the spatial relationship between the front and rear and rear, must use Z buffer technology; otherwise, the position of each face before and after overlapping each other, does not display correctly. The z buffer stores the value of each point of the object, which indicates the distance from the human eye. The z buffer requires a large amount of memory and CPU time. Enabling z buffer simply adds Glenable (GL_Depth_test) on OnSIZE (); Remember: Clear the Z buffer using the Glclear (GL_DEPTH_BUFFER_BIT) statement before each redraw. 5) It is now possible to generate a three-dimensional object correctly, but it also needs to beautify, so that the object is more bright. We define the properties value of the light source with the GLLightFv function. The following example will define a light source: glLightfv (GL_LIGHT0, GL_AMBIENT, LightAmbient); glLightfv (GL_LIGHT0, GL_DIFFUSE, LightDiffuse); glLightfv (GL_LIGHT0, GL_SPECULAR, LightSpecular); glLightfv (GL_LIGHT0, GL_POSITION, LightPosition); glEnable (GL_LIGHT0); GL_LIGHT0 Is the logo number of the light source, I identified by GL_Lighti (i from 0 to GL_MAX_Lights). GL_AMBIENT, GL_DIFFUSE, GL_SPECULAR, GL_POSION respectively defines the surrounding color intensity of the source, the scattering strength of the light source, the mirror reflection intensity of the light source, and the position of the light source. This article is simple, and there are many examples in Visual C 5.0. Refer to this document, you must experience the powerful graphics of OpenGL, image drawing function.