计算机图形学编程练习8:MFC+明暗处理实现
MFC与OpenGL集成
在Windows下编程,利用MFC是一个非常便捷的方法。本次练习的主要目的,是希望同学们在MFC应用程序框架下进行OpenGL编程。为此,需要对MFC生成的应用程序进行适当的初始化,关于这方面的内容详见: [1] Crain, Dennis. \April 1994. (MSDN Library, Technical Articles) [2] Rogerson, Dale. \. December 1994. (MSDN Library, Technical Articles)
[3] D. Shreiner and The Khronos OpenGL ARB Working Group. OpenGL Programming Guide: The Official Guide to Learning OpenGL, Versions 3.0 and 3.1, 7th Ed., 2009. (附录D)
从设计目标来说,OpenGL是流水线结构(streamlined)、硬件无关(hardware-independent)、跨平台的3D图形编程API。但是,在实际应用时,OpenGL的具体实现是与操作系统以及图形硬件相关的。为此,操作系统需要提供像素格式(pixel format)与绘制上下文管理函数(rendering context managnment functions)。Windows操作系统提供了通用图形设备接口(generic graphics device interface, GDI)以及设备驱动实现。为了使OpenGL命令得到正确的执行,需要调用WGL函数,具体的步骤如下:
Step 1: 添加成员变量
在CView类(利用AppWizard生成)中添加如下成员变量:
// OpenGL Windows specification HDC m_hDC;
// Device Context // Rendering Context
HGLRC m_hGLRC;
CPalette m_cGLLP; // Logical Palette
Step 2: 设置像素格式
创建CView类的WM_CREATE的消息响应函数,进行像素格式的设置,例如:
int COpenGLRenderView::OnCreate(LPCREATESTRUCT lpCreateStruct) {
if (CView::OnCreate(lpCreateStruct) == -1)
// TODO: Add your specialized creation code here int nPixelFormat;
// Pixel format index // Get the window's handle // Get the Device context
HWND hWnd = GetSafeHwnd(); m_hDC = ::GetDC(hWnd);
static PIXELFORMATDESCRIPTOR pfd = {
sizeof(PIXELFORMATDESCRIPTOR),
1,
PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL |
// Size of this structure // Version of this structure
// Draw to Window (not to bitmap) // Support OpenGL calls in window
return -1;
}
PFD_DOUBLEBUFFER, PFD_TYPE_RGBA, 24, 0,0, 32, 0, 0, 0,
0,0,0,0,0,0, 0,0,0,0,0,
// Double buffered mode // RGBA Color mode // Want 24bit color // Not used to select mode // Not used to select mode // Not used to select mode // Size of depth buffer // Not used to select mode // Not used to select mode // Draw in main plane // Not used to select mode // Not used to select mode
PFD_MAIN_PLANE, 0,0,0 };
// Choose a pixel format that best matches that described in pfd nPixelFormat = ChoosePixelFormat(m_hDC, &pfd);
// Set the pixel format for the device context VERIFY(SetPixelFormat(m_hDC, nPixelFormat, &pfd));
// Create the rendering context m_hGLRC = wglCreateContext(m_hDC);
// Create the palette if needed InitializePalette();
// Make the rendering context current, perform initialization, then deselect it VERIFY(wglMakeCurrent(m_hDC, m_hGLRC)); GLSetupDef(m_hDC);
wglMakeCurrent(NULL, NULL);
return 0;
上述步骤的具体含义参看参考文献[1-3].
Step 3: 创建绘制上下文
该步骤在Step 2中已完成,具体的就是:
m_hGLRC = wglCreateContext(m_hDC);
Step 4: 设置调色板
创建CView类的一个成员函数,进行调色板的设置,例如:
void CTriangularPatchView::InitializePalette(void) {
PIXELFORMATDESCRIPTOR pfd; // Pixel Format Descriptor LOGPALETTE *pPal;
// Pointer to memory for logical palette
int nPixelFormat; int nColors; int i;
// Pixel format index
// Number of entries in palette // Counting variable
BYTE RedRange,GreenRange,BlueRange; // Range for each color entry (7,7,and 3)
// Get the pixel format index and retrieve the pixel format description nPixelFormat = GetPixelFormat(m_hDC);
DescribePixelFormat(m_hDC, nPixelFormat, sizeof(PIXELFORMATDESCRIPTOR), &pfd);
// Does this pixel format require a palette? If not, do not create a // palette and just return NULL
if (!(pfd.dwFlags & PFD_NEED_PALETTE))
// Number of entries in palette. 8 bits yeilds 256 entries nColors = 1 << pfd.cColorBits;
// Allocate space for a logical palette structure plus all the palette entries pPal = (LOGPALETTE*)malloc(sizeof(LOGPALETTE) +nColors*sizeof(PALETTEENTRY));
// Fill in palette header pPal->palVersion = 0x300;
// Build mask of all 1's. This creates a number represented by having // the low order x bits set, where x = pfd.cRedBits, pfd.cGreenBits, and // pfd.cBlueBits.
RedRange = (1 << pfd.cRedBits) -1; GreenRange = (1 << pfd.cGreenBits) - 1; BlueRange = (1 << pfd.cBlueBits) -1;
// Loop through all the palette entries for (i = 0; i < nColors; i++) {
// Fill in the 8-bit equivalents for each component
pPal->palPalEntry[i].peRed = (i >> pfd.cRedShift) & RedRange; pPal->palPalEntry[i].peRed = (unsigned char)(
pPal->palPalEntry[i].peGreen = (i >> pfd.cGreenShift) & GreenRange; pPal->palPalEntry[i].peGreen = (unsigned char)(
(double)pPal->palPalEntry[i].peGreen * 255.0 / GreenRange); (double) pPal->palPalEntry[i].peRed * 255.0 / RedRange);
// Windows 3.0
pPal->palNumEntries = nColors; // table size
return;
}
}
// Create the palette
m_cGLLP.CreatePalette(pPal);
// Go ahead and select and realize the palette for this device context SelectPalette(m_hDC,(HPALETTE)m_cGLLP,FALSE); RealizePalette(m_hDC);
// Free the memory used for the logical palette structure free(pPal);
pPal->palPalEntry[i].peBlue = (i >> pfd.cBlueShift) & BlueRange; pPal->palPalEntry[i].peBlue = (unsigned char)(
pPal->palPalEntry[i].peFlags = (unsigned char) NULL;
(double)pPal->palPalEntry[i].peBlue * 255.0 / BlueRange);
至此,已经可以调用OpenGL函数了,一定要记住:OpenGL命令只在获取了正确的绘制上下文后才能正确执行,即
wglMakeCurrent(m_hDC, m_hGLRC); // issue OpenGL commands wglMakeCurrent(m_hDC, NULL);
…..
Step 5: 重载OnEraseBkgnd函数
创建CView类的WM_ERASEBKGND的消息响应函数,防止Windows进行额外的背景清除操作,例如:
BOOL COpenGLRenderView::OnEraseBkgnd(CDC* pDC) { }
// TODO: Add your message handler code here and/or call default
return FALSE;
Step 6: 设置OpenGL的基本参数(可选)
在CView类中添加头文件:
#include
以及成员函数:
void GLSetupDef(void *pData);
创建CView类的一个成员函数,进行OpenGL的基本参数的设置,例如:
void COpenGLRenderView::GLSetupDef(void *pData) {