u
将OpenGL
与OS
的窗口系统进行关联
我们早说过,这是很重要的一步,也是一个SDI
的OpenGL
程序在初始化的过程中必须做的工作,因此下面需要介绍一些相关概念:
图形操作描述
在Windows
窗口程序必须首先处理设备描述表(Device Contexts
,DC
),DC
包括许多如何在窗口上显示图形的信息,如指定画笔和刷子的颜色,设置绘图模式、调色板、映射模式以及其它图形属性。同样,OpenGL
的程序也必须使用DC
,这与其它Windows
程序类似。但是,OpenGL
必须处理
特殊的
DC
渲染描述表
,这是DC
中专为OpenGL
使用的一种
“渲染描述表
(RC)
”
。一个OpenGL
渲染描述表内有OpenGL
与Windows
窗口系统相关的各种信息。
一个
OpenGL
应用首先必须创建一个渲染描述表,然后再启动它,最后在所定义的窗口内按常规方式调用
OpenGL
函数绘制图形。
一个渲染描述表RC
不同于其它DC
,后者调用每个GDI
函数都需要一个句柄,而RC
方式下只需一个句柄就可以任意调用OpenGL
函数。也就是说,只要当前启用了某个渲染描述表,那么在未删除渲染描述表之前可以调用任何OpenGL
函数,进行各种操作。在程序中也可以看到,除了在初始化和卸载过程中使用RC
外,其余的代码我们几乎没有再遇到过RC
。
像素格式
在创建一个图形操作表之前,首先必须设置像素格式。像素格式含有设备绘图界面的属性,这些属性包括绘图界面是用RGBA
模式还是颜色表模式,像素缓存是用单缓存还是双缓存,以及颜色位数、深度缓存和模板缓存所用的位数,还有其它一些属性信息。
像素格式结构
每个OpenGL
显示设备都支持一种指定的像素格式。一般用一个名为PIXELFORMATDESCRIPTOR
的结构来表示某个特殊的像素格式,这个结构包含26
个属性信息。Win32
定义PIXELFORMATDESCRIPTOR
如下所示:
typedef struct tagPIXELFORMATDESCRIPTOR
{
WORD nSize;
WORD nVersion;
DWORD dwFlags;
BYTE iPixelType;
BYTE cColorBits;
BYTE cRedBits;
BYTE cRedShift;
BYTE cGreenBits;
BYTE cGreenShift;
BYTE cBlueBits;
BYTE cBlueShift;
BYTE cAlphaBits;
BYTE cAlphaShift;
BYTE cAccumBits;
BYTE cAccumRedBits;
BYTE cAccumGreenBits;
BYTE cAccumBlueBits;
BYTE cAccumAlphaBits;
BYTE cDepthBits;
BYTE cStencilBits;
BYTE cAuxBuffers;
BYTE iLayerType;
BYTE bReserved;
DWORD dwLayerMask;
DWORD dwVisibleMask;
DWORD dwDamageMask;
} PIXELFORMATDESCRIPTOR;
好,了解了这些,我们开始写MFC
代码,首先产生一个SDI
的MFC
程序,然后必须引入OpenGL
的库,方法与前例相同(这些都不难),然后在StdAfx.h
中键入下列的代码:
#include <gl/gl.h>
#include <gl/glu.h>
#include <gl/glaux.h>
这样我们就可以在程序的任何地方都使用OpenGL
的函数了。
我新建的SDI
项目名为COpenGLDemo
,在CCOpenGLDemoView
类中添加如下方法和变量:
public:
BOOL InitOpenGL(CDC* pDC);
void SetLogicalPalette();
BOOL SetupPixelFormat();
BOOL RenderScene();
private:
HPALETTE m_hPalette;
CDC* m_pDC;
HGLRC m_hRC;
添加完以后,我们必须修改SDI
的窗口类型,使之与OpenGL
相匹配,这个更改是在窗口产生以前,代码如下(灰色部分):
BOOL CCOpenGLDemoView::PreCreateWindow(CREATESTRUCT& cs)
{
// TODO: Modify the Window class or styles here by modifying
// the CREATESTRUCT cs
cs.style|=WS_CLIPSIBLINGS|WS_CLIPCHILDREN;
return CView::PreCreateWindow(cs);
}
然后该在OnCreate
里面进行初始化了,其实这个初始化在OnInitialUpdate
中实现也可以,我们就选择前者。添加一个WM_Create
的响应函数(这个要是不会的话,您该先看看MFC
再来),然后键入代码:
int CCOpenGLDemoView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CView::OnCreate(lpCreateStruct) == -1)
return -1;
// TODO: Add your specialized creation code here
//
产生一个
DC
m_pDC=new CClientDC(this);
//
使用窗口
DC
来设置
RC
InitOpenGL(m_pDC);
//
产生一个
Timer
进程,程序每
20ms
就产生一个
WM_TIMER
事件,作用后面讲
SetTimer(0,20,NULL);
return 0;
}
上面的代码中我们使用了InitOpenGL
函数来对OpenGL
进行初始化,其函数具体实现如下:
BOOL CCOpenGLDemoView::InitOpenGL(CDC *pDC)
{
m_pDC=pDC;
//
设置像素格式
SetupPixelFormat();
//
产生一个
RC
m_hRC::wglCreateContext(m_pDC->GetSafeHdc());
//
设置该
RC
为当前使用的
RC
::wglMakeCurrent(m_pDC->GetSafeHdc(),m_hRC);
return TRUE;
}
注意上面使用了SetupPixelFormat
函数来设置OS
的像素格式,该函数详细为:
BOOL CCOpenGLDemoView::SetupPixelFormat()
{
PIXELFORMATDESCRIPTOR *pfd=new PIXELFORMATDESCRIPTOR();
pfd->nSize=sizeof(PIXELFORMATDESCRIPTOR);
pfd->nVersion=1;
pfd->dwFlags=PFD_DRAW_TO_WINDOW|
PFD_SUPPORT_OPENGL|
PFD_DOUBLEBUFFER|
PFD_STEREO_DONTCARE;
pfd->iPixelType=PFD_TYPE_RGBA;
pfd->cColorBits=32;
pfd->cRedBits=8;
pfd->cRedShift=16;
pfd->cGreenBits=8;
pfd->cGreenShift=8;
pfd->cBlueBits=8;
pfd->cBlueShift=0;
pfd->cAlphaBits=0;
pfd->cAlphaShift=0;
pfd->cAccumBits=64;
pfd->cAccumRedBits=16;
pfd->cAccumGreenBits=16;
pfd->cAccumBlueBits=16;
pfd->cAccumAlphaBits=0;
pfd->cDepthBits=32;
pfd->cStencilBits=8;
pfd->cAuxBuffers=0;
pfd->iLayerType=PFD_MAIN_PLANE;
pfd->bReserved=0;
pfd->dwLayerMask=0;
pfd->dwVisibleMask=0;
pfd->dwDamageMask=0;
//
选择一个像素索引
int m_GLPixelIndex=::ChoosePixelFormat(m_pDC->GetSafeHdc(),pfd);
//
设置像素索引
::SetPixelFormat(m_pDC->GetSafeHdc(),m_GLPixelIndex,pfd);
if (pfd->dwFlags&PFD_NEED_PALETTE)
{
SetLogicalPalette(); //
有必要的话设置逻辑调色板
}
return TRUE;
}
好长一个函数啊,不过主要是一个结构体的设置,关于这个结构体的具体属性,请大家自己去查阅,在设置了一个结构体后,我们选择了一个像素索引并设置为当前像素结构。至于逻辑调色板,其代码如下:
void CCOpenGLDemoView::SetLogicalPalette()
{
struct
{
WORD Version;
WORD NumberOfEntries;
PALETTEENTRY aEntries[256];
} logicalPalette={0x300,256};