在MFC框架中使用OpenGL的简单实例

引言

  我们知道,在MFC框架中,用于绘图的接口是GDI。但GDI只能绘制简单的2D图形,要想制作精美的3D图形,一个可行的办法是使用OpenGL或者Direct3D等第三方库。

  由于最近在给导师的一个小项目帮忙,而且要求使用OpenGL,所以我特地在网上搜索“如何在MFC框架中使用OpenGL”,看了很多博文,甚至论文(居然还有人把这个写成论文)后,自己又到VS2012上尝试了一番,最终摸索出了最最简单基本的使用方法,故总结在这里。一方面开启自己在博客园的学习和自省之路,另一方面也为需要的朋友提供点小小的帮助。相信这个实例会是最简单最容易理解的。

摘要

  GDI绘图使用的是HDC,而OpenGL使用的则是HGLRC。与D2D通过把RenderTarget绑定到HDC以实现和GDI的混用的方法类似,OpenGL要想与HDC混用,或者说兼容HDC吧,需要使用wglCreateContext()函数来通过HDC创建一个HGLRC并把它选为当前所使用的环境。不过在此之前,我们还需要把像素格式设置成支持OpenGL的格式。另外,窗口也必须被改为特定的样式才能被OpenGL使用。做好了这三点,就能在MFC框架中使用OpenGL进行绘制了。

具体实现步骤

1. 封装OpenGL类

  首先,基于OOP的思想,我们把跟OpenGL相关的数据与操作封装起来:

#pragma once
class COpenGL
{
private:
  HDC hDC;     //GDI绘图中使用的的设备环境句柄
  HGLRC hRC;    //OpenGL渲染时使用的渲染环境句柄

public:
  COpenGL(void);
  virtual ~COpenGL(void);
    
  bool SetupPixelFormat(HDC hdc);  //设置像素格式为适合OpenGL的格式
  void Init(void);           //初始化渲染过程中属性的设置
  void Render(void);          //绘制代码

  void Reshape(int width,int height);  //改变窗口大小时对视窗进行的操作
};
#include "stdafx.h"
#include "OpenGL.h"


COpenGL::COpenGL(void)
{
}


COpenGL::~COpenGL(void)
{
    wglMakeCurrent(hDC, NULL);
    wglDeleteContext(hRC);
}


void COpenGL::Init(void)
{  
    //可添加例如“开启深度探测”等绘图属性设置代码
}


bool COpenGL::SetupPixelFormat(HDC hdc)
{
    hDC=hdc;

    PIXELFORMATDESCRIPTOR pfd = 
    {
       sizeof(PIXELFORMATDESCRIPTOR),    // pfd结构的大小
       1,                                // 版本号
       PFD_DRAW_TO_WINDOW |              // 支持在窗口中绘图
       PFD_SUPPORT_OPENGL |              // 支持OpenGL
       PFD_DOUBLEBUFFER,                 // 双缓存模式
       PFD_TYPE_RGBA,                    // RGBA 颜色模式
       24,                               // 24 位颜色深度
       0, 0, 0, 0, 0, 0,                 // 忽略颜色位
       0,                                // 没有非透明度缓存
       0,                                // 忽略移位位
       0,                                // 无累加缓存
       0, 0, 0, 0,                       // 忽略累加位
       32,                               // 32 位深度缓存   
       0,                                // 无模板缓存
       0,                                // 无辅助缓存
       PFD_MAIN_PLANE,                   // 主层
       0,                                // 保留
       0, 0, 0                           // 忽略层,可见性和损毁掩模
    };

    int nPixelFormat;    // 像素点格式
    if (!(nPixelFormat = ChoosePixelFormat(hDC, &pfd)))
    { 
        MessageBox(NULL,L"can not find proper mode",L"Error",MB_OK|MB_ICONEXCLAMATION);
        return FALSE;
    }

    SetPixelFormat(hDC,nPixelFormat,&pfd);
    hRC = wglCreateContext(hDC);    //利用GDI绘图所使用的HDC创建对应的HGLRC
    wglMakeCurrent(hDC, hRC);        //使OpenGL绘图所使用的HGLRC为当前绘图工具

    return TRUE;
}


