在VS中(单文档)配置和使用OpenGL

来源:互联网 发布:在线js代码编辑器 编辑:程序博客网 时间:2024/06/04 23:26

OpenGL(碎梦花事)

(参考《实战OpenGL三维可视化系统开发与源码精解》,吕希奎,周小平著)

一、OpenGL像素格式

1、像素格式设置

      像素格式是OpenGL窗口的重要属性,它包括是否使用双缓冲,颜色位数和类型以及深度位数等。像素格式可由Windows系统定义的所谓像素格式描述子结构来定义(PIXELFORMATDESCRIPTOR),该结构定义在windows.h中(与BMP头文件BITMAPINFOHEADER相似,在windows.h中,结构体不需要单独定义)。
在该结构中包含有26个属性信息,其形式为:
typedef structtagPIXELFORMATDESCRIPTOR

{

    WORD nSize//该结构所占内存空间。

    WORD nVersion;//版本号,当前为。

    DWORD dwFlags;

     /* PFD_DRAW_TO_BITMAP  支持内存中绘制位图

       PFD_DRAW_TO_WINDOW   支持屏幕绘图

       PFD_DOUBLEBUFFER   支持双缓冲

       PFD_CENERIC_FORMAT指定选择GDI支持的像素格式

       PFD_NEED_PALETTE   指定需要逻辑调色板

       PFD_NEED_SYSTEM_PALETTE   指定需要硬件调色板

       PFD_STEREONT  不支持

       PFD_SUPPORT_OPENGL支持OpenGL

       PFD_SUPPORT_GDI支持GDI此时不可使用PFD_DOUBLEBUFFER

       */

    BYTE iPixelType;//像素颜色模式,可选项为PFD_TYPE_RGBA或PFD_TYPE_INDEX,分别对应于RGBA模式和颜色索引模式。

    BYTE cColorBits;//指定颜色的位数。

    BYTE cRedBits//采用RGBA模式时,红色组分占用位数

    BYTE cRedShift;//采用RGBA模式时,红色组分占偏移量

    BYTE cGreenBits;//采用RGBA模式时,绿色组分占用位数

    BYTE cGreenShift;//采用RGBA模式时,绿色组分偏移量

    BYTE cBlueBits//采用RGBA模式时,蓝色组分占用位数

    BYTE cBlueShift;//采用RGBA模式时,蓝色组分偏移量

    BYTE cAlphaBits;//采用RGBA模式时,Alpha组分占用位数。

    BYTE cAlphaShift;//采用RGBA模式时,Alpha组分偏移量。

 

    BYTE cAccumBits;//指定累积缓冲区表示一个像素所用位数。

    BYTE cAccumRedBits;//定累积缓冲区表示红色组分占用位数。

    BYTE cAccumGreenBits;//指定累积缓冲区表示绿色组分占用位数。

    BYTE cAccumBlueBits;//指定累积缓冲区表示蓝色组分占用位数。

    BYTE cAccumAlphaBits;//指定累积缓冲区表示Alpha组分占用位数

 

    BYTE cDepthBits;//指定深度缓冲区表示一个像素所用位数。

    BYTE cStencilBits;//指定模板缓冲区表示一个像素所用位数。

    BYTE cAuxBuffers;//指定辅助缓冲区,Windows9x、NT不支持

    BYTE iLayerType;//Windows9x、NT下只能是PFD_MAIN_PLANE。

    BYTE bReserved//0

    DWORD dwLayerMask;//指定覆盖层的屏蔽,Windows9x、NT不支持

    DWORD dwVisibleMask;//Windows9xNT不支持

    DWORD dwDamageMask;//Windows9xNT不支持

} PIXELFORMATDESCRIPTOR;


     Windows提供了四个像素格式管理函数,分别介绍如下:

(1)int ChoosePixelFormat(HDChdc,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, intiPixelFormat, LPPIXELFORMATDESCRIPTOR*ppfd)

       该函数用格式索引iPixelFormat来设置hdc的像素格式。在使用该函数之前应该调用ChoosePixelFormat来获取像素格式索引。另外,OpenGL窗口风格必须包含WS_CLIPCHILDREN和WS_CLIPSIBLINGS类型,否则设置失败。

