OpenGL DC RC关系

1.Windows操作系统对OpenGL的支持 

    具有Windows编程经验的人都知道,在Windows下用GDI作图必须通过设备上下文(DeviceContext简写DC)调用相应的函数;用OpenGL作图也是类似,OpenGL函数是通过"渲染上下文"(RenderingContext简写RC)完成三维图形的绘制。Windows下的窗口和设备上下文支持"位图格式"(PIXELFORMAT)属性,和RC有着位图结构上的一致。只要在创建RC时与一个DC建立联系(RC也只能通过已经建立了位图格式的DC来创建),OpenGL的函数就可以通过RC对应的DC画到相应的显示设备上。创建RC的过程如下:

  • 获取一个DC
  • 选择并且设置DC的像素格式
  • 通过API:wglCreateContext(DC)创建一个RC
  • 设置当前的RC,通过API:wglMakeCurrent(RC, DC)来设置.

实现释放或者选择其它RC:  

  • 调用API:wglMakeCurrent(NULL, NULL)使DC不再有RC。
  • 调用API:wglMakeCurrent(AnotherRC, DC)选择其他RC为当前RC,一般情况下,我们都是一个RC对应一个DC,当然,在某些场合里面是可以有多个RC。
  • 调用API:wglDeleteContent(RC) 直接删除,这个方法比较直截了当。

        这里还有以下需要注意的方面:


     1.一个线程只能拥有一个渲染上下文(RC),也就是说,用户如果在一个线程内对不同设备作图,只能通过更换与RC对应的DC来完成,而RC在线程中保持不变。(当然,删除旧的RC后再创建新的是可以的)与此对应,一个RC也只能属于一个线程,不能被不同线程同时共享。 

    2.设定DC位图格式等于设定了相应的窗口的位图格式,并且DC和窗口的位图格式一旦确定就不能再改变。这一点只能期望以后的Windows版本改进了。 

    3.一个RC虽然可以更换DC,在任何时刻只能利用一个DC(这个DC称为RC的当前DC),但由于一个窗口可以让多个DC作图从而可以让多个线程利用多个RC在该窗口上执行OpenGL操作。 

    4.现在的Windows下的OpenGL版本对OpenGL和GDI在同一个DC上作图有一定的限制。当使用双缓存用OpenGL产生动画时,不能使用GDI函数向该DC作图。 

    5.不建议用ANSIC在Windows下编写OpenGL程序。这样的程序虽然具有跨平台的可移植性(比如很多SGI的例子程序),但是它们不能利用 Windows操作系统的很多特性,实用价值不大。 

2.用VC来编写OpenGL程序 

经过上面的分析,用VC来调用OpenGL作图的方法就很显然了。步骤如下: 

   1.先设置显示设备DC的位图格式(PIXELFORMAT)属性。这通过填充一个PIXELFORMATDESCRIPTOR的结构来完成(关于PIXELFORMATDESCRIPTOR中 各项数据的意义,请参照VC的帮助信息),该结构决定了OpenGL作图的物理设备的属性,比如该结构中的数据项dwFlags中PFD_DOUBLEBUFFER位如果没有设置(置1),通过该设备的DC上作图的OpenGL命令就不可能使用双缓冲来做动画。有一些位图格式(PIXELFORMAT)是DC支持的,而有一些DC就不支持了。所以程序必须先用ChoosePixelFormat来选择DC所支持的与指定位图格式最接近的位图格式,然后用SetPixelFormat设置DC的位图格式。 

   2.利用刚才的设备DC建立渲染上下文RC(wglCreateContext),使得RC与DC建立联系(wglMakeCurrent)。 

   3.调用OpenGL函数作图。由于线程与RC一一对应,OpenGL函数的参数中都不指明本线程RC的句柄(handle) 

   4.作图完毕以后,先通过置当前线程的RC为NULL(::wglMakeCurrent(NULL,NULL);),断开当前线程和该渲染上下文的联系,由此断开与DC的联系。此时RC句柄的有效性在微软自己的文档中也没有讲清楚,所以在后面删除RC的时候要先判断以下RC句柄的有效性(f(m_hrc)::wglDeleteContext(m_hrc);)。再根据情况释放(ReleaseDC)或者删除(DeleteDC)DC 。

