Graphics 版 (精华区)

发信人: jun (☆子夜☆), 信区: Graphics
标  题: OpenGL programming in MFC(1)
发信站: 哈工大紫丁香 (Sat Jan  9 15:08:53 1999), 转信

发信人: lanhaitao (蓝天-大海-波涛-你和我), 信区: Graphics
发信站: BBS 水木清华站 (Sat Nov 21 15:57:48 1998)

Using OpenGL in Visual C++
Version 4.x 

Writing an OpenGL Program in MFC

The first program demonstrated here will show you the minimum 
requirements for setting up a Windows program to display OpenGL 
graphics. As GDI needs a Device Context(DC) to draw images, OpenGL
requires a Rendering Context (RC). Unlike GDI, inwhich each GDI
command requires that a DC is passed into it, OpenGL uses the
concept of a current RC. Once a rendering context has been made
current in a thread, all OpenGL calls in that thread will use the
 same current rendering context. While multiple rendering contexts
may be used to draw in a single window, only one rendering context
may be current at any time in a single thread. 

The goal of this sample is to create and make current an OpenGL
rendering context.There are three steps to creating and making 
current a rendering context: 

   1.Set the window's pixel format. 
   2.Create the rendering context. 
   3.Make the rendering context current. 

Take the following steps to create the project: 

   1.Create a new Project Workspace of type "MFC AppWizard (exe)".
    Select the directory you where you want the project directory 
    to be created, and type  "GLSample1" as the project name. Click 
    "Create" to enter the AppWizard. 
     Following is a list of the steps in the AppWizard and the 
  parameters you should  enter in each of them. Any parameters
   not listed are optional. 
   2.Single Document Interface 
   3.Database support: None 
   4.OLE support: None 
   5.Docking Toolbar: OFF (optional) 
     Initial Status Bar: OFF (optional) 
     Printing an Print Preview: OFF (Printing OpenGL images is 
     accomplished by creating an RC using a printer DC. If you 
     would like to experiment with this later, without rebuilding 
     everything, go ahead and turn this option on). 
     Context-Sensitive Help: OFF (optional) 
     3D Controls: ON (optional) 
   6.Generate Source File Comments: Yes 
     Use the MFC library as a shared DLL. 
   7.Keep everything at the default. 
     Press Finish 

  Check the "New Project Information" dialog to make sure everything 
is as it should be and press OK. The new project will be created in
the subdirectory "GLSample1". 

First we will include all necessary OpenGL files and libraries in this 
project. Select "Build-Settings" from the menu. Click on the "Link" tab
 (or press Ctrl-Tab to move there). Select the "General" category (it 
should already be selected by default), and enter the following into the 
Object/Library Modules edit box: "opengl32.lib glu32.lib glaux.lib".
Press OK. Now open the file "stdafx.h". Insert the following lines into 
the file:

        #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>
        #include <gl\glu.h>
        #ifndef _AFX_NO_AFXCMN_SUPPORT
        #include <afxcmn.h>   // MFC support for Windows 95 Common Controls

        #endif // _AFX_NO_AFXCMN_SUPPORT

OpenGL requires the window to have styles WS_CLIPCHILDREN and
WS_CLIPSIBLINGS set. Edit OnPreCreate so that it looks like this:

        BOOL CGLSample1View::PreCreateWindow(CREATESTRUCT& cs)
        {
                cs.style |= (WS_CLIPCHILDREN | WS_CLIPSIBLINGS);

                return CView::PreCreateWindow(cs);
        }

The first set to creating a rendering context is to define the window's
 pixel format. The pixel format describes how the graphics that the window
 displays are represented in memory. Parameters controlled by the pixel format
 include color depth, buffering method, and supported drawing interfaces. We 
will look at some of these below. First create a new protected member function
 in the CGLSample1View class called "BOOL SetWindowPixelFormat(HDC hDC)" 
(my preferred method of doing this is right clicking on the class name in the
 Project Workspace and selecting "Add Function..." from the resulting pop-up menu.
 You may also do it manually if you wish) and edit the function so
 that it looks like this: 

        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.iLayerType            = PFD_MAIN_PLANE;
                pixelDesc.bReserved             = 0;
                pixelDesc.dwLayerMask           = 0;
                pixelDesc.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;

        }

