|  | 
 
| 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++6.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 &
  FD-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.peRed=
 
 ComponentFromlndex(i,pfd.cRedBits,pfd .cRedShift);
 
 pPal->palPalEnty.peGreen=
 
 ComponentFromlndex(i,PFd,cCREENbits,pfd.cGreenShift);
 
 pPal->palPalEnty.peBlue=
 
 ComponentFromlndex(i,pfd,cBlueBits,pfd.cBlueShift);
 
 pPal->palPalEntry.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;
 
 }
 | 
 |