虽然MFC已经落伍好多年,而且用来做界面非常的不好用。。。但是我既不会C#也不会QT,又需要使用OpenGL,就只能将就用了。。。
一、首先介绍Windows图像程序设计中几个重要的概念:
GDI(Graphics Device Interface,图形设备接口):这是Windows API的一个库。当Windows应用程序需要显示点、线、图像、文字等内容,在显示器或打印输入这些内容时,就需要用到GDI。Windows应用程序不能直接操作系统的硬件(比如显卡),GDI就为应用程序提供了相关的接口。
其相关的函数接口、数据类型等都在WinGDI.h中声明(已经由Windows.h引入),在程序开发时,需要链接到Gdi32.lib。
DC(Device Contexts,设备上下文):是GDI库中最基本也是最重要的概念。DC是一个对象,设定了图形输出的特性和属性。
系统中可以有多个DC,每一个DC都必须关联到一个特定的图像输出设备。这些设备可以是真实存在的物理设备(显示器、打印机、绘图仪等),也可以使虚拟设备。这些反应在DC的类型上,DC具有4种类型:“显示”、“打印机”、“内存”、“信息”。
其中显示类型DC是最常用的,它被关联到了显示设备上,所有的图像输出操作将直接反映在显示器上。
注:DC也可以只是设备全部输出范围的一部分。比如界面上某个窗口的客户区也可以有DC与之对应,对这样的DC进行操作只会影响到窗口客户区。
如果要将图像输出到特定的设备只需要创建相应类型的DC即可(注:对不同类型DC的操作是统一的),我们只关注获取显示器相关DC的操作:(以下的函数都是GDI库中的接口函数)
1. 获取DC - GetDC(HWND hWnd):
调用该函数会返回hWnd参数所指定的窗口的客户区所对应的DC的句柄。
如果hWnd参数设置为NULL,那么函数会返回整个桌面的DC。
2. 另一种获取DC的方法 - CreateDC
该函数也是用来获取DC句柄,与GetDC不同的是,CreateDC可以获取非显示器输出DC。只需指定不同的参数即可。
获取显示器的DC:HDC hScreenDC = CreateDC("DISPLAY", NULL, NULL, NULL);
若想获取打印机的DC,一般是将第一个参数改为"WINSPOOL"。
3. 释放DC - ReleaseDC(HWND hWnd, HDC hDC)
该函数的作用是释放DC,使其他应用程序可以使用。
4. 释放DC的相关系统资源 - DeleteDC(HDC hDC)
这个函数并不常用,一般做图结束后,我们只需要调用ReleaseDC释放DC即可。
也许看到这里,对DC具体是个什么东西还没有什么清楚的概念。下面大概讲一下。。。
DC实际上是包含了一系列的图形对象,比如位图(Bitmap)、画刷(Brushe)、画笔(Pen)、字体(Font)、逻辑调色板(Logical palette)等等,GDI库中还定义了一系列接口函数,应用程序通过调用这些接口函数来操作当前DC中的图形对象,完成期望的绘图操作,最终影响放映到对应设备的输出上。
比如先创建一个窗口,然后得到该窗口对应的DC,这时候系统会为DC创建默认的图形对象(位图和路径除外),此时如果不进行任何操作,那么显示的窗口是一片白(也就是说刚开始是一块白色画布)。然后我们可以调用GDI库中的接口函数进行画图操作,或者载入位图(相当于在白色画布上作画)。DC中就是提供了对画布进行作画的工具。
注:DC对应设备的显示信息都存储在位图对象中。
为了更清楚DC的作用,下面举个很简单的例子:在屏幕上画一条线
首先,获得整个显示器的DC:
HDC hdC = GetDC(NULL);
创建新的画笔对象:
COLORREF cPen = RGB(0,0,0); //指定画笔颜色为黑色
HPEN hpen = CreatePen(PS_SOLID, 10, cPen); //创建新的画笔,返回新画笔的句柄
将新创建的画笔指定为DC的当前画笔:(对同一种类型的图形对象,DC中只能有一个当前对象)
HPEN hpenOld = SelectObject(hdc, hpen);
画线:
LineTO(hdc, 500, 500);
画图操作结束,还原画笔:
SelectObject(hdc, hpenOld);
释放画笔资源:
DeleteObject(hpen);
释放DC:
ReleaseDC(NULL, hdc);
二、下面来讨论使用OpenGL来绘图的相关知识:
使用OpenGL绘图与使用GDI库绘图是不同的,主要体现在:OpenGL采用的是RC(Render Context,渲染上下文)绘图。
DC和RC的区别和联系:
1. 在Windows中使用GDI绘图时必须指定在哪个DC中绘制,同样地,在使用OpenGL函数时也必须指定一个所谓的RC。正如设备上下文DC要存储GDI的绘制环境信息如笔、刷和字体等,RC也必须存储OpenGL所需的渲染信息如像素格式等。
2. Windows下的窗口和DC支持的位图格式(PIXELFORMAT)属性,和RC有位图结构上的一致。只要在创建RC时与一个DC建立联系,OpenGL函数就可以通过与RC对应的DC绘制到相应设备上。而实际上,RC只能通过建立了位图格式的DC来创建。
3. 一个DC对应的是一个图像输出设备,而一个RC对应的则是一个线程。一个线程只能拥有一个RC,而一个RC也只能属于一个线程,不能在线程中共有。若一个线程想要在不同的设备上绘图,只能通过更改与RC对应的DC来实现,而RC在线程总保持不变(当然也可以删除旧的RC,再利用其它设备的DC创建新的RC)。
下面来讲一下RC的创建。正如前面所说的,RC只能通过建立了位图格式的DC来创建。
1. 首先,我们需要创建一个窗口,然后使用GetDC函数得到这个窗口的DC。我们这里讨论在MFC使用OpenGL,因此窗口类是由工程自动创建的COpenglDemoView类,由于我们做的是绘图前的窗口初始化工作,因此我们将之后所有的操作都在OnCreate函数中完成。(至于一些必要的成员数据初始化工作,应放在View类的构造函数中)
HWND hWnd = this->GetSafeHwnd();
HDC hDC = ::GetDC(hWnd);
2. 然后,我们需要指定DC中的位图像素格式。这个工作是由GDI库中名为PIXELFORMATDESCRIPTOR的类来实现的。
第一步是填充这个数据结构:
PIXELFORMATDESCRIPTOR pixelDesc={
sizeof(PIXELFORMATDESCRIPTOR), //nsize:像素格式描述子结构的大小
1, //nVersion:PIXELFORMATDESCRIPTOR结构的版本,一般设为1
//dwFlags:一组表明像素缓冲特性的标志位
PFD_DRAW_TO_WINDOW | //使之能在窗口或者其他设备窗口画图
PFD_SUPPORT_OPENGL,// | //使之能使用OpenGL函数
//PFD_DOUBLEBUFFER, //指明使用了双缓冲
PFD_TYPE_RGBA, //PixelType:定义了显示颜色的方法
24, //cColorBits:指定了一个颜色的位数
0,0, //cRedBits, cRedShift:每个RGBA颜色缓冲区中红色位平面的数目和偏移数
0,0, //cGreenBits, cGreenShift:每个RGBA颜色缓冲区中绿色位平面的数目和偏移数
0,0, //cBlueBits, cBlutShift:每个RGBA颜色缓冲区中蓝色位平面的数目和偏移数
0,0, //cAlphaBits, cAlphaShift:每个RGBA颜色缓冲区中Alpah位平面的数目和偏移数
0, //cAccumBits:累加缓冲区中全部位平面的数目
0,0,0,0, //cAccumRedBits, cAccumGreenBits, cAccumBlueBits, cAccumAlphaBits
32, //cDepthBits:Z(深度)缓冲区的深度
0, //cStencilBits:模板缓冲区的深度
0, //cAuxBuffers:轴向缓冲区的数量(一般1.0版本不支持)
PFD_MAIN_PLANE, //iLayerType:忽略,为了一致性而包含的
0, //bReserved:表层和底层平面的数量
0,0,0 //dwLayerMask, dwVisibleMask, dwDamageMask
};
这个类中的大部分成员变量我们并不关心,最重要的是第三个参数dwFlags,其中指定的PFD_SUPPORT_OPENGL使得我们可以在这个窗口中使用OpenGL函数。
而指定的PFD_DOUBLEBUFFER则使得我们可以使用OpenGL中的双缓存机制(这个机制在制作动画时非常重要)。但奇怪的是当我选择了这个标志位时,生成的窗口没有图像(一片空白),而将这个标志位注释掉,就能显示我所绘制的图了。非常诡异,我也不明白为什么。。。
第二步是将这个像素格式选为当前DC的使用像素格式:
this->m_GLPixelIndex = ChoosePixelFormat(hDC, &pixelDesc);
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;
}
这段代码首先调用了ChoosePixelFormat函数来寻找OpenGL所支持的像素格式中,最接近所设置的像素格式,如果没有找到,就调用DescribePixelFormat函数来选择索引值为1的像素格式来填充设置的像素格式。这些操作完成后,保证了所设置的像素格式是OpenGL所支持的。
最后就可以调用SetPixelFormat函数来为指定当前DC的像素格式。
注:代码中的this->m_GLPixelIndex是我们自己添加的View类的protected型的成员变量,类型为int,用于保存所设置的像素格式在OpenGL所支持的像素格式列表中的索引值。
3. 设置像素格式完成后,就可以创建RC了。这里面要用到两个GDI库中的函数 wglCreateContext 和 wglMakeCurrent。
首先创建RC:
this->m_hGLContext = wglCreateContext(hDC);
然后选择新创建的RC成为当前DC对应的RC
wglMakeCurrent(hDC, this->m_hGLContext);
注:代码中的this->m_hGLContext是我们自己添加的View类的protected型的成员变量,类型为HGLRC,用于保存当前RC的句柄。
这样,RC就创建成功了。
RC创建成功后,我们就可以在View窗口中执行绘图操作了。其中,视窗、投影方式等设置放在OnSize函数中,而绘制操作则放在OnPaint函数中。
这里需要注意的是,MFC中没有提供类似GLUT中glutIdleFunc()的函数,而OnPaint函数只会在窗口创建或者窗口需要重绘的时候才会被调用。因此OnPaint函数中的绘图操作一般都是静态的,如果想绘制动画,则需要程序员自己写定时函数来控制窗口的重绘。
最后列一下参考的资料:
- MFC中添加OpenGL
WINDOWS下展示OpenGL有多种形式: MFC 或 win32,该如何向MFC中添加OpenGL?下面是介绍最简单OpenGL框架. 1.首先通过VS建立MFC应用程序-MyOpenGL,选择单 ...
- MFC中添加ToolTip提示框
PART 1 MFC 对话框中的 Buttton添加提示 例如我们想在一个对话框中的一个button控件添加tooltip,实现的方法如下: 1. 在该对话框的类中添加一个CToolTipCtrl类型 ...
- MFC中的主窗口修改标题
MFC中的主窗口修改标题 如何去掉“无标题”1.在主程序中的InitInstance(): m_pMainWnd->SetWindowText("你要显示的东西如果不想显示置空就行&q ...
- MFC中添加消息响应函数
转自:http://blog.csdn.net/eddy_liu/article/details/8474677 目前,用MFC设计的Windows应用程序几乎都采用文档/视图结构.这种程序框架与简单 ...
- MFC框架下Opengl窗口闪屏问题解决方案
转自https://blog.csdn.net/niusiqiang/article/details/43116153 虽然启用了双缓冲,但是仍然会出闪屏的情况,这是由于OpenGL自己有刷新背景的函 ...
- Windows MFC 两个OpenGL窗口显示与线程RC问题
问题为:背景界面是一个OpenGL窗口(对话框),在其上弹出一个OpenGL窗口(模态对话框)时, 1.上方的OpenGL窗口能响应鼠标操作等并刷新: 2.当移动或放大缩小上方的OpenGL窗口时,其 ...
- MFC中无标题栏窗口的移动
原文链接: http://blog.sina.com.cn/s/blog_6288219501015dwa.html 移动标准窗口是通过用鼠标单击窗口标题条来实现的,但对于没有标题条的窗口,就需要 ...
- MFC中添加了一个dialog,并创建了相应的类,初始化函数没有怎么办?
1.在头文件中添加 virtual BOOL OnInitDialog();2.在对于的CPP文件中添加 BOOL CXXDlg::OnInitDialog() { return TR ...
- MFC中获取各个窗口之间的句柄或者指针对象的方法
MFC在非常多的对话框操作中,我们常常要用到在一个对话框中调用还有一个对话框的函数或变量.能够用例如以下方法来解决. HWND hWnd=::FindWindow(NULL,_T("S ...
随机推荐
- vue 之 render 函数不能渲染非全局自定义函数-方案
import customCom from 'xxx.vue' render: (h) => { return h(customCom) }
- django创建路径导航
路径导航 : 1.怎样设置需要登录但又不需要验证权限的路径 : 在settings中定义一个列表,列表中以正则的方式放入需要登录但无需验证的权限的项.在 ...
- 标准库heapq的使用
转载自: https://blog.csdn.net/y472360651/article/details/80725355 查找最大或最小的N个元素 怎么样从一个列表中取出最大或最小的N个元素的列表 ...
- spring配置和映射文件
配置 <?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www. ...
- mousemove([[data],fn])
mousemove([[data],fn]) 概述 当鼠标指针在指定的元素中移动时,就会发生 mousemove 事件.大理石构件来图加工 mousemove事件处理函数会被传递一个变量——事件对象, ...
- Java进阶知识19 Struts2和Spring整合在一起
1.概述 1.Spring负责对象创建 2.Struts2负责用Action处理请求 3.整合的关键点:让Struts2框架Action对象的创建交给Spring完成. 2.整合实例 需要用到的 ...
- 牛客练习赛39 B.选点
链接:https://ac.nowcoder.com/acm/contest/368/B 来源:牛客网 题目描述 有一棵n个节点的二叉树,1为根节点,每个节点有一个值wi.现在要选出尽量多的点. 对于 ...
- svn 外部引用别的项目文件
建立了一个文件目录E:\My\myproject 想在该目录下有一个文件夹引用别的工程的文件. 1.在E:\My\myproject 空白处右键属性. 2.点击Properties,弹出 3.点击ne ...
- 在Android中使用OpenGL ES进行开发第(三)节:绘制图形
一.前期基础知识储备笔者计划写三篇文章来详细分析OpenGL ES基础的同时也是入门关键的三个点: ①OpenGL ES是什么?与OpenGL的关系是什么?——概念部分 ②使用OpenGLES绘制2D ...
- js和jQuery实现的Ajax
1. JS实现Ajax <!doctype html> <html lang="en"> <head> <meta charset=&qu ...