Now add the following member variable to the CGLSample1View class (again, 
I like to use the right mouse button on the class name and select "Add 
Variable..."): 

        int m_GLPixelIndex; // protected

Finally, in the ClassWizard, add the function OnCreate in response to a 
WM_CREATE message and edit it to look like this: 

        int CGLSample1View::OnCreate(LPCREATESTRUCT lpCreateStruct) 
        {
                if (CView::OnCreate(lpCreateStruct) == -1)
                        return -1;
                
                HWND hWnd = GetSafeHwnd();
                HDC hDC = ::GetDC(hWnd);

                if (SetWindowPixelFormat(hDC)==FALSE)
                        return 0;
                
                return 0;
        }

Compile the program and fix any syntax errors. You may run the program
 if you wish but at the moment, it will look like a generic MFC shell. 
Try playing with the pixel format descriptor. You may want to try passing
 other indices into DescribePixelFormat to see what pixel formats are 
available. I'll spend some time now explaining what the code does and 
precautions  you should take in the future.  

PIXELFORMATDESCRIPTOR contains all of the information defining a pixel
 format. I'll explain some of the important points here, but for a complete 
description look in the VC++ online help. 

     dwFlags Defines the devices and interfaces with which the pixel format 
     is compatible. Not all of these flags are implemented in the generic 
     release of  OpenGL. Refer to the documentation for more information. 
     dwFlags can accept   the following flags:
     PFD_DRAW_TO_WINDOW -- Enables drawing to a window or device surface.
     PFD_DRAW_TO_BITMAP -- Enables drawing to a bitmap in memory.
     PFD_SUPPORT_GDI -- Enables GDI calls. Note: This option is not valid if
     PFD_DOUBLEBUFFER is specified.
     PFD_SUPPORT_OPENGL -- Enables OpenGL calls.
     PFD_GENERIC_FORMAT -- Specifies if this pixel format is supported by the
     Windows GDI library or by a vendor hardware device driver.
     PFD_NEED_PALETTE -- Tells if the buffer requires a palette. This tutorial
     assumes color will be done with 24 or 32 bits and will not cover palettes.
     PFD_NEED_SYSTEM_PALETTE -- This flag indicates if the buffer requires the
     reserved system palette as part of its palette. As stated above, this 
     tutorial will not  cover palettes.
     PFD_DOUBLEBUFFER -- Indicates that double-buffering is used. Note that GDI
     cannot be used with windows that are double buffered.
     PFD_STEREO -- Indicates that left and right buffers are maintained for 
     stereo  images.
     iPixelType Defines the method used to display colors. PFD_TYPE_RGBA
     means each set of bits represents a Red, Green, and Blue value, while
     PFD_TYPE_COLORINDEX means that each set of bits is an index into a color
     lookup table. All of the examples in this program will use PFD_TYPE_RGBA. 
     cColorBits Defines the number of bits used to define a color. For RGBA it
     is the number of bits used to represent the red, green, and blue components
     of the color ( but not the alpha). For indexed colors, it is the number
     of colors in the table. 
     cRedBits, cGreenBits, cBlueBits, cAlphaBits The number of bits used
     to represent the respective components. 
     cRedShift, cGreenShift, cBlueShift, cAlphaShift The number of bits
     each componet is offset from the beginning of the color. 

Once we initialize our structure, we try to find the system pixel format that
is closest to  the one we want. We do this by calling:

        m_hGLPixelIndex = ChoosePixelFormat(hDC, &pixelDesc);

ChoosePixelFormat takes an hDC and a PIXELFORMATDESCRIPTOR*, and returns an
index used to reference that pixel format, or 0 if the function fails. If the
function fails, we just set the index to 1 and get the pixel format description
using DescribePixelFormat. There are a limited number of pixel formats, and the 
system defines what their properties are. If you ask for pixel format properties
 that are not supported, ChoosePixelFormat will return an integer to the format 
that is closest to the one you requested. Once we have a valid pixel format index
and the corresponding description we can call SetPixelFormat. A window's pixel 
format may be set only once. 

Now that the pixel format is set, all we have to do is create the rendering context
 and make it current. Start by adding a new protected member function to the
CGLSample1View class called "BOOL CreateViewGLContext(HDC hDC)" and edit it
so that it looks like this: 

        BOOL CGLSample1View::CreateViewGLContext(HDC hDC)
        {
                m_hGLContext = wglCreateContext(hDC);
                if (m_hGLContext == NULL)
                {
                        return FALSE;
                }

                if (wglMakeCurrent(hDC, m_hGLContext)==FALSE)
                {
                        return FALSE;
                }

                return TRUE;
        }

Add the following member variable to the CGLSample1View class: 

        HGLRC m_hGLContext; // protected

Edit OnCreate to call the new function: 

        int CGLSample1View::OnCreate(LPCREATESTRUCT lpCreateStruct) 
        {
                if (CView::OnCreate(lpCreateStruct) == -1)
                        return -1;
                
                HWND hWnd = GetSafeHwnd();
                HDC hDC = ::GetDC(hWnd);

                if (SetWindowPixelFormat(hDC)==FALSE)
                        return 0;

                if (CreateViewGLContext(hDC)==FALSE)
                        return 0;
                
                return 0;
        }

Add the function OnDestroy in response to a WM_DESTROY message and edit it to
look like this: 

        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(); 
        }