注意:
       应该注意的是ChoosePixelFormat函数并不一定返回一个最佳的像素格式值,可以利用DescribePixelFormat来枚举系统所支持的所有像素格式。OpenGL的通常支持24种不同的像素格式,如果系统安装了OpenGL硬件加速器,它可能会支持其它的像素格式。

2、着色表描述(Rendering Context,RC)

         任何一个Windows程序都必须处理设备描述表(Device Context),他告诉Windows怎样在一个窗口中显示图形信息一个设备描述表(DC)说明了笔和画刷的颜色、绘制模式、调色盘信息、映射模式、以及其他Windows必须知道的怎样显示图像的属性。与其他Windows程序一样,OpenGL应用程序应必须应用DC。不过我们将其称为着色描述表(Rendering Context,RC),有他通知Windows在窗口中绘制图形。每一个OpenGL都被两接到一个着色描述表上。着色描述表将所有的OpenGL命令连接到的设备描述表(Device Context,DC)上,应用程序必须在绘图之前调用专用函数wglCreateContext()创建自己的着色描述表,调用wlgMakeCurrent()使其当前化,退出OpenGL时使着色表非当前化。

3、设置像素格式

     设置像素格式,首先要填充PIXELFORMATDESCRIPTOR结构,然后设置像素格式。

BOOL CMyDEM3DView::bSetupPixelFormat()

{

    //填充PIXELFORMATDESCRIPTOR结构

    static PIXELFORMATDESCRIPTORpfd =

    {

       sizeof(PIXELFORMATDESCRIPTOR), //像素格式描述符的大小

       1,                          // 版本号

       PFD_DRAW_TO_WINDOW |        // 格式必须支持窗口

       PFD_SUPPORT_OPENGL |        // 格式必须支持OpenGL

       PFD_DOUBLEBUFFER,           // 必须支持双缓冲

       PFD_TYPE_RGBA,                  // 申请RGBA 格式

       24,                         // 选定色彩深度

       0, 0, 0, 0, 0, 0,           // 忽略的色彩位

       0,                          // 无Alpha缓存

       0,                          // 忽略Shift Bit

       0,                          // 无聚集缓存

       0, 0, 0, 0,                     // 忽略聚集位

       32,                         // 32位Z-缓存(深度缓存) 

       0,                          // 无模板缓存

       0,                          // 无辅助缓存

       PFD_MAIN_PLANE,                 // 主绘图层

       0,                          // 保留

       0, 0, 0                         // 忽略层遮罩

    };

 

    //设置像素格式

    int pixelformat;

    // Windows能否找到相应的象素格式?

    if ( (pixelformat =ChoosePixelFormat(m_pDC->GetSafeHdc(), &pfd)) == 0 )

    {

       MessageBox("选择像素格式失败!");

       return FALSE;

    }

    // 能否设置象素格式?

    if (SetPixelFormat(m_pDC->GetSafeHdc(),pixelformat, &pfd) ==FALSE)

    {

       MessageBox("设置像素格式出错!");

       return FALSE;

    }

    return TRUE;

}

注意:

       一个应用程序只为一个窗口设置一次像素格式,并且一旦设置,在程序执行过程中就不能更改,而且,在创建着色描述表之前必须设置像素格式,否则着色描述表就不知道它该选择什么样的像素属性。

4、创建着色描述表

     一般在视图类(Cview)的消息OnCreate()函数中创建图形操作描述表。

有两种方法创建着色描述表:

(1)通过响应Windows消息WM_CREATE来创建并当前化一个着色描述表,通过相应Windows消息WM_DESTORY来删除它

(2)创建和删除与(1)相同,但是只在每次应用OpenGL进行绘制前立即当前化,并且一旦此次绘制完成立即非当前化。

方法(1)只当前化一次着色描述表,减少了许多开销,加快了程序的执行,缺点是必须在整个应用过程中保留一个DC。方法(2)易于实现,但需要更多执行时间。

方法(1)实现:

int OnCreate(LPCREATESTRUCTlpCreateStruct)

