本博客计算机图形学系列文章索引:
地址:《计算机图形学系列相关文章索引(持续更新)》
一、简介
Windows GDI是通过设备句柄(Device Context(设备描述表)以下简称"DC")来绘图,而OpenGL则需要绘制环境(Rendering Context(着色描述表),以下简称"RC")。每一个GDI命令需要传给它一个DC,但与GDI不同,OpenGL使用当前绘制环境(RC)。但是RC并不能直接完成绘图,只能与特定的DC联系起来,从而完成具体的绘图工作。一旦在一个线程中指定了一个当前RC,在此线程中其后所有的OpenGL命令都使用相同的当前RC。虽然在单一窗口中可以使用多个RC,但在单一线程中只有一个当前RC。下面我将首先产生一个OpenGL RC并使之成为当前RC。这将分为三个步骤:
1、设置窗口像素格式;
2、产生RC;
3、设置为当前RC。
用一个图表示如下所示,图中介绍了需要在各个函数中设置的信息:
二、搭建MFC中的OpenGL基本框架
本文以一个单文档程序为例。新建一个单文档名字为VCOpenGL2 其他的默认。
1、添加链接库。
打开菜单栏下的项目->属性->配置属性->链接器->输入->附加依赖项里加入OpenGL32.lib GLu32.lib GLaux.lib,如图
如果不用这种方法添加链接库的话,可以写上下列代码,也能达到同样的效果:
- #pragma comment( lib, "opengl32.lib" )
- #pragma comment( lib, "glu32.lib" )
- #pragma comment( lib, "glut32.lib" )
- #pragma comment( lib, "glaux.lib" )
2、 包含头文件。
在stdafx里面添加opengl的头文件(当然也可以在其他文件中添加,比如绘图一般都是在视图中的,可以在xxxView.cpp文件中包含头文件)。如下代码所示: #include <GL\glaux.h>
#include <GL\glut.h>
有几点说明:
2.1、包含glut.h的同时就把gl.h和glu.h都包括了。因为打开glut.h你可以看到如下图所示的,已经包含了gl.h和glu.h。
2.2、关于这OpenGL中的这几个库的介绍,看一看我的另一篇文章,地址如下:
http://blog.csdn.net/zhangkaihang/article/details/7459629
3、设置窗口显示风格。
窗口创建之前我们必须设置窗口风格包含
WS_CLIPCHILDREN(创建父窗口使用的Windows风格,用于重绘时裁剪子窗口所覆盖的区域)和WS_CLIPSIBLINGS(创建子窗口使用的Windows风格,用于重绘时剪裁其他子窗口所覆盖的区域),
从而避免OpenGL绘制到其他窗口中去。这些应该放在PreCreateWindow()中。代码如下:
4、设置窗口像素格式
首先向VCOpenGL2View类中添加几个保护的成员变量和公共的成员函数。如下:
- HGLRC m_hRC; //Rendering Context着色描述表
- CClientDC* m_pDC; //Device Context设备描述表
- BOOL InitializeOpenGL(); //初始化 OpenGL
- BOOL SetupPixelFormat(); //设置像素格式
- void RenderScene(); //绘制场景
别忘了在VCOpenGL2View的构造函数中设置 m_hRC = NULL; m_pDC = NULL;
产生一个RC的第一步是定义窗口的像素格式。像素格式决定窗口着所显示的图形在内存中是如何表示的。由像素格式控制的参数包括:颜色深度、缓冲模式和所支持的绘画接口。在下面将在SetupPixelFormat()函数中对这些参数的设置。代码如下:
- BOOL CVCOpenGL2View::SetupPixelFormat(void)
- {
- static 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 pixelFormat;
- // 为设备描述表得到最匹配的像素格式
- if((pixelFormat = ChoosePixelFormat(m_pDC->GetSafeHdc(), &pfd)) == 0)
- {
- MessageBox( _T("ChoosePixelFormat failed") );
- return FALSE;
- }
- // 设置最匹配的像素格式为当前的像素格式
- if(SetPixelFormat(m_pDC->GetSafeHdc(), pixelFormat, &pfd) == FALSE)
- {
- MessageBox( _T("SetPixelFormat failed") );
- return FALSE;
- }
- return TRUE;
- }
5、产生RC,设置为当前RC。
现在像素格式已经设定,我们下一步工作是产生绘制环境(RC)并使之成为当前绘制环境,即编写InitializeOpenGL()函数。代码如下:
- BOOL CVCOpenGL2View::InitializeOpenGL(void)
- {
- PIXELFORMATDESCRIPTOR pfd;
- int n;
- m_pDC=new CClientDC(this);
- ASSERT(m_pDC != NULL);
- // 设置当前的绘图像素格式
- if(!SetupPixelFormat())
- {
- return FALSE;
- }
-
- n=::GetPixelFormat(m_pDC->GetSafeHdc());
- ::DescribePixelFormat(m_pDC->GetSafeHdc(), n,sizeof(pfd),&pfd);
- // 创建绘图描述表
- m_hRC=wglCreateContext(m_pDC->GetSafeHdc());
- if(m_hRC == NULL)
- {
- return FALSE;
- }
- // 使绘图描述表为当前调用现程的当前绘图描述表
- if( wglMakeCurrent(m_pDC->GetSafeHdc(),m_hRC) == FALSE)
- {
- return FALSE;
- }
- glClearDepth(1.0f);
- glEnable(GL_DEPTH_TEST);
- return TRUE;
- }
别忘了再OnCreate()函数中调用 InitializeOpenGL()函数。如下所示:
- int CVCOpenGL2View::OnCreate(LPCREATESTRUCT lpCreateStruct)
- {
- if (CView::OnCreate(lpCreateStruct) == -1)
- return -1;
-
- // TODO: 在此添加您专用的创建代码
- if ( InitializeOpenGL())
- {
- return 0;
- }
-
- return 0;
- }
6、设置视口
在OnSize()中一般用来设置视口和视锥,因为这些是和窗口大小相关的。代码如下:
- void CVCOpenGL2View::OnSize(UINT nType, int cx, int cy)
- {
- CView::OnSize(nType, cx, cy);
-
- // TODO: 在此处添加消息处理程序代码
- m_wide = cx; //m_wide为在CVCOpenGL2View类中添加的表示视口宽度的成员变量
- m_heigth = cy; //m_height为在CVCOpenGL2View类中添加的表示视口高度的成员变量
- //避免除数为0
- if(m_heigth==0)
- {
- m_heigth=1;
- }
- //设置视口与窗口的大小
- glViewport(0,0,m_wide,m_heigth);
- }
7、绘制场景(用OpenGL绘图相关的代码都在这里哦!!)
本文以一个三维正方体为例,代码如下:
- void CVCOpenGL2View::RenderScene(void)
- {
- //设置清屏颜色为黑色
- glClearColor(0.0f,0.0f,0.0f,0.0f);
- //清除颜色缓冲区和深度缓冲区
- glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
- /*
- //建立正交变换下的剪切体
- if(w<h)
- {
- glOrtho(-nRange,nRange,-nRange*h/w,nRange*h/w,-nRange,nRange);
- }
- else
- {
- glOrtho(-nRange*w/h,nRange*w/h,-nRange,nRange,-nRange,nRange);
- }
- */
- //透视投影变换
- glMatrixMode(GL_PROJECTION);
- glLoadIdentity();
- gluPerspective(m_tFovy, (double)m_wide/(double)m_heigth,m_zNear,m_zFar);
- //视角变换
- glMatrixMode(GL_MODELVIEW);
- glLoadIdentity();
- gluLookAt(10,10,10,0,0,0,0,1,0);
- //矩阵堆栈函数,和glPopMatrix()相对应
- glPushMatrix();
-
- glBegin( GL_LINES );
- glColor3d(1.0, 0.0, 0.0); // X轴 红色
- glVertex3d(0.0, 0.0, 0.0);
- glVertex3d(2.0, 0.0, 0.0);
- glColor3d(0.0, 1.0, 0.0); // Y轴 绿色
- glVertex3d(0.0, 0.0, 0.0);
- glVertex3d(0.0, 2.0, 0.0);
- glColor3d(0.0, 0.0, 1.0); // Z轴 蓝色
- glVertex3d(0.0, 0.0, 0.0);
- glVertex3d(0.0, 0.0, 2.0);
- glEnd();
-
- glColor3f(1.0, 1.0, 1.0);
- glutWireCube(0.5);
-
- glPopMatrix();
- glFinish();
- SwapBuffers(wglGetCurrentDC());
- }
别忘了,在OnDraw()函数中调用哦!!!!!如下:
- void CVCOpenGL2View::OnDraw(CDC* /*pDC*/)
- {
- CVCOpenGL2Doc* pDoc = GetDocument();
- ASSERT_VALID(pDoc);
- if (!pDoc)
- return;
- // TODO: 在此处为本机数据添加绘制代码
-
- RenderScene();
- }
8、一些收尾工作。
8.1 为了使改变窗口大小时严重的闪烁,在OnEraseBkgnd()里做一些操作,避免windows自己的窗口刷新闪烁。OnEraseBkgnd()函数需要重写。如下:
- BOOL CVCOpenGL2View::OnEraseBkgnd(CDC* pDC)
- {
- // TODO: 在此添加消息处理程序代码和/或调用默认值
- return TRUE;
-
- // return CView::OnEraseBkgnd(pDC);
- }
8.2 为了避免内存泄露,OnDestroy()函数中加一些代码,如下:
- void CVCOpenGL2View::OnDestroy()
- {
- CView::OnDestroy();
-
- // TODO: 在此处添加消息处理程序代码
-
- m_hRC = ::wglGetCurrentContext();
- if(::wglMakeCurrent (0,0) == FALSE)
- {
- MessageBox(_T("Could not make RC non-current"));
- }
-
- if(m_hRC)
- {
- if(::wglDeleteContext(m_hRC)==FALSE)
- {
- MessageBox(_T("Could not delete RC"));
- }
- }
-
- if(m_pDC)
- {
- delete m_pDC;
- }
- m_pDC = NULL;
- }
至此一个单文档的框架就弄好了。下面贴出所画的立方体,如下图:
|