OpenGL在MFC中的实现

转自http://www.cnblogs.com/carfield/archive/2011/11/20/2255780.html

1、在写代码之前,首先要做好OpenGL的环境配置。

    比如路径之类,还有添加头文件,头文件添加在stdafx.h中,有位置要求,不能在#include <afxwin.h>  这句之上

////这两句加在"stdafx.h"里面的#include <afxwin.h>以上就会出现问题,需要加在这一句下面
#include <gl\gl.h> 
#include <gl\glu.h> 

2、修改函数BOOL COpenGLDemoView::PreCreateWindow(CREATESTRUCT& cs)。这句代码知识在MDI中必须,如果使用的是单文档,可以不加。

WS_CLIPCHILDREN(创建父窗口使用的Windows风格,用于重绘时裁剪子窗口所覆盖的区域)

WS_CLIPSIBLINGS(创建子窗口使用的Windows风格,用于重绘时剪裁其他子窗口所覆盖的区域)

BOOL COpenGLDemoView::PreCreateWindow(CREATESTRUCT& cs)
{
    // TODO: 在此处通过修改
    //  CREATESTRUCT cs 来修改窗口类或样式
    cs.style |= (WS_CLIPCHILDREN | WS_CLIPSIBLINGS);//openGL在MDI中必需
    return CView::PreCreateWindow(cs);
}

3、在VIEW中添加成员函数和变量

// 生成的消息映射函数
protected:
    DECLARE_MESSAGE_MAP()
    bool SetWindowPixelFormat(HDC hDC);
    // 创建绘制环境(RC)并使之成为当前绘制环境    bool CreateViewGLContext(HDC hDC);
    // 初始化openGL
    bool InitGL(void);
    // 绘图代码区
    int DrawGLScene(void);
    //>像素格式的索引值
    int m_GLPixelIndex;
    // 绘制环境,HGLRC是一个指向rendering context的句柄
    HGLRC m_hGLContext;

 4、初始化变量

//初始化
    this->m_GLPixelIndex = 0;
    this->m_hGLContext = NULL;

 5、设置像素格式

