Chinaunix首页 | 论坛 | 博客
  • 博客访问: 639992
  • 博文数量: 263
  • 博客积分: 6000
  • 博客等级: 准将
  • 技术积分: 2555
  • 用 户 组: 普通用户
  • 注册时间: 2008-02-26 11:20
文章分类

全部博文(263)

文章存档

2011年(10)

2010年(19)

2009年(170)

2008年(64)

我的朋友

分类: WINDOWS

2009-11-18 00:09:37


一、 概述

     OPenGL 是一个与硬件和窗口系统无关的图形库,在其标准库里提供了核心的3-D 图形指令,包括物体形状的描述、矩阵转换、光照、颜色、纹理、剪裁、位图等的处理函数。常见的材料比如红宝书里边为了使得讨论集中于图形学的概念,因而使用了辅助库glut 处理与操作系统的交互(比如创建窗口、键盘消息、客户区重绘)。这样做使得读者可以将注意力集中于图形学的概念、原理理解以及OpenGL 模型学习,同时也隐藏了OpenGL窗口系统 的接口部分。

 

     当需要开发一个有用的应用程序的时候,glut 中简化了的窗口系统难以胜任。另一方面专业的窗口系统则专注于窗口与操作系统以及用户的交互。因而将二者的长处结和起来的应用程序结构式比较合理的,比如使用MS windows 窗口系统处理界面工作,使用OpenGL 处理图形渲染工作。

本文假设读者具有MFC 的基本知识。

 

二、 OpenGL 指令的通道——渲染环境 

       事实证明处理复杂任务时采用分治与抽象的策略是科学的选择。把任务的划分为功能明确、相互关联的子任务,当各个子任务完成的时候总的任务就随之解决。这里功能明确 意味着各个子任务的功能并集组成了整体的任务;相互关联 意味着子任务之间需要一定的接口来进行信息传输。一般而言,子模块定义了信息的处理,而接口定义了信息的流动。

这样站在功能模块内部来看,其他部分只剩下一个抽象的接口。

 

      设置好OpenGLMFC 的接口以后,程序员就可以专注于图形的绘制,而将窗口维护以及用户交互的任务留给MFC 来做。具体地说是渲染环境和设备环境起到了这个作用。

三、 具体操作例子

  用一个多视图的MFC 程序为例来演示如何进行环境的搭建。

(1)    创建一个多视图的 MFC 应用程序框架。 比如命名为MFC_OpenGL_exp 。则正确创建以后,程序具有 6 个类分别是:

         CAboutDlg,

         CChildFrame,

         CMainFrame,

         CMFC_OpenGL_expApp,

         CMFC_OpenGL_expDoc,

         CMFC_OpenGL_View,

     运行结果如下图所示:

 

 

                                                             图一。 多文档MFC程序框架的运行结果

(1)   创建像素描述格式

    对这个框架而言,视图类是展现输出的载体。因而我们将对这个类进行改造。在视图类被创建的时候创建像素描述格式,并且设置到设备环境中的。 

  

    对 CMFC_OpenGL_expView 类的 WM_CREATE 消息进行响应,添加消息响应函数 OnCreate 。在该函数里边添加如下的代码:

  1. // TODO:  Add your specialized creation code here  
  2.    
  3.        //Step1 , Set the pixelformat descriptor  
  4.        PIXELFORMATDESCRIPTOR pfd = {  
  5.               sizeof(PIXELFORMATDESCRIPTOR),  
  6.               1,  //version  
  7. PFD_DRAW_TO_WINDOW| PFD_SUPPORT_OPENGL| PFD_DOUBLEBUFFER| PFD_STER   EO_DONTCARE,  
  8.    PFD_TYPE_RGBA,  
  9.    24,  
  10.    0,0,0,0,0,0,  
  11.    0, //op alpha buffer,  
  12.    0, //shift bit ignored  
  13.    0, //no accumulation buffer  
  14.    0,0,0,0,  
  15.    32, //32 bit z buffer  
  16.               0,  
  17.               0,  
  18.               PFD_MAIN_PLANE,  
  19.               0,  
  20.               0,0,0  
  21.        };  
  22.    
  23.        HDC hDC = ::GetDC(this->GetSafeHwnd());  
  24.        int indxPfd = ChoosePixelFormat(hDC, &pfd);  
  25.        if(!SetPixelFormat(hDC, indxPfd, &pfd)){  
  26.               MessageBox(TEXT("Fail to set pixelformat"));  
  27.       }  
 

 



     这一步通过 SetPixelFormat 函数设置需要的像素格式描述。像素描述是设备环境的一个包含部分,但是该函数的输入并非我们直接创建的像素描述(变量 pfd )。而是一个整形的索引。原因如下:

      每一个设备环境 (DC) 支持一定数量的像素格式,设备环境维护了一个描述列表来保存上述信息。程序创建一个像素格式描述 (PIXELFORMATDESCRIPTOR 型的变量 ) 以后,使用 ChoosePixelFormat 来与描述列表进行对照,该函数返回一个索引。该索引指向与制定像素格式最接近的一个描述。利用这个索引对设备环境进行设置。  

      也就是说,最终用来设置设备环境的像素格式的是设备环境本身维护的格式描述。这样做使得硬件免于遭受不安全的格式描述的骚扰,也可以使得程序员只关心与程序有关的描述。

      有关像素格式描述部分的描述在 MSDN 文档中的描述,参见

 