3.一些说明 

   1.一旦设定了一个DC的位图格式,该DC所联系的窗口的位图格式随之设定。该窗口若含有子窗口或者有兄弟窗口,这些兄弟/子窗口的位图格式没有设成与对应RC一致的格式,OpenGL在它们上面作图就容易出错。故而OpenGL作图的窗口必须具有WS_CLIPCHILDREN和WS_CLIPSIBLINGS风格,程序中在主框窗的构造函数中用LoadFrame(IDR_MAINFRAME,WS_OVERLAPPEDWINDOW|WS_CLIPCHILDREN|WS_CLIPSIBLINGS,NULL,NULL);指定了主窗口的风格。 

   2.在ANSIC的OpenGL编程中,由auxReshapeFunc定义设置OpenGL视口大小和作图尺寸的回调函数。在MFC中应该由WM_SIZ消息的处理函数来完成。在ANSIC的OpenGL编程中,由EauxMainLoop定义作图的回调函数。在MFC中应该由WM_PAINT消息的处理函数来处理。相应的,由OpenGL定义的键盘、鼠标处理函数都应该由相应的Windows处理函数来响应。 

   3.OpenGL自己有刷新背景的函数glClear,故而应禁止Windows刷新窗口背景。否则,当窗口需要重画时,Windows会自动先发送WM_ERASEBKGND ,而缺省的处理函数使用白色的背景刷。当OpenGL使用的背景颜色不是白色时,作图时有一帧白色的闪烁。这种现象在做动画时特别明显。程序中只需要在WM_ERASEBKGND的消息处理函数中禁止父窗口类的消息处理,简单的返回一个TRUE即可。 

   4.由于OpenGL的跨平台性,它必须用操作系统的调色板。所以如果GL_INDEX_MODE作图时,必须用VC自己定义调色板。不过一般情况下,用GL_RGBA_MODE模式比较方便,很少用到GL_INDEX_MODE模式。 

   5.在OpenGL作图期间,RC对应的DC不能删除或者释放。 

   6.由于OpenGL作图时需要长时间占用DC,所以最好把作图窗口类设成CS_OWNDC。MFC缺省的窗口类风格中没有设这一属性,程序中在主窗口C++ 类的PreCreateWindow方法中自己注册了一个窗口类,除了设定了CS_OWNDC属性以外,还设定了CS_HREDRAW、CS_VREDRAW和CS_SAVEBITS。设定CS_HREDRAW、CS_VREDRAW是为了让窗口缩放时产生WM_PAINT消息,修正OpenGL视口和作图尺寸;由于OpenGL作图需要很多计算,设定CS_SAVEBITS是为了在OpenGL窗口被遮盖后显现出来时,不产生WM_PAINT消息,用内存存储的图象来填充,从而用空间消耗换取计算时间。 

   7.本程序中没有对OpenGL函数的出错情况作出处理。OpenGL出错后返回错误码,不会抛出异常;而且在某一个函数出错以后,后继函数也一般不会出现异常,只是返回错误码,一不小心就可能忽略某些错误。而对每一个OpenGL函数都做出错与否的判断比较麻烦,所以编程序时对OpenGL的函数应当非常小心。