bool COpenGLDemoView::SetWindowPixelFormat(HDC hDC)
{
    //定义窗口的像素格式
    PIXELFORMATDESCRIPTOR pixelDesc=
    {
        sizeof(PIXELFORMATDESCRIPTOR),           //nSize结构长度
        1,                                       //nVersion结构版本
        PFD_DRAW_TO_WINDOW|PFD_SUPPORT_OPENGL| 
        PFD_DOUBLEBUFFER|PFD_SUPPORT_GDI,        //dwFlags告诉OpenGL如何处理像素
         /*
         wFlags能接收以下标志:
            PFD_DRAW_TO_WINDOW 使之能在窗口或者其他设备窗口画图;
            PFD_DRAW_TO_BITMAP 使之能在内存中的位图画图;
            PFD_SUPPORT_GDI 使之能调用GDI函数(注:如果指定了PFD_DOUBLEBUFFER,这个选项将无效);
            PFD_SUPPORT_OpenGL 使之能调用OpenGL函数;
            PFD_GENERIC_FORMAT 假如这种象素格式由Windows GDI函数库或由第三方硬件设备驱动程序支持,则需指定这一项;
            PFD_NEED_PALETTE 告诉缓冲区是否需要调色板,本程序假设颜色是使用24或 32位色,并且不会覆盖调色板;
            PFD_NEED_SYSTEM_PALETTE 这个标志指明缓冲区是否把系统调色板当作它自身调色板的一部分;
            PFD_DOUBLEBUFFER 指明使用了双缓冲区(注:GDI不能在使用了双缓冲区的窗口中画图);
            PFD_STEREO 指明左、右缓冲区是否按立体图像来组织。
                        PFD_SWAP_LAYER_BUFFERS
         */
        PFD_TYPE_RGBA,  //iPixelType,颜色模式,包括两种PFD_TYPE_RGBA意味着每一位(bit)组代表着rgb各分量的值。PFD_TYPE_COLORINDEX意味着每一位组代表着在彩色查找表中的索引值
        24,   //cColorBits定义了指定一个颜色的位数。对RGBA来说,位数是在颜色中红、绿、蓝各分量所占的位数。对颜色的索引值来说,指的是表中的颜色数。
        0,0,0,0,0,0,  //cRedBits、cRedShifts、cGreenBits、cGreenShifts、cBlueBits、cBlueShifts用,基本不被采用,一般置0
                  //cRedBits、cGreenBits、cBlueBits用来表明各相应分量所使用的位数。
                      //cRedShift、cGreenShift、cBlue-Shift用来表明各分量从颜色开始的偏移量所占的位数。
        0,                                       //cAlphaBits,RGB颜色缓存中Alpha的位数                            
        0,                                 //cAlphaShift,已经不被采用,置0                   
        0,                                       //cAcuumBits累计缓存的位数
        0,0,0,0,                                 //cAcuumRedBits/cAcuumGreenBits/cAcuumBlueBits/cAcuumAlphaBits,基本不被采用,置0
        32,                                      //cDepthBits深度缓存的位数
        0,                                       //cStencilBits,模板缓存的位数
        0,                                       //cAuxBuffers,辅助缓存的位数,一般置0
        PFD_MAIN_PLANE,                          //iLayerType,说明层面的类型,可忽略置0,是早期的版本,包括
                                                 //PFD_MAIN_PLANE,PFD_OVER_LAY_PLANE,PFD_UNDERLAY_PLANE
        0,0,0,0                                  //bReserved,dwLayerMask,dwVisibleMask,dwDamageMask,必须置0
    };

    this->m_GLPixelIndex = ChoosePixelFormat(hDC,&pixelDesc);//选择最相近的像素格式
    /*
    ChoosePixelFormat接受两个参数:一个是hDc,另一个是一个指向PIXELFORMATDESCRIPTOR结构的指针&pixelDesc
    该函数返回此像素格式的索引值,如果返回0则表示失败。
    假如函数失败,我们只是把索引值设为1并用 DescribePixelFormat得到像素格式描述。
    假如你申请一个没得到支持的像素格式,则ChoosePixelFormat将会返回与你要求的像素格式最接近的一个值
    一旦我们得到一个像素格式的索引值和相应的描述,我们就可以调用SetPixelFormat设置像素格式,并且只需设置一次。
    */
    if(this->m_GLPixelIndex==0)
    {//选择失败
        this->m_GLPixelIndex = 1;//默认的像素格式
        //用默认的像素格式进行设置
        //
        if(DescribePixelFormat(hDC,this->m_GLPixelIndex,sizeof(PIXELFORMATDESCRIPTOR),&pixelDesc)==0)
        {
            return FALSE;
        }
    }

    if(SetPixelFormat(hDC,this->m_GLPixelIndex,&pixelDesc)==FALSE)
    {
        return FALSE;
    }
    return TRUE;
}

 6. 创建绘制环境(RC)并使之成为当前绘制环境

bool COpenGLDemoView::CreateViewGLContext(HDC hDC)
{
    //WglCreateContext函数创建一个新的OpenGL渲染描述表(RC)
    //此描述表必须适用于绘制到由hdc返回的设备
    //这个渲染描述表将有和设备上下文(dc)一样的像素格式.
    this->m_hGLContext = wglCreateContext(hDC);//创建RC

    if(this->m_hGLContext==NULL)
    {//创建失败
        return FALSE;
    }

    /*
    wglMakeCurrent 函数设定OpenGL当前线程(线程相关性)的渲染环境。
    以后这个线程所有的OpenGL调用都是在这个hdc标识的设备上绘制。
    你也可以使用wglMakeCurrent 函数来改变调用线程的当前渲染环境
    使之不再是当前的渲染环境。
    */
    if(wglMakeCurrent(hDC,this->m_hGLContext)==FALSE)
    {//选为当前RC失败
        return FALSE;
    }
    return TRUE;
}

7、在OnCreate函数中调用上函数 

int COpenGLDemoView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
    if (CView::OnCreate(lpCreateStruct) == -1)
        return -1;

    // TODO:  在此添加您专用的创建代码
    //得到一个窗口对象(CWnd的派生对象)指针的句柄(HWND)  
    HWND hWnd = this->GetSafeHwnd();  
    //GetDC该函数检索一指定窗口的客户区域或整个屏幕的显示设备上下文环境的句柄
    //以后可以在GDI函数中使用该句柄来在设备上下文环境中绘图。
    HDC hDC = ::GetDC(hWnd);

    if(this->SetWindowPixelFormat(hDC)==FALSE)
    {//设置像素格式
        return 0;
    }
    if(this->CreateViewGLContext(hDC)==FALSE)
    {//创建RC并选为所用
        return 0;
    }
    if(!this->InitGL())
    {//初始化openGL
        return 0;
    }
    return 0;
}

8、初始化OpenGL

bool COpenGLDemoView::InitGL(void)
{
    glShadeModel(GL_SMOOTH);                            // Enable Smooth Shading
    glClearColor(0.0,0.0,0.0,0.0);// Black Background
    glClearDepth(1.0f);                                    // Depth Buffer Setup
    glEnable(GL_DEPTH_TEST);                            // Enables Depth Testing
    glDepthFunc(GL_LEQUAL);                                // The Type Of Depth Testing To Do
    glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);    // Really Nice Perspective Calculations
    return TRUE;                                        // Initialization Went OK
}  

 9、添加WM_DESTROY的消息处理函数Ondestroy( ),使之如下所示:

void COpenGLDemoView::OnDestroy()
{
    CView::OnDestroy();

    // TODO: 在此处添加消息处理程序代码
    if(wglGetCurrentContext()!=NULL)
    {
        wglMakeCurrent(NULL,NULL);
    }
    if(this->m_hGLContext!=NULL)
    {
        wglDeleteContext(this->m_hGLContext);
        this->m_hGLContext = NULL;
    }
}

至此,框架都打好。

我们在程序开头产生了一个RC,自始自终都使用它。
这与大多数GDI程序不同。在GDI程序中,DC在需要时才产生,并且是画完立刻释放掉。
实际上,RC也可以这样做;但要记住,产生一个RC需要很多处理器时间。
因此,要想获得高性能流畅的图像和图形,最好只产生RC一次,并始终用它,直到程序结束。
   
CreateViewGLContex产生RC并使之成为当前RC。
WglCreateContext返回一个RC的句柄。
在你调用 CreateViewGLContex之前,你必须用SetWindowPixelFormat(hDC)将与设备相关的像素格式设置好.
wglMakeCurrent将RC设置成当前RC。传入此函数的DC不一定就是你产生RC的那个DC,但二者的设备句柄(Device Context)和像素格式必须一致。
假如你在调用wglMakeforCurrent之前已经有另外一个RC存在,wglMakeforCurrent 就会把旧的RC冲掉,并将新RC设置为当前RC。
另外你可以用wglMakeCurrent(NULL, NULL)来消除当前RC。
我们要在OnDestroy中把绘制环境删除掉。
但在删除RC之前,必须确定它不是当前句柄。
我们是通过wglGetCurrentContext来了 解是否存在一个当前绘制环境的。
假如存在,那么用wglMakeCurrent(NULL, NULL)来把它去掉。然后就可以通过wglDelete-Context来删除RC了。
这时允许视类删除DC才是安全的。注:一般来说,使用的都是单线 程的程序,产生的RC就是线程当前的RC,不需要关注上述这一点。
但如果使用的是多线程的程序,那我们就特别需要注意这一点了,否则会出现意想不到的后果。

10、现在实现画图功能

ClasswizardCOpenGLDemoView添加WMSIZE的消息处理函数OnSize,代码如下:

void COpenGLDemoView::OnSize(UINT nType, int cx, int cy)
{
    CView::OnSize(nType, cx, cy);

    // TODO: 在此处添加消息处理程序代码
    /*
    OnSize通过glViewport(0, 0, width, height)定义了视口和视口坐标。
    glViewport的第一、二个参数是视口左下角的像素坐标,第三、四个参数是视口的宽度和高度。

    OnSize中的glMatrixMode是用来设置矩阵模式的,它有三个选项:GL_MODELVIEW、GL_PROJECTION、 GL_TEXTURE。
    GL_MODELVIEW表示从实体坐标系转到人眼坐标系。
    GL_PROJECTION表示从人眼坐标系转到剪裁坐标系。
    GL_TEXTURE表示从定义纹理的坐标系到粘贴纹理的坐标系的变换。

    glLoadIdentity初始化工程矩阵(project matrix)
    gluOrtho2D把工程矩阵设置成显示一个二维直角显示区域。
    */
    GLsizei width,height;
    width = cx;
    height = cy;
    if (height==0)                                        // Prevent A Divide By Zero By
    {
        height=1;                                        // Making Height Equal One
    }

    glViewport(0,0,width,height);                        // Reset The Current Viewport

    glMatrixMode(GL_PROJECTION);                        // Select The Projection Matrix
    glLoadIdentity();                                    // Reset The Projection Matrix
    // Calculate The Aspect Ratio Of The Window
    gluPerspective(45.0f,(GLfloat)width/(GLfloat)height,0.1f,100.0f);//透视投影
    glMatrixMode(GL_MODELVIEW);                            // Select The Modelview Matrix
    glLoadIdentity();                                    // Reset The Modelview Matrix
}

11、用ClasswizardCOpenGLDemoView添加WM_PAINT的消息处理函数OnPaint,代码如下:

void COpenGLDemoView::OnPaint()
{
CPaintDC dc(this); // device context for painting
// TODO: 在此处添加消息处理程序代码
// 不为绘图消息调用 CView::OnPaint()
this->DrawGLScene();
SwapBuffers(dc.m_hDC);
}

这里最后一句很关键,起先我就是因为没写他而纠结没有画面

12、绘图代码。这里可以改了

// 绘图代码区
int COpenGLDemoView::DrawGLScene(void)
{
    // Here's Where We Do All The Drawing
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);    // Clear Screen And Depth Buffer
    glLoadIdentity();                                    // Reset The Current Modelview Matrix
    glTranslatef(-1.5f,0.0f,-6.0f);//物体左移1.5,向内移6,相当于移动镜头一样,让物体进入镜头中
    glBegin(GL_TRIANGLES);                            // 绘制三角形
        glColor3f(255.0f,0.0f,0.0f);
        glVertex3f( 0.0f, 1.0f, 0.0f);                    // 上顶点
        glColor3f(0.0f,255.0f,0.0f);
        glVertex3f(-1.0f,-1.0f, 0.0f);                    // 左下
        glColor3f(0.0f,0.0f,255.0f);
        glVertex3f( 1.0f,-1.0f, 0.0f);                    // 右下
    glEnd();                                // 三角形绘制结束
    glTranslatef(3.0f,0.0f,0.0f);
    glBegin(GL_QUADS);                            //  绘制正方形
        glColor3f(255.0f,0.0f,0.0f);
        glVertex3f(-1.0f, 1.0f, 0.0f);                    // 左上
        glColor3f(0.0f,255.0f,0.0f);
        glVertex3f( 1.0f, 1.0f, 0.0f);                    // 右上
        glColor3f(0.0f,0.0f,255.0f);
        glVertex3f( 1.0f,-1.0f, 0.0f);                    // 左下
        glColor3f(255.255f,255.0f,255.0f);
        glVertex3f(-1.0f,-1.0f, 0.0f);                    // 右下
    glEnd();                                // 正方形绘制结束
    glFlush();
    return TRUE;                                        // Everything Went OK
}
阅读更多
个人分类: opengl C++
想对作者说点什么? 我来说一句

如何在VC++中用MFC进行OpenGL编程

2017年09月07日 262KB 下载

没有更多推荐了,返回首页

不良信息举报

OpenGL在MFC中的实现

最多只允许输入30个字

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