MSDN:win32 and COM development/graphics and multimedia/OpenGL/SDK document/OpenGL/ win32 extensions to openGL/ openGL on windows NT…/pixelformat

 

(2)   创建渲染环境,并且将渲染环境与当前的设备环境关联。

 

   OpenGL wgl*** 函数完成相应的功能。具体做法如下(在 OnCreate 中完成)


 

  第一个函数 wglCreateContext 创建一个与输入(设备环境)参数兼容渲染环境。

  第二个函数 wlgMakeCurrent 将输入(渲染环境)参数设置为当前的渲染环境。值得注意的是这两个函数的设备环境不一定相同,但是需要保证两个设备环境中的像素格式描述 (PFD) 是一样的。

  此外需要注意的是生成一个渲染环境是一个必将耗费资源的操作,因此为了保证系统的性能起见,最好在整个运行过程中只生成一次渲染环境。并且在系统退出的时候销毁。

  因此,具体编程的时候建议在视图类中加入一个 HGLRC 型的成员变量,用于保存生成的渲染环境,以便于程序退出的时候在类的析构函数里边释放。

 

(3)OpenGL 的设置

   从原理上讲,完成了以上的设置以后便完成了接口的设置,可以添加 WM_PAINT 消息的响应函数,在其中使用 OpenGL 进行绘图。但是还有一些问题需要注意:

    (1)窗口的变化

    众所周知,当窗口发生变化以后相应的客户区的参数会发生变化,进而引起客户区内容重新绘制。如果不考虑这一点,就会使得图形不能很好地响应窗口形状变化。因此,在 WM_SIZE 的消息响应函数里边必须对视口进行相应的调整。假设程序需要绘制的区域要填充整个客户区。那么相应的代码如下所示:

 



 



 

注意: i   glViewport 函数将绘制区域设置为整个窗口的客户区。

      ii  上述例子在 WM_SIZE 消息的响应中同样设置了投影方式。具体放在下一节

      

 (2) 鼠标响应

  MFC 可以得到以窗口为参照的鼠标位置,进而可以得到以客户区为参照的鼠标位置。根据视口的位置大小参数,可以得到以视口为参照的鼠标位置进而进行命中测试。


(4)   其他技巧

    完成以上的步骤以后,就能够在 WM_PAINT 的响应函数里边专心使用 OpenGL 画图。假如要画一个透视投影的正方体,背景为黑色。程序代码如下:

 


 

 

 


运行结果如下:


注意: 1 解决闪烁显现可以使用双缓存

第一步:在像素格式的 dwFlags 里边设置支持双缓存,具体可以参考 OnCreate 里边的设置。第二步:完成 OpenGL 设置以后,加入语句: glDrawBuffer(GL_BACK) 使得,绘图指令对后缓存进行操作。第三步:在每一个引起客户区无效需要重绘的地方 , 即显式使用 Invalidate ()的地方,将该函数的第二个参数设置为 FALSE 。取消 GDI 的绘制可能。

阅读(1047) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~