{

    if (CView::OnCreate(lpCreateStruct) == -1)

       return -1;

    //TODO:  Add your specialized creation codehere

    Init();

    return 0;

}

void Init()

{

    PIXELFORMATDESCRIPTOR pfd;

    HGLRC      hrc;

    m_pDC = newCClientDC(this); //创建DC

    ASSERT(m_pDC !=NULL);

    if (!bSetupPixelFormat())   //设置像素格式

        return;

    hrc = wglCreateContext(m_pDC->GetSafeHdc());//用DC去创建一个RC

    wglMakeCurrent(m_pDC->GetSafeHdc(),hrc);//RC与当前DC相关联

}

 

void CMyDEM3DView::OnDestroy()

{

    HGLRC  hrc;

    hrc = wglGetCurrentContext();

    wglMakeCurrent(NULLNULL); //使当前着色表不再是当前的

   

    if (hrc)

        wglDeleteContext(hrc);//删除RC

    if (m_pDC)    

        delete m_pDC//删除当前View拥有的DC

 

       CView::OnDestroy();

}

     wglMakeCurrent(m_pDC->GetSafeHdc(),hrc)是着色描述表当前化,wglMakeCurrent()有两个参数,一是DC的句柄,二是着色描述表句柄。如果成功返回GL_TRUE,否则,返回GL_FALSE。

     wglMakeCurrent(NULL,NULL)和wglMakeCurrent(m_pDC->m_hDC,NULL)是当前着色描述表不再是当前的。两者是完全一样的,只要第二个参数为NULL,则一定可以使当前着色描述表非当前化,且第一个参数自动被忽略。

二、基于单文档OpenGL框架(VS2008)

     着色表采用方法(1)的模式,即RC和DC只在初始化时(Init)绑定一次,在OnDestroy时使RC非当前化并删除RC和DC。

    该程序适用于单文档,只含有一个RC与DC,具有很高的时间效率。

    该程序在OnSize中没有加入防止形变得内容。

1、配置OpenGL环境

     配置OpenGL环境,添加.lib类库

2、添加头文件

    在stdafx.h中添加头文件(放到其他文件中也可以)

#include <gl\gl.h>

#include <gl\glu.h>

3、添加变量

public:

    CClientDC *m_pDC;  //DeviceContext设备上下文

    HGLRC  m_hRC;   //RenderingContext着色上下文

4、设定OpenGL风格

      在PreCreateWindow中添加cs.style |= WS_CLIPSIBLINGS | WS_CLIPCHILDREN;

5、初始化

int OnCreate(LPCREATESTRUCTlpCreateStruct)

{

    if (CView::OnCreate(lpCreateStruct) == -1)

       return -1;

    Init(); //初始化

    return 0;

}

BOOL bSetupPixelFormat()

{

    //填充PIXELFORMATDESCRIPTOR结构

    staticPIXELFORMATDESCRIPTORpfd =

    {

       sizeof(PIXELFORMATDESCRIPTOR), //像素格式描述符的大小

       1,                          //版本号

       PFD_DRAW_TO_WINDOW |       //格式必须支持窗口

       PFD_SUPPORT_OPENGL |       //格式必须支持OpenGL

       PFD_DOUBLEBUFFER,          //必须支持双缓冲

       PFD_TYPE_RGBA,                 //申请RGBA格式

       24,                         //选定色彩深度

       0, 0, 0, 0,0, 0,           //忽略的色彩位

       0,                          //无Alpha缓存

       0,                          //忽略ShiftBit

       0,                          //无聚集缓存

       0, 0, 0, 0,                     //忽略聚集位

       32,                         // 32位Z-缓存(深度缓存) 

       0,                          //无模板缓存

       0,                          //无辅助缓存

       PFD_MAIN_PLANE,                //主绘图层

       0,                          //保留

       0, 0, 0                         //忽略层遮罩

    };

 

    //设置像素格式

    int pixelformat;

    // Windows能否找到相应的象素格式了?

    if ( (pixelformat =ChoosePixelFormat(m_pDC->GetSafeHdc(),&pfd)) ==0 )

    {

       MessageBox("选择像素格式失败!");

       returnFALSE;

    }

    // 能否设置象素格式?

    if (SetPixelFormat(m_pDC->GetSafeHdc(),pixelformat,&pfd) ==FALSE)

    {

       MessageBox("设置像素格式出错!");

       returnFALSE;

    }

    returnTRUE;

 

}

 