4.像素格式


  像素格式是OpenGL窗口的重要属性,它包括是否使用双缓冲,颜色位数和类型以及深度位数等。像素格式可由Windows系统定义的所谓像素格式描述子结构来定义(PIXELFORMATDESCRIPTOR),该结构定义在windows.h中。
  在该结构中包含有26个属性信息,其形式为:
  typedef struct tagPIXELFORMATDESCRIPTOR
  {
  WORD nSize;
  WORD nVersion;
  DWORD dwFlags;         //如果使用了双缓冲,则需要在绘制代码结束后使用SwapBuffers(HDC hDc)换页
  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;
  各变量的含义如下:
  nSize:该结构所占内存空间。
  nVersion:版本号,当前为1。
  dwFlags:指定像素格式属性,可选参量如表1.1所示。

   像素格式属性
  标识符 解释
  PFD_DRAW_TO_BITMAP 支持内存中绘制位图
  PFD_DRAW_TO_WINDOW 支持屏幕绘图
  PFD_DOUBLEBUFFER 支持双缓冲
  PFD_CENERIC_FORMAT 指定选择GDI支持的像素格式
  PFD_NEED_PALETTE 指定需要逻辑调色板
  PFD_NEED_SYSTEM_PALETTE 指定需要硬件调色板
  PFD_STEREO NT不支持
  PFD_SUPPORT_OPENGL 支持OpenGL
  PFD_SUPPORT_GDI 支持GDI,此时不可使用PFD_DOUBLEBUFFER
  iPixelType:像素颜色模式,可选项为PFD_TYPE_RGBA或PFD_TYPE_INDEX,分别对应于RGBA模式和颜色索引模式。
  cColorBits:指定颜色的位数。
  cRedBits:采用RGBA模式时,红色组分占用位数。
  cRedShift:采用RGBA模式时,红色组分偏移量。
  cGreenBits:采用RGBA模式时,绿色组分占用位数。
  cGreenShift:采用RGBA模式时,绿色组分偏移量。
  cBlueBits:采用RGBA模式时,蓝色组分占用位数。
  cBlueShift: 采用RGBA模式时,蓝色组分偏移量。
  cAlphaBits:采用RGBA模式时,Alpha组分占用位数。
  cAlphaShift:采用RGBA模式时,Alpha组分偏移量。
  cAccumBits:指定累积缓冲区表示一个像素所用位数。
  cAccumRedBits:指定累积缓冲区表示红色组分占用位数。
  cAccumGreenBits:指定累积缓冲区表示绿色组分占用位数。
  cAccumBlueBits:指定累积缓冲区表示蓝色组分占用位数。
  cAccumAlphaBits:指定累积缓冲区表示Alpha组分占用位数。
  cDepthBits:指定深度缓冲区表示一个像素所用位数。
  cStencilBits:指定模板缓冲区表示一个像素所用位数。
  cAuxBuffers:指定辅助缓冲区,Windows9x、NT不支持。
  iLayerType:Windows9x、NT下只能是PFD_MAIN_PLANE。
  bReserved:=0。
  dwLayerMask:指定覆盖层的屏蔽,Windows9x、NT不支持。
  dwVisibleMask:Windows9x、NT不支持。
  dwDamageMask:Windows9x、NT不支持。
  Windows提供了四个像素格式管理函数,分别介绍如下:
  (1) int ChoosePixelFormat(HDC hdc,
  PIXELFORMATDESCRIPTOR *ppdf)
  该函数比较传过来的像素格式描述和OpenGL支持的像素格式,返回一个最佳匹配的像素格式索引。该索引值可传给SetPixelFormat为DC设置像素格式。返回值为0表示失败。
  在比较像素格式时,匹配优先级顺序为像素格式描述子结构中的下述各域:
  dwFlags->cColorBits->cAlphaBits->cAccumBits
  ->cDepthBits->cStencilBits->cAuxBuffers->iLayerType
  硬件支持的像素格式优先。
  (2) int DescribePixelFormat(HDC hdc, int iPixelFormat, UINT nBytes,
  LPPIXELFORMATDESCRIPTOR *ppfd)
  该函数用格式索引iPixelFormat说明的像素格式来填写由ppfd所指向的像素格式描述子结构,利用该函数可以枚举像素格式。
  (3) int GetPixelFormat(HDC hdc)
  该函数用于获取hdc的格式索引。
  (4) BOOL SetPixelFormat(HDC hdc, int iPixelFormat,
  LPPIXELFORMATDESCRIPTOR *ppfd)
  该函数用格式索引iPixelFormat来设置hdc的像素格式。在使用该函数之前应该调用ChoosePixelFormat来获取像素格式索引。另外,OpenGL窗口风格必须包含WS_CLIPCHILDREN和WS_CLIPSIBLINGS类型,否则设置失败。
  应该注意的是ChoosePixelFormat函数并不一定返回一个最佳的像素格式值,可以利用DescribePixelFormat来枚举系统所支持的所有像素格式。OpenGL的通常支持24种不同的像素格式,如果系统安装了OpenGL硬件加速器,它可能会支持其它的像素格式

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值