And lastly, edit the CGLSample1View class constructor to look like this: 

        CGLTutor1View::CGLTutor1View()
        {
                m_hGLContext = NULL;
                m_GLPixelIndex = 0;
        }

Once again compile the program and fix any syntax errors. When you run the program it
will still look like a generic MFC program, but it is now enabled for OpenGL drawing.
You may have noticed that we created one rendering context at the beginning of the
program and used it the entire time. This goes against most GDI programs where DCs
are created only when drawing is required and freed immediately afterwards. This is a
valid option with RCs as well, however creating an RC can be quite processor intensive.
Because we are trying to achieve high performance graphics, the code only creates the
RC once and uses it the entire time. 

CreateViewGLContext creates and makes current a rendering context.
wglCreateContext returns a handle to an RC. The pixel format for the device
associated with the DC you pass into this function must be set before you call
CreateViewGLContext. wglMakeCurrent sets the RC as the current context. The DC
passed into this function does not need to be the same DC you used to create the
context, but it must have the same device and pixel format. If another rendering 
context is current when you call wglMakeCurrent, the function simply flushes the
old RC and replaces it with the new one. You may call wglMakeCurrent(NULL, NULL) 
to make no rendering context current. 

Because OnDestroy releases the window's RC, we need to delete the rendering context
there. But before we delete the RC, we need to make sure it is not current. We use
wglGetCurrentContext to see if there is a current rendering context. If there is, we
remove it by calling wglMakeCurrent(NULL, NULL). Next we call wglDeleteContext to
delete out RC. It is now safe to allow the view class to release the DC. Note that 
since the RC was current to our thread we could have just called wglDeleteContext
 without first making it not current. Don't get into the habit of doing this. If you
 ever start using multi-threaded applications that laziness is going to bite you. 
e latest version of PKZip. 

                     

                                  
Congratulations on your first OpenGL program, even if it doesn't do much! If you
already know OpenGL on another platform read the tips below and go write the next
killer graphics applications. If you don't know OpenGL keep reading. I'll give you 
a tour of some of its functions. 

OpenGL Tips: 

   1.Set the viewport and matrix modes in response to a WM_SIZE message. 
   2.Do all of your drawing in response to a WM_PAINT message. 
   3.Creating a rendering context can take up a lot of CPU time. Only
     create it once and use it for the life of your program. 
   4.Try encapsulating your drawing commands in the document class. That way you
     can use the same document in different views. 


--
※ 来源:·BBS 水木清华站 bbs.net.tsinghua.edu.cn·[FROM: 202.38.220.50]

--
☆ 来源:.哈工大紫丁香 bbs.hit.edu.cn.[FROM: sunup.bbs@bbs.net.ts]
[百宝箱] [返回首页] [上级目录] [根目录] [返回顶部] [刷新] [返回]
Powered by KBS BBS 2.0 (http://dev.kcn.cn)
页面执行时间:209.105毫秒