/*初始化*/

void Init()

{

    PIXELFORMATDESCRIPTOR pfd;

   

    m_pDC =new CClientDC(this); //创建DC

    ASSERT(m_pDC !=NULL);

 

    if (!bSetupPixelFormat())   //设置像素格式

       return;

 

    m_hRC =wglCreateContext(m_pDC->GetSafeHdc());//DC去创建一个RC

    wglMakeCurrent(m_pDC->GetSafeHdc(),m_hRC);//RC与当前DC相关联

}

6、OnSize()窗口改变

void CLidar428LabView::OnSize(UINTnType,intcx,intcy)

{

    CView::OnSize(nType,cx,cy);

    // TODO: Add yourmessage handler code here

    if(cy > 0)

    {

      glMatrixMode(GL_PROJECTION); //选用投影矩阵

       glLoadIdentity(); //重置当前指定的矩阵为单位矩阵

       glViewport(0, 0,cx,cy);    //根据窗口的实时变化重绘窗口

    }  

}

7、响应WM_ERASEBKGND消息

BOOL OnEraseBkgnd(CDC*pDC)

{

    // TODO: Add your message handler code here and/or calldefault

//returnCView::OnEraseBkgnd(pDC);

    return TRUE;

}

     当需要重新设置窗口背景时,产生WM_ERASEBKGND消息,处理该消息的默认操作是用当前背景色填充整个窗口。处理方法:注释掉原有的return语句,改为return true;使该函数不执行操作,仅返回true。

8、画图

在OnDraw()中调用绘图函数。

void CLidar428LabView::OnDraw(CDC*pDC)

{

    CLidar428LabDoc*pDoc =GetDocument();

    ASSERT_VALID(pDoc);

    if (!pDoc)

       return;

    // TODO: 在此处为本机数据添加绘制代码

    DrawScene(); //绘图

}

 

void DrawScene()

{

    glClearColor(0.6f,0.6f,1.0f,1.0f);//设置清屏颜色

    glClear(GL_COLOR_BUFFER_BIT |GL_DEPTH_BUFFER_BIT); //清除颜色缓冲区和深度缓冲区

    glPushMatrix();//矩阵堆栈函数,和glPopMatrix()相对应

 

    glPointSize(10.0);//设置点的大小

    glBegin(GL_LINES);//说明几何图元为“点”,和glEnd()相对应

    glColor3f(1.0,0.0,0.0);//绘制红色的点

 

    glVertex3f(-0.7,-0.7,0.0);

    glVertex3f(0.7,-0.7,0.0);

 

    glVertex3f(0.7,-0.7,0.0);

    glVertex3f(0.0,0.0,0.0);

 

    glVertex3f(0.0,0.0,0.0);

    glVertex3f(0.7,0.7,0.0);

 

    glVertex3f(0.7,0.7,0.0);

    glVertex3f(-0.7,0.7,0.0);

 

    glVertex3f(-0.7,0.7,0.0);

    glVertex3f(-0.7,-0.7,0.0);

   

    glEnd();   

 

    glPopMatrix();

    glFinish();  

    SwapBuffers(wglGetCurrentDC());//双缓冲

}

    红色部分是画图内容,可以随意更改。

9、响应WM_DESTROY消息

    取消DC和RC的关联,并且删除DC与RC。

void CLidar428LabView::OnDestroy()

{

    CView::OnDestroy();


    // TODO: Add your message handler code here

    //m_hRC = wglGetCurrentContext();

    wglMakeCurrent(NULL,NULL); //消DC和RC的关联

    if(m_hRC)      

       wglDeleteContext(m_hRC);  //删除RC

    if(m_pDC)

       delete m_pDC;     //删除DC

}

    图像 glVertex3f()参数只能小于1,大于1会被裁剪掉。

注:不能添加GLUT32.lib,因为glut32是与窗口有关的库,而我们使用的是MFC单文档,否则会起冲突!