计算机图形学编程试8MFC明暗处理实现————————————————————————————————作者:————————————————————————————————日期:计算机图形学编程练习8:MFC+明暗处理实现MFC与OpenGL集成在Windows下编程,利用MFC是一个非常便捷的方法。
本次练习的主要目的,是希望同学们在MFC应用程序框架下进行OpenGL编程。
为此,需要对MFC生成的应用程序进行适当的初始化,关于这方面的内容详见:[1] Crain, Dennis. "Windows NT OpenGL: Getting Started." April 1994. (MSDN Library, Technical Articles)[2] Rogerson, Dale. "OpenGL I: Quick Start.". 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 specificationHDC m_hDC; // Device ContextHGLRC m_hGLRC; // Rendering ContextCPalette m_cGLLP; // Logical PaletteStep 2: 设置像素格式创建CView类的WM_CREATE的消息响应函数,进行像素格式的设置,例如:int COpenGLRenderView::OnCreate(LPCREATESTRUCT lpCreateStruct){if (CView::OnCreate(lpCreateStruct) == -1)return -1;// TODO: Add your specialized creation code hereint nPixelFormat; // Pixel format indexHWND hWnd = GetSafeHwnd(); // Get the window's handlem_hDC = ::GetDC(hWnd); // Get the Device contextstatic PIXELFORMATDESCRIPTOR pfd = {sizeof(PIXELFORMATDESCRIPTOR), // Size of this structure1, // Version of this structurePFD_DRAW_TO_WINDOW | // Draw to Window (not to bitmap)PFD_SUPPORT_OPENGL | // Support OpenGL calls in windowPFD_DOUBLEBUFFER, // Double buffered modePFD_TYPE_RGBA, // RGBA Color mode24, // Want 24bit color0,0,0,0,0,0, // Not used to select mode0,0, // Not used to select mode0,0,0,0,0, // Not used to select mode32, // Size of depth buffer0, // Not used to select mode0, // Not used to select modePFD_MAIN_PLANE, // Draw in main plane0, // Not used to select mode0,0,0 }; // Not used to select mode// Choose a pixel format that best matches that described in pfdnPixelFormat = ChoosePixelFormat(m_hDC, &pfd);// Set the pixel format for the device contextVERIFY(SetPixelFormat(m_hDC, nPixelFormat, &pfd));// Create the rendering contextm_hGLRC = wglCreateContext(m_hDC);// Create the palette if neededInitializePalette();// 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 DescriptorLOGPALETTE *pPal; // Pointer to memory for logical paletteint nPixelFormat; // Pixel format indexint nColors; // Number of entries in paletteint i; // Counting variableBYTE 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 NULLif (!(pfd.dwFlags & PFD_NEED_PALETTE))return;// Number of entries in palette. 8 bits yeilds 256 entriesnColors = 1 << olorBits;// Allocate space for a logical palette structure plus all the palette entries pPal = (LOGPALETTE*)malloc(sizeof(LOGPALETTE) +nColors*sizeof(PALETTEENTRY));// Fill in palette headerpPal->palVersion = 0x300; // Windows 3.0pPal->palNumEntries = nColors; // table size// 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 entriesfor (i = 0; i < nColors; i++){// Fill in the 8-bit equivalents for each componentpPal->palPalEntry[i].peRed = (i >> pfd.cRedShift) & RedRange;pPal->palPalEntry[i].peRed = (unsigned char)((double) pPal->palPalEntry[i].peRed * 255.0 / RedRange);pPal->palPalEntry[i].peGreen = (i >> pfd.cGreenShift) & GreenRange;pPal->palPalEntry[i].peGreen = (unsigned char)((double)pPal->palPalEntry[i].peGreen * 255.0 / GreenRange);pPal->palPalEntry[i].peBlue = (i >> pfd.cBlueShift) & BlueRange;pPal->palPalEntry[i].peBlue = (unsigned char)((double)pPal->palPalEntry[i].peBlue * 255.0 / BlueRange);pPal->palPalEntry[i].peFlags = (unsigned char) NULL;}// Create the palettem_cGLLP.CreatePalette(pPal);// Go ahead and select and realize the palette for this device contextSelectPalette(m_hDC,(HPALETTE)m_cGLLP,FALSE);RealizePalette(m_hDC);// Free the memory used for the logical palette structurefree(pPal);}至此,已经可以调用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 defaultreturn FALSE;}Step 6: 设置OpenGL的基本参数(可选)在CView类中添加头文件:#include <GL/gl.h>#include <GL/glu.h>#include <GL/glut.h>以及成员函数:void GLSetupDef(void *pData);创建CView类的一个成员函数,进行OpenGL的基本参数的设置,例如:void COpenGLRenderView::GLSetupDef(void *pData){// set up clear colorglClearColor(1.0f, 1.0f, 1.0f, 1.0f);// set up lightingGLfloat light_position0[4] = {52.0f, 16.0f, 50.0f, 0.0f}; GLfloat light_position1[4] = {26.0f, 48.0f, 50.0f, 0.0f}; GLfloat light_position2[4] = {-16.0f, 52.0f, 50.0f, 0.0f};GLfloat direction0[3] = {-52.0f, -16.0f, -50.0f};GLfloat direction1[3] = {-26.0f, -48.0f, -50.0f};GLfloat direction2[3] = {16.0f, -52.0f, -50.0f};GLfloat color0[4] = {1.0f, 0.0f, 0.0f, 1.0f};GLfloat color1[4] = {0.0f, 1.0f, 0.0f, 1.0f};GLfloat color2[4] = {0.0f, 0.0f, 1.0f, 1.0f};glLightfv(GL_LIGHT0, GL_POSITION, light_position0); glLightfv(GL_LIGHT0, GL_SPOT_DIRECTION, direction0); glLightfv(GL_LIGHT0, GL_DIFFUSE, color0);glLightfv(GL_LIGHT0, GL_SPECULAR, color0);glLightfv(GL_LIGHT1, GL_POSITION, light_position1); glLightfv(GL_LIGHT1, GL_SPOT_DIRECTION, direction1); glLightfv(GL_LIGHT1, GL_DIFFUSE, color1);glLightfv(GL_LIGHT1, GL_SPECULAR, color1);glLightfv(GL_LIGHT2, GL_POSITION, light_position2); glLightfv(GL_LIGHT2, GL_SPOT_DIRECTION, direction2); glLightfv(GL_LIGHT2, GL_DIFFUSE, color2);glLightfv(GL_LIGHT2, GL_SPECULAR, color2);glEnable(GL_LIGHTING);glEnable(GL_LIGHT0);glEnable(GL_LIGHT1);glEnable(GL_LIGHT2);// set up materialsGLfloat ambient[4] = {0.3f, 0.3f, 0.3f, 0.5f};GLfloat material_ambient[4] = {0.0f, 0.0f, 0.0f, 0.0f}; GLfloat material_diffuse[4] = {1.0f, 1.0f, 1.0f, 0.3f}; GLfloat material_specular[4] = {0.5f, 0.5f, 0.5f, 0.5f}; GLfloat material_shininess = 51.2f;glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambient);glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, material_specular);glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, material_diffuse);glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, material_ambient);glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, material_shininess);// enable depth testglDepthFunc(GL_LESS);glEnable(GL_DEPTH_TEST);// enable cull face// glCullFace(GL_BACK);// glEnable(GL_CULL_FACE);// set up point size and line widthglPointSize(1.0);glLineWidth(1.0);// set up point, line, polygon anti-aliasing mode// glEnable(GL_POINT_SMOOTH);// glHint(GL_POINT_SMOOTH_HINT, GL_NICEST);// glEnable(GL_LINE_SMOOTH);// glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);glEnable(GL_POLYGON_SMOOTH);glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST);// set up the direction of front faceglFrontFace(GL_CCW);// glPolygonMode(GL_BACK, GL_LINE);}Step 7: 设置绘制模式、重载OnSize函数、鼠标响应函数处理等(可选) 略,参见计算机图形学编程练习6及示例代码。