void COpenGL::Render()
{
    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);        
    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
    glLoadIdentity();                        
 
    //绘制操作:
    glColor3ub(255,0,0);
    glBegin(GL_POLYGON);//填充凸多边形
        glVertex3f(0.5f,0.5f ,0.0f);
        glVertex3f(0.5f,-0.5f, 0.0f);
        glVertex3f(-0.5f,-0.5f,0.0f);
        glVertex3f(-0.5f,0.5f,0.0f);
    glEnd();
   
    glFlush();
    SwapBuffers(hDC);
}


void COpenGL::Reshape(int width,int height)
{
    glViewport(0,0,width,height);
}

 2. 使用OpenGL类

  接下来,我们在MFC框架中的CProjNameView类中创建刚刚编写的OpenGL类成员并进行相应的调用和操作来进行绘制:

  先在头文件中添加成员变量:

COpenGL opengl;

  然后,改写CProjNameView::PreCreateWindow()函数以改变窗口样式来适应OpenGL的要求:

BOOL CProjNameView::PreCreateWindow(CREATESTRUCT& cs)
{
    // TODO: 在此处通过修改
    //  CREATESTRUCT cs 来修改窗口类或样式
    cs.style|=WS_CLIPSIBLINGS|WS_CLIPCHILDREN;

    return CView::PreCreateWindow(cs);
}

  再在CProjNameView::OnInitialUpdate()函数中添加设置像素格式、转换当前绘图所使用的环境和初始化OpenGL绘制属性的操作。(这里我们省去了初始化操作,即省去了OpenGL.cpp中的Init()函数的代码。设置像素格式、转换当前绘图所使用的环境都包含在COpenGL类的成员函数SetupPixelFormat()中)

void CProjNameView::OnInitialUpdate()
{
    CView::OnInitialUpdate();

    // TODO: 写入最终选择模式代码之后移除此代码
    m_pSelection = NULL;    // 初始化所选内容
   opengl.SetupPixelFormat(::GetDC(GetSafeHwnd()));
    opengl.Init();
} 

  接下来,在CProjNameView::OnDraw()函数中添加绘制操作。

void CProjNameView::OnDraw(CDC* /*pDC*/)
{
    CFaceModelingDoc* pDoc = GetDocument();
    ASSERT_VALID(pDoc);
    if (!pDoc)
        return;

    // TODO: 在此处为本机数据添加绘制代码
    opengl.Render();
}

  最后,再改写CProjNameView::OnSize()函数,就大功告成了!(当然,这个并不是本例子中必要的,没有这项控制也能成功绘制。)

void CProjNameView::OnSize(UINT nType, int cx, int cy)
{
    if(cx==0)
    {
       cx=1;
    }
    opengl.Reshape(cx,cy);
}

  

  效果图如下:

补充

  当然,事先需要包含gl.h和glut.h两个头文件,他们一般都在GL文件夹目录下,而且gl.h是自带的,glut.h是需要自己扩展的。另外,还需要把glut.lib添加到工程可见的范围内。方便起见,我在这里提供一下glut的下载链接:glut扩展包

  另外,在MFC中使用OpenGL的绘制列表和纹理时要注意,MFC貌似不支持OpenGL的绘制列表,而且MFC似乎要求在每次绘制前都要设置一次纹理。这之中的道理只有深入了解MFC和OpenGL后才能明白了,如果有朋友知道的话,希望能在评论里给一些指导哦~!

  如果还有什么问题或是建议的话,希望大家提出来!新人第一次写随笔,还请多多指教~!

 

posted on 2013-07-29 11:14  Jermaine.Lee  阅读(4041)  评论(1编辑  收藏  举报

导航