(1) OpenGL简介
作者: pioneer
OpenGL是近几年发展起来的一个性能卓越的三维图形标准,它是在SGI等多家世界闻名的计算机公司的倡导下,以SGI的GL三维图形库为基础制定的一个通用共享的开放式三维图形标准。目前,包括Microsoft、SGI、IBM、DEC、SUN、HP等大公司都采用了OpenGL做为三维图形标准,许多软件厂商也纷纷以OpenGL为基础开发出自己的产品,其中比较著名的产品包括动画制作软件Soft Image和3D Studio MAX、仿真软件Open Inventor、VR软件World Tool Kit、CAM软件ProEngineer、GIS软ARC/INFO等等。值得一提的是,随着Microsoft公司在Windows NT和最新的Windows 95中提供了OpenGL标准及OpenGL三维图形加速卡(如北京黎明电子技术公司的AGC-3D系列三维图形加速卡)的推出,OpenGL将在微机中有广泛地应用,同时也为广大用户提供了在微机上使用以前只能在高性能图形工作站上运行的各种软件的机会。
(2)OpenGL特点及功能
OpenGL实际上是一个开放的三维图形软件包,它独立于窗口系统和操作系统,以它为基础开发的应用程序可以十分方便地在各种平台间移植;OpenGL可以与Visual C++紧密接口,便于实现机械手的有关计算和图形算法,可保证算法的正确性和可靠性;OpenGL使用简便,效率高。它具有七大功能:
1) 建模 OpenGL图形库除了提供基本的点、线、多边形的绘制函数外,还提供了复杂的三维物体(球、锥、多面体、茶壶等)以及复杂曲线和曲面(如Bezier、Nurbs等曲线或曲面)绘制函数。
2) 变换 OpenGL图形库的变换包括基本变换和投影变换。基本变换有平移、 旋转、变比镜像四种变换,投影变换有平行投影(又称正射投影)和透 视投影两种变换。其变换方法与机器人运动学中的坐标变换方法完全一致,有利于减少算法的运行时间,提高三维图形的显示速度。
3) 颜色模式设置 OpenGL颜色模式有两种,即RGBA模式和颜色索引(Color Index)。
4) 光照和材质设置 OpenGL光有辐射光(Emitted Light)、环境光(Ambient Light)、漫反射光(Diffuse Light)和镜面光(Specular Light)。材质是用光反射率来表示。场景(Scene)中物体最终反映到人眼的颜色是光 的红绿蓝分量与材质红绿蓝分量的反射率相乘后形成的颜色。
5) 纹理映射(Texture Mapping):利用OpenGL纹理映射功能可以十分逼真地表达物体表面细节。
6) 位图显示和图象增强:图象功能除了基本的拷贝和像素读写外,还提供融合(Blending)、反走样(Antialiasing)和雾(fog)的特殊图象效果处理。以上三条可是被仿真物更具真实感,增强图形显示的效果。
7) 双缓存(Double Buffering)动画:双缓存即前台缓存和后台缓存,简而言之,后台缓存计算场景、生成画面,前台缓存显示后台缓存已画好的画面。此外,利用OpenGL还能实现深度暗示(Depth Cue)、运动模糊(Motion Blur)等特殊效果。从而实现了消隐算法。
(3)OpenGL for Windows 95图形库
OpenGL图形库一共有100多个函数。其中核心函数有115个,它们是最基本的函数,其前缀是gl,OpenGL实用库(OpenGL utility library ,GLU)的函数功能更高一些,如绘制复杂的曲线曲面、高级坐标变换、多边形分割等,共有43个,前缀为glu;OpenGL辅助库(OpenGL auxiliarylibrary ,GLAUX)的函数是一些特殊的函数,包括简单的窗口管理、输入事件处理、某些复杂三维物体绘制等函数,共有31个,前缀为aux。
此外,还有六个WGL函数非常重要,专门用于OpenGL和Windows 95窗口系统的联接,其前缀为wgl,主要用于创建和选择图形操作描述表(renderingcontexts)以及在窗口内任一位置显示字符位图。这些功能是Windows 95对OpenGL的唯一补充。
另外,还有五个Win32函数用来处理像素格式(pixel formats)和双缓存。由于它们是对Win32系统的扩展,因此不能应用在其它OpenGL平台上。
(4)OpenGL for Windows 95程序设计
OpenGL for Windows 95的设计与OpenGL for UNIX的程序设计有一点小
区别,关键就在于如何将OpenGL与不同的操作系统下的窗口系统联系起来。
如果调用OpenGL辅助库窗口管理函数,则不用考虑这些问题。下面简要介绍
在Windows 95下OpenGL的程序设计关键。
1.图形操作描述
在Windows 95下窗口程序必须首先处理设备描述表(Device Contexts ,DC),DC包括许多如何在窗口上显示图形的信息,既指定画笔和刷子的颜色,设置绘图模式、调色板、映射模式以及其它图形属性。同样,OpenGL for Windows95的程序也必须使用DC,这与其它Windows 95程序类似。但是,OpenGL forWindows 95必须处理特殊的DC图形操作描述表,这是DC中专为OpenGL使用的一种。一个OpenGL应用图形操作描述表内有OpenGL与Windows 95窗口系统相关的各种信息。一个OpenGL应用首先必须创建一个图形操作描述表,然后再启动它,最后在所定义的窗口内按常规方式调用OpenGL函数绘制图形。
一个图形操作描述表不同于其它DC,它们调用每个GDI函数都需要一个句柄,而图形操作描述表方式下只需一个句柄就可以任意调用OpenGL函数。也就是说,只要当前启用了某个图形操作描述表,那么在未删除图形操作描述表之前可以调用任何OpenGL函数,进行各种操作。
2.像素格式
在创建一个图形操作表之前,首先必须设置像素格式。像素格式含有设备绘图界面的属性,这些属性包括绘图界面是用RGBA模式还是颜色表模式,像素缓存是用单缓存还是双缓存,以及颜色位数、深度缓存和模板缓存所用的位数,还有其它一些属性信息。
3.像素格式结构
每个OpenGL显示设备都支持一种指定的像素格式。一般用一个名为PIXELFORMATDESCRIPTOR的结构来表示某个特殊的像素格式,这个结构包含26个属性信息。Win32定义PIXELFORMATDESCRIPTOR如下所示:
typedef struct tagPIXELFORMATDESCRIPTOR
{ // pfd
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;
4.初始化PIXELFORMATDESCRIPTOR结构
PIXELFORMATDESCRIPTOR中每个变量值的具体含义和设置可以参考有关资料,下面举出一个PIXELFORMATDESCRIPTOR初始化例子来简要说明相关变量的意义。定义PIXELFORMATDESCRIPTOR结构的pfd如下:
PIXELFORMATDESCRIPTOR pfd = {
sizeof(PIXELFORMATDESCRIPTOR), // size of this pfd
1,
PFD_DRAW_TO_WINDOW | // support window
PFD_SUPPORT_OPENGL | // support OpenGL
PFD_DOUBLEBUFFER, // double buffered
PFD_TYPE_RGBA, // RGBA type
24, // 24-bit color depth
0, 0, 0, 0, 0, 0, // color bits ignored
0, // no alpha buffer
0, // shift bit ignored
0, // no accumulation buff
0, 0, 0, 0, // accum bits ignored
32, // 32-bit z-buffer
0, // no stencil buffer
0, // no auxiliary buffer
PFD_MAIN_PLANE, // main layer
0, // reserved
0, 0, 0 // layer masks ignored
};
在这个结构里,前两个变量的含义十分明显。第三个变量dwFlags的值是PFD_DRAW_TO_WINDOW |PFD_SUPPORT_OPENGL|PFD_DOUBLEBUFFER,表明应用程序使用OpenGL函数来绘制窗口,第四个表明当前采用RGBA颜色模式,第五个采用24位真彩色,既1.67千万种颜色,如果是256色系统则自动实现颜色抖动;因为没有使用alpha缓存和累计缓存,所以从变量cAlphaBits到cAccumAlphaBits都设置为0;深度缓存设置为32位,这个缓存能解决三维场景的消隐问题;变量cAuxBuffers设置为0,在Windows 95下不支持辅助缓存;Windows 95下针对OpenGL变量ilayerType只能设置为PFD_MAIN_PLANE,但在其它平台也许支持PFD_MAIN_PLANE或PFD_MAIN_UNDERLAYPLANE;接下来bReserved变量只能设为0,而最后三个变量Windows 95都不支持,故全设置为0。
5.设置像素结构
当初始化PIXELFORMATDESCRIPTOR结构后,就要设置像素格式。下面举例说明如何设置像素格式。
CClientDC clientDC(this);
int PixelFormat = ChoosePixelFormat(clientDC.m_hDC,&pfd);
BOOL result=SetPixelFormat(clientDC.m_hDC,PixelFormat,&pfd);
第一行语句说明得到一个应用窗口客户区的设置描述表。
第一行调用ChoosePixelFormat()选择一个像素格式,并将像素格式索引号返回给pixelFormat变量;函数中第一个参数是选择像素格式的设备描述表的句柄,第二个参数是PIXELFORMATDESCRIPTOR结构的地址。如果调用失败则返回0;否则返回像素格式索引号。
第三行调用SetPixelFormat()设置像素格式,三个参数分别是设备描述表的句柄、像素格式索引号和PIXELFORMATDESCRIPTOR结构的地址。如果调用成功则返回TRUE,否则返回FALSE。
6.创建图形操作描述表
正如前所述,必须创建图形操作描述表并启用它后,才能调用OpenGL函数在窗口内进行各种图形操作。一般来说,利用MFC中增补的管理图形操作描述表方法来编程比较方便。即在视类(CView)的消息OnCreat()中创建图形操作描述表。
OpenGL中调色板的使用
作者:kerne
OpenGL是由SGI公司开发的一套3D图形软件接口标准,由于具有体系结构简单合理、使用方便、与操作平台无关等优点,OpenGL迅速成为一种3D图形接口的工业标准,并陆续在各种平台上得以实现。微软公司在推出WindowsNT3.5时便开始把OpenGL集成到NT操作系统之中,96年下半年又在Windows95OSR2中加入OpenGL,使得低端用户也能够充分享受到OpenGL带来的高质量3D图形效果。
基于OpenGL的应用程序有两种类型。一种是利用OpenGL辅助库来完成一些简单的图形显示,这种应用程序编写起来非常简单,但其功能十分有限,因此只适合于演示OpenGL函数的用法等任务。第二种是与VC++等这样功能强大的开发工具相结合,从而完成比较复杂的任务。本文所要讨论的就是以MFC类库为基础,使用OpenGL时所涉及到的关于调色板的问题。
1、Windows下的调色板
OpenGL可以使用16色、256色、64K和16M真彩色。真彩模式下不需要调色板,而在16色模式下根本不可能得到较为满意的效果,因此对OpenGL而言,调色板只有在256色模式下才有意义。
我们知道,Windows把调色板分为系统调色板和逻辑调色板。每个应用程序都拥有一套自己的逻辑调色板(或使用缺省调色板),当该应用程序拥有键盘输入焦点时可以最多使用从16M种色彩中选取的256种颜色(20种系统保留颜色和236种自由选取的颜色),而失去焦点的应用程序可能会有某些颜色显示不正常。系统调色板由Windows内核来管理,它是由系统保留的20种颜色和经仲裁后各个应用程序设置的颜色组成,并与硬件的256个调色板相对应。应用程序的逻辑调色板与硬件的调色板没有直接的对应关系,而是按照最小误差的原则映射到系统调色板中,因此即使应用程序自由选取256种不同颜色构成自己的逻辑调色板,也有可能某些颜色显示到屏幕上时是一样的。
当应用程序的窗口接收到键盘输入焦点时,Windows会向它发送一条WM—QUERYNEWPALETTE消息,让它设置自己的逻辑调色板,此时Windows会在系统调色板中尽量多地加入该应用程序需要的颜色,并生成相应的映射关系。接着Windows会向系统中所有的覆盖型窗口和顶级窗口(包括拥有键盘输入焦点的窗口)发送一条WM—PALETTECHANGED消息,让它们设置逻辑调色板和重绘客户区,以便能更充分地利用系统调色板,已拥有键盘输入焦点的窗口不应再处理这条消息,以避免出现死循环。
2、OpenGL的颜色表示与转换
OpenGL内部用浮点数来表示和处理颜色,红绿蓝和Alpha值这四种成份每种的最大值为1.0,最小值为0.0。在256色模式下,OpenGL把一个象素颜色的内部值按线性关系转换为8比特来输出到屏幕上,其中红色占最低位的3比特,绿色占中间的3比特,蓝色占最高位的2比特,Windows将这个8比特值看作逻辑调色板的索引值。例如OpenGL的颜色值(1.0,0.14,0.6667)经过转换后二进制值为10001111(红色为111,绿色为001,蓝色为10),即第143号调色板,该调色板指定的颜色的RGB值应与(1.0,0.14,0.6667)有相同的比率,为(255,36,170),如果不是该值,那么显示出来的颜色就会有误差。
3、调色板的生成算法
很明显,OpenGL输出的8比特值中直接表明了颜色的组成,为了使图形显示正常,我们应以线性关系来设置逻辑调色板,使其索引值直接表明颜色的组成。因此生成调色板时,把索引值从低位到高位分成3-3-2共三个部分,将每一部分映射到0-255中去,这
样3比特映射为{0,36,73,109,146,182,219,255},2比特映射为{0,85,170,255},最后把三部分组合起来成为一种颜色。
经过上面的处理后,256种颜色均匀分布在颜色空间中,并没有完全包含系统保留的20种颜色(只包含了7种),这意味着将会有数种颜色显示成一样,从而影响效果。一个较好的解决办法是按照最小均方误差的原则把13种系统颜色纳入到逻辑调色板中,具体的对应关系请参见后面的例子。
从原理上来说,并非一定要使用线性映射,还可以用其它一些映射关系,如加入Gamma校正以便更能符合人眼的视觉特性,不过这些映射关系应用得并不广泛,在此不再讨论。
4、MFC应用程序中设置调色板的例子
下面以一个简单的MFC应用程序为例来说明设置调色板的方法,使用的开发工具为VisualC++5.0。
1)使用AppWizard生成一个拥有缺省选项的应用程序Sample,并在Project\Settings...\Link属性页中添加库模块:opengl32.lib、glu32.lib和glaux.lib。
2)利用ClassWizard为CMainFrame添加处理WM—QUERYNEWPALETTE和WM—PALETTECHANGED两条消息的函数,并在函数体添加如下内容:
voidCMainFrame::OnPaletteChanged(CWnd*pFocusWnd){
CView*pView=GetActiveView();//取得活动的视
//把消息传递给活动视的消息处理函数
if(pView)pView->SendMessage(WM—PALETTECHANGED,
(WPARAM)(pFocusWnd->GetSafeHwnd()),
(LPARAM)0);
}
BOOLCMainFrame::OnQueryNewPalette(){
CView*pView=GetActiveView();
if(pView)returnpView->SendMessage(WM—QUERYNEW
PALETTE,
(WPARAM)0,(LPARAM)0);
returnFALSE;
}
3)利用ClassWizard为CSampleView加入处理WM—QUERYNEWPALETTE和WM—PALETTECHANGED两条消息的函数,其中内容如下:
voidCSampleView::OnPaletteChanged(CWnd*pFocusWnd){
//如果自己拥有输入焦点,那么不处理这条消息
if(pFocusWnd!=this)OnQueryNewPalette();
}
BOOLCSampleView::OnQueryNewPalette(){
if(m—pPal){
CDC*pDC=GetDC();
CPalette*pOldPal=pDC->SelectPalette(m—pPal,FALSE);
UINTu=pDC->RealizePalette();
ReleaseDC(pDC);
//某些颜色改变了,需要重画客户区
if(u!=0)InvalidateRect(NULL,TRUE);
}
returnTRUE;
}
4)在SampleView.h中添加下列公有成员变量和函数的声明:
CPalette*m—pPal;
HGLRCm—hrc;
staticunsignedcharm—oneto8[2];
staticunsignedcharm—twoto8[4];//2比特转换数组
staticunsignedcharm—threeto8[8];//3比特转换数组
staticintm—defaultOverride[13];//需处理的13种系统保留颜色
staticPALETTEENTRYm—defaultPalEntry[20];//系统保留颜色
BOOLCreateRGBPalette(HDChDC);
unsignedcharComponentFromIndex(inti,UINTnbits,
UINTshift);
并在SampleView.cpp中加入下列代码:
unsignedcharCSampleView::m—threeto8[8]={
0,0111>>1,0222>>1,0333>>1,0444>>1,0555>>1,0666>>1,
0377};
unsignedcharCSampleView::m—twoto8[4]={
0,0x55,0xaa,0xff};
unsignedcharCSampleView::m—oneto8[2]={0,255};
//按最小均方误差计算出的需用系统保留颜色替换的索引号
intCSampleView::m—defaultOverride[13]={
0,3,24,27,64,67,88,173,181,236,247,164,91};
//20种系统保留颜色值
PALETTEENTRY CSample View::m-defaultp alEntu[20]={
{0,0, 0, 0,},{0x80, 0, 0, 0},{0, 0x80,0, 0},
{0x80,0x80,0, 0},{0,0, 0x80, 0},{ 0x80, 0, 0x80, 0},
{ 0, 0x80, 0x80,0},{0xC0,0xC0,0xC0,0},{192,220,192,0}
{166,202,240,0},{255,251,240,0},{160,160,164,0}
{0x80,0x80,0x80, 0},{0xFF,0,0,0},{0xFF,0, 0xFF,}
{0, 0xFF,0xFF,O},{0xFF,0xFF,0xFF,0} };
unsigned char CSanple V iew :: ComponentFromlndex(int
i,UINT nbits,
UINT shift){
unsigned char val;
val=(unsigned char)(i>>shift);
switch (nbits){
case 1: val &=0x1;
RETURN M-ONETO8[VAL]
case 2: val &=0x3; return m-twoto8[val]
case 3: val &=0x7;
return m-threeto8[val];
default :return0;
}
}
BOOL CSample View ::CreateRGBPalette (HDC
hDC){PIXELFORMATDESCRIPTOR pfd;
int n=GetPixelFormat(hDC);
DescribepixxelFornt(hDC, n, sizeof
(PIXELFORMATDESCRIPTOR),&pfd);
//判断是否需要调色板,真彩模式下不需要
if (!(pfd.dwflags &PFD-NEED-PALETTE))return FALSE;
LOGPALETTE*pPAL=(LOGPALETTE*)malloc (sizeof (logpalette)
+256*sizeof(PALETTEENTRY));
if (!pPal)return FALSE;//内存不足返回
pPal->palVersion=0x300;
pPal->palNumEntries=256;//逻辑调色板数量
n=1<<pfd.cColorBits;
for(int i =0; i<n; i++){
pPal->palPalEnty[i].peRed=
ComponentFromlndex(i,pfd.cRedBits,pfd .cRedShift);
pPal->palPalEnty[i].peGreen=
ComponentFromlndex(i,PFd,cCREENbits,pfd.cGreenShift);
pPal->palPalEnty[i].peBlue=
ComponentFromlndex(i,pfd,cBlueBits,pfd.cBlueShift);
pPal->palPalEntry[i].peFlags=0;
}
//插入13种系统保留颜色
if ((pfd.cColorBits==8) & &
(pfd,cRedBits = =3)& &(pfd,cRedShift = =0)& &
(pfd,cCreenBits= =3)& &(pfd,cCreenBits= =3)& &
(pfd,cBlueBits= =2)& &(pfd,cBlueBits= =6)){
for (int j =1; j <=12; j ++
pPal->palPalEntry[m-defaulrOverride[j]]=m-default-
palEntry[j];
}
if(m-pPal)delete m-pPal;
m-pPal =new CPalette;
BOOL bResult =m-pPal->CreatePalette(pPal);
free(pPal );
return bResult;
}
5)修改CSamplsView::OnCreate()函数:
int CSample View::OnCreate(lpCreateStruct)= =-1)
return -1;CClientDC dc(this)
//填充象素格式描述符,请参考联机帮助
PLXELFORMATDESCRIPTOR ;
memset (&pfd ,0,sixeof(PLXELFORMATDESCRIPTOR));
pfd .nSize=sizeof(PIXELFORMATDESCRIPTOR);
pfd.nVersion =1;
pfd,dwflags=PFD=DOUBLEBUFFER.1 PFD-SUPPORT-OPENGL
1 PFD-DRAW-TO-WINDOW;
pfd,ipixlType=PFD-TYPE-RGBA;
pfd.cColorBits=24;
pfd,cColorBits=32;
pfd,iLayerType=PED-MAIN -PLANE;
int nPixlformat=ChoosePixelFormat(dc.m-
hDC,nPixelFormat,&pfd);
if(!bResult ) return-1;
//创建rendering context.
m-hrc=wglCreateContext(dc,m-hDC);
if(!m-hrc)return-1;
//创建逻辑调色板
CreateRGBPalette(dc.m-hDC);
return 0;
}
此外还要修改OnSize、OnDraw和preCreateWindow等函数,此处不
再述,读者可以从联机帮助中找到。