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)
页面执行时间:203.646毫秒