文档章节

用多线程方法实现在MFC/WIN32中调用OpenGL函数并创建OpenGL窗口

guoliang
 guoliang
发布于 2014/05/29 22:37
字数 3003
阅读 579
收藏 1
点赞 0
评论 0

OpenGL相关的工具库中的OpenGL程序往往都是在C函数main中初始化和创建的,使用控制台来完成显示和控制颇为不便。如果能够在MFC中OpenGL函数并创建OpenGL窗口,并且可以将控制参数传入给OpenGL则可以得到很好的交互性能。自己查找很多文献资料,貌似都是说要在MFC中显示OpenGL都是通过微软的wgl扩展来完成,但是wgl很早就停止更新了的并且自己写的wgl运行框架尽管有些时候可以使用但在自己的电脑上却总是发现有运行有内存泄漏的问题并且加载opengl程序也非常慢,也许是显卡驱动的问题?但自己装的英伟达的GTX560显卡并且是最新的显卡驱动,也还是存在这个问题,看来应该是自己写的和参考的wgl框架有bug。本来是想用glut的却发现也是很久就停止更新了,只有freeglut不错,今年初还出了更新的。于是决定使用glew+freeglut来实现这个想法。

不过有一个问题就是glut的初始化函数往往都是写的glutInit(&argc,argv);其中argcargv两个参数是从控制台下的C函数的main(int argc,char *argv[])中传过来的,argc记录的是命令行中输入参数的数目,argv是一个拥有argc个元素的字符串数组,每个元素保存一个命令行中输入的参数。但是在MFC中默认是不会生成控制台窗口的,而实际上如果蹦出个控制台来控制opengl也不太好(这是可以实现的,可以参考《GUI程序也能使用控制台窗口》等文章http://www.cppblog.com/wish/articles/23642.html)偶然看到pfan论坛的eastcowboy的一个帖子《OpenGL入门学习——写给想用计算机画图的朋友》中提到可以在WinMain中初始化并且创建OpenGL窗口,通过自定义argcargv参数的值达到了欺骗glutInit完成初始化并且不依赖与命令行/控制台窗口。个人觉得既然可以自定义初始化参数,那么就实际上可以在MFC中的任何地方初始化和创建OpenGL窗口了。但是为了不影响MFC正常工作、完成与用户交互即UI的功能,可以通过多线程来完成,即可以将OpenGL的运行封装在一个工作线程的run函数中(这实际上真是有趣:这个名义上的工作线程却是可以创建窗口显示的)。

这里给出一个MFC对话框实现的例子:

第一步就是创建一个MFC对话框应用程序,我这里取名MFCwithOpenGLWindow。第二步添加一个启动OpenGL按钮IDC_STARTOPENGL及其对应的消息响应函数OnStartOpengl().

第三步就是添加相应的函数。在对话框实现MFCwithOpenGLWindowDlg.cpp文件顶部添加两个全局函数声明(因为是子线程函数,必须是全局的)

int OpenGLThread(LPVOID lpv);//线程run函数
void DisplayGLView(void);//被线程函数调用以在OpenGL窗口中绘图显示的函数

接下来在对话框实现MFCwithOpenGLWindowDlg.cpp文件中给出线程函数的实现(这里借鉴了《OpenGL入门学习——写给想用计算机画图的朋友》帖子中的程序,只不过添加了显示绘图的颜色为红色)

void DisplayGLView()//被线程函数调用以在OpenGL窗口中绘图显示的函数
{
      glClear(GL_COLOR_BUFFER_BIT);//清空颜色缓冲
      glColor3f(1.0,0.0,0.0);//设置绘图为红色
      glRectf(-0.5f,-0.5f,0.5f,0.5f);//绘制一个正方形
      glFlush();//刷新显示缓冲完成显示
}
int OpenGLThread(LPVOID lpv)//线程run函数
{
      //获得线程创建时对话框传入的参数,可以通过这里传递给opengl一些控制参数
      CMFCwithOpenGLWindowDlg* pTaskMain = (CMFCwithOpenGLWindowDlg *) lpv;
      int argc=1;
      char *argv[]={"OpenGL Thread "};
      glutInit(&argc,argv);//初始化glut
      glutInitDisplayMode(GLUT_RGB|GLUT_SINGLE);//设置显示模式
      glutInitWindowPosition(100,100);//设置opengl窗口显示位置
      glutInitWindowSize(400,400);//设置opengl窗口大小
      glutCreateWindow("OpenGL Thread Window");//设置opengl窗口标题
      glutDisplayFunc(&DisplayGLView);//调用显示回调函数绘图
      //调用freeglut中的函数设置opengl窗口被关闭和结束返回时可以继续执行glutMainLoop后面的部分
      glutSetOption(GLUT_ACTION_ON_WINDOW_CLOSE, GLUT_ACTION_GLUTMAINLOOP_RETURNS);
      glutMainLoop();//进入glut循环
      //AfxMessageBox("it end");
      AfxEndThread(0);//结束线程返回,以免出现意外结束
      return 1;//结束时返回
}



    红色部分非常重要,首先通过调用glutSetOption设置了opengl窗口被关闭时或者其他结束响应事件发生之后可以继续执行glutMainLoop死循环后面的语句,一方面可以正确地调用AfxEndThread函数结束子线程而不致于opengl对话框关闭时之间调用exit(0)MFC主对话框也给关闭了即结束了主线程,还可以完成一些清理工作,比如相应的内存释放等以免造成内存泄漏事件。该函数需要freeglut的支持,在glut中是没有这个函数的,由此可以看到freeglutglut的完善和补充,,使得程序更为可靠和安全了。

最后就是在启动OpenGL按钮对应的消息响应函数OnStartOpengl()中创建opengl线程,将opengl窗口显示出来,这里比较简单只有一句:

      CWinThread *TEST1=AfxBeginThread((AFX_THREADPROC)OpenGLThread,this);//创建opengl线程

如此,编译程序并运行,点击启动OpenGL按钮就可以得到一个绘制了一个红色正方形的opengl窗口,如下图所示

为了使程序编译通过,需要下载和安装glewfreeglut库,并且在MFCwithOpenGLWindowDlg.cpp文件中需要引用对应的头文件,在VC6.0或者VS平台中设置工程的头文件,库函数以及可执行dll文件的目录,这里为了方便使用已将相关的库的文件放置在了工程目录下,仅需要在MFCwithOpenGLWindowDlg.cpp文件中设置如下:

#include "./include/GL/glew.h"
#include "./include/GL/freeglut.h"
#pragma comment(lib,"glew32.lib")
#pragma comment(lib,"freeglut.lib")

    为了验证可以通过MFCOpenGL窗口绘图进行控制,这里进一步实现了一个旋转前面绘制出来的红色正方形。

首先在前面的对话框中再添加一个按钮IDC_ROTATEGLVIEW用于旋转GL绘图。

然后添加该按钮的消息响应函数OnRotateGLview(),实现如下:

   

csfortangle.Lock();       //临界区加锁
      rtangle=rtangle+5.0f;        //增加旋转角度
      if (rtangle>180.0f)       //处理旋转角度范围
      {
             rtangle=0;
      }
      csfortangle.Unlock();    //临界区开锁

其中csfortangle是rtangle角度对应的临界区对象用于实现线程之间对rtangle变量的访问控制使得每时刻仅有一个线程对其操作,在对话框实现文件的顶部定义的:

GLfloat rtangle=0;                        //旋转角度

CCriticalSection csfortangle;  //控制旋转角度线程之间访问同步的临界区对象

同时要使用临界区对象,必须加上对应的头文件:

#include "Afxmt.h"            //引入MFC中的线程/进程同步类,这里使用临界区对象。

为了实现真正的旋转绘制的图形,在显示回调函数DisplayGLView中修改如下,即增加红色部分:

      glClear(GL_COLOR_BUFFER_BIT);//清空颜色缓冲
      glLoadIdentity();                             //复位当前模型视角矩阵
      csfortangle.Lock();                                //临界区加锁
      glRotatef(rtangle,0.0f,0.0f,1.0f);   //绕Z轴旋转绘制的图形
      csfortangle.Unlock();                     //临界区开锁
      glColor3f(1.0,0.0,0.0);//设置绘图为红色
      glRectf(-0.5f,-0.5f,0.5f,0.5f);//绘制一个正方形
      glFlush();//刷新显示缓冲完成显示最后编译运行。



  
  测试时创建 opengl 窗口后首先点击对话框中的“旋转 GL 视图”按钮增加旋转的角度,然后鼠标切换到 opengl 窗口点击一下(为了产生一个点击事件使得 glut 能够调用显示回调函数 DisplayGLView 旋转图形并刷新显示,也可以通过定时等事件来达到相应的目的)就可以看到旋转后的图形。

如果不想手动去切换,可以通过glut中的定时事件来触发显示函数回调。

在前面程序基础上实现如下:

      首先添加一个glut定时timerEvent回调函数,相当于MFC中的ontimer定时器响应函数:

void timerEvent(int value)//GLUT定时回调函数
{
   glutPostRedisplay();//发送重新显示消息,刷新显示
   glutTimerFunc(REFRESH_DELAY, timerEvent, 0);//重置定时器
}



然后修改OpenGLThread函数如下:

int OpenGLThread(LPVOID lpv)//线程run函数

{

      //获得线程创建时对话框传入的参数,可以通过这里传递给opengl一些控制参数

      CMFCwithOpenGLWindowDlg* pTaskMain = (CMFCwithOpenGLWindowDlg *) lpv;

 

      int argc=1;

      char *argv[]={"OpenGL Thread "};

      glutInit(&argc,argv);//初始化glut

      glutInitDisplayMode(GLUT_RGB|GLUT_SINGLE);//设置显示模式

      glutInitWindowPosition(100,100);//设置opengl窗口显示位置

      glutInitWindowSize(400,400);//设置opengl窗口大小

      glutCreateWindow("OpenGL Thread Window");//设置opengl窗口标题

      glutDisplayFunc(&DisplayGLView);//注册显示回调函数绘图

      glutTimerFunc(REFRESH_DELAY, timerEvent, 0);//注册定时回调函数

      //调用freeglut中的函数设置opengl窗口被关闭和结束返回时可以继续执行glutMainLoop后面的部分

      glutSetOption(GLUT_ACTION_ON_WINDOW_CLOSE, GLUT_ACTION_GLUTMAINLOOP_RETURNS);

      glutMainLoop();//进入glut循环,处理响应事件

      //AfxMessageBox("it end");

      AfxEndThread(0);//结束线程返回,以免出现意外结束
      return 1;//结束时返回
}



其中REFRESH_DELAY为文件顶部的定时时间ms数值宏定义,这里取200ms,重新编译并运行程序就可以实现在MFC中点击“旋转GL视图”即可连续地旋转绘制的正方形了。

为了验证glut窗口中也能够通过按钮实现正方形旋转的控制,可以进一步添加一个全局按钮控制回调函数:

void keyboard(unsigned char key, int /*x*/, int /*y*/)//GLUT按键回调函数
{
             switch(key)
             {
                    case '+'://+号实现旋转的逆时针角度增加
                           {
                                  csfortangle.Lock();        //临界区加锁
                                  rtangle=rtangle+5.0f;          //增加旋转角度
                                  if (rtangle>180.0f)        //处理旋转角度范围
                                  {
                                         rtangle=0;
                                  }
                                  csfortangle.Unlock();     //临界区开锁
                           }
                           break;
                    case '-'://-号实现旋转的顺时针角度增加
                           {
                                  csfortangle.Lock();        //临界区加锁
                                  rtangle=rtangle-5.0f;    //增加旋转角度
                                  if (rtangle<-180.0f)        //处理旋转角度范围
                                  {
                                         rtangle=0;
                                  }
                                  csfortangle.Unlock();     //临界区开锁
                           }
                           break;
                    default:
                           break;
             }
}



并且在创建线程时将函数注册到glut中:

      glutKeyboardFunc(keyboard);//注册键盘按钮控制回调函数

编译之后运行就可以看到既可以通过glut的窗口中通过键盘按钮控制正方形旋转,也可以通过MFC对话框按钮实现。

 

补充:

如果要通过主线程如UI线程来结束自己创建的opengl线程,则可以在自己定义的显示函数或者定时器处理函数中加上一个条件变量如Boolean型的变量,当该变量条件为真时就调用freeglut的退出循环函数glutLeaveMainLoop(),它将直接跳出glutMainLoop,接着执行AfxEndThread()结束线程返回,从而实现提前退出和结束线程。

其实通过阅读freeglut的源程序可以发现windows平台上它实际上对窗口的绘制对事件的响应都是对windows API的封装,尤其对于窗口和图形的绘制也是对微软wgl的封装,如fgSetWindow函数中封装了wglMakeCurrent函数和ReleaseDC函数,fgWindowProc函数则更是专门封装了对应win32窗口处理的函数,其中封装了wglCreateContextwglGetCurrentContext等等函数。

 

最后给出本文中的示例源代码下载地址:

http://download.csdn.net/detail/menglongbor/4268748

参考文献:

GUI程序也能使用控制台窗口,http://www.cppblog.com/wish/articles/23642.html

OpenGL入门学习——写给想用计算机画图的朋友,http://bbs.pfan.cn/post-184355.html

OpenGL内存泄漏之主循环函数glutMainLoop()

http://blog.csdn.net/ronggang175/article/details/6068854

坑爹的sshglutMainLoop

http://hi.baidu.com/tyxxybd/blog/item/64f6040f698402306159f337.html

本文转载自:http://blog.csdn.net/menglongbor/article/details/7524864

共有 人打赏支持
guoliang
粉丝 26
博文 131
码字总数 27457
作品 0
杭州
程序员
VC++6.0中OpenGL应用程序开发

1. Win32控制台方式 建立Win32 控制台程序,在头文件中加入 2. MFC方式 (1)采用VC AppWizard向导创建空的MFC(EXE)工程框架,整个过程总共6步,值得指出的是一般情况下在向导的第1步选择创...

robslove
2015/04/06
0
0
【GoogleSamples】源码研究 - hello-gl2

简单介绍 hello-gl2项目是用来展示如何用jni的方式来使用OpenGL ES 2.0。所谓JNI的方式,是以Java代码为主,在Java代码中调用C++代码去实现功能。与之相对的,可以以C++代码为主,在C++代码中...

闪电的蓝熊猫
05/19
0
0
Mac OS X下如何使用OpenGL

作为3D的工业标准,OpenGL在苹果上可以说无处不在,用起来也是分外的舒服。 一般OpenGL需要搭配一些窗口库来做,比如Qt、MFC等,或者用跨平台的GLUT。 好在GLUT是Mac OS X自带的,所以直接用...

Jian-Yong
2013/01/17
0
3
通过FFmpeg解码和OpenGL的YUV转RGB实现Android视频播放

前言 在我的博文 https://blog.csdn.net/ericbar/article/details/80506390 中,我们在Android平台上,实现了通过FFmpeg在native(C/C++)层进行视频解码,并通过OpenGL实现了硬件渲染工作,...

ericbar
05/30
0
0
ubuntu下eclipse OpenGL编程

转载美文:解惑 本文假设读者已经在ubuntu下配置好了gcc/g++环境,并使用eclipse开发opengl程序 在ubuntu终端下运行以下命令,安装opengl所需要的库文件 $ sudo apt-get install build-essen...

我是一只鱼
2011/11/02
0
0
MFC+OpenGL三维绘图(一)——简单绘图平台的搭建与实现图像的旋转、缩放

声明:本文章为小白本人第一次创作,文章可能会有诸多不足,希望大家批评指正! VS2013下载:https://pan.baidu.com/s/1Y7TuZlLaGsbj2KCZVuckw OpenGL下载:https://download.csdn.net/downl...

belence_zhao
05/11
0
0
OpenGL学习入门之VS2010环境配置 [转]

OpenGL开发环境简介    基于OpenGL标准开发的应用程序运行时需有动态链接库OpenGL32.DLL、Glu32.DLL,这两个文件在安装Windows NT时已自动装载到C:WINDOWSSYSTEM32目录下(这里假定用户将W...

IMGTN
2012/07/24
0
0
VC++如何编写一个语音识别的类for Micosoft speech Api5.1

1)在构造语音类之前,必须先设置好工程环境: a、从微软官方网站下载windows speech sdk并安装,然后在Visual Studio 6.0中进行相关设置,在Project Setings选项的C++选项卡的“分类:预处理...

junwong
2012/03/09
0
0
iPhone游戏开发笔记(一):使用到的工具和技术

从小到大都有游戏梦,所以希望能开发出自己的游戏。iPhone游戏开发主要用到这些技术和工具:Objective-C或C/C++, Xcode, UIKit, Quartz 2D, Core Animation, OpenGL, audio APIs, networkin...

晨曦之光
2012/03/09
0
0
OpenGL入门(一)freeglut和glew的编译

写在前面 最近对OpenGL编程又双叒产生了浓厚的兴趣,决定把学习OpenGL过程中学到的知识都整理到博客中来,一来方便日后查看,而来也是为了和诸位大佬共勉。有不当的地方还望诸位批评指正,谢...

renkai0406
04/23
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

利用世界杯,读懂 Python 装饰器

Python 装饰器是在面试过程高频被问到的问题,装饰器也是一个非常好用的特性, 熟练掌握装饰器会让你的编程思路更加宽广,程序也更加 pythonic。 今天就结合最近的世界杯带大家理解下装饰器。...

p柯西
18分钟前
0
0
Xshell登录阿里云服务器ECS

Xshell登录阿里云服务器ECS 1. 参考资料: 1). 《阿里云服务器怎么用?阿里云服务器使用教程》 链接:http://www.cr173.com/html/50758_1.html 2). eagle-zhang的CSDN博客《Xshell连接不上阿...

SuShine
28分钟前
1
0
IDEA中的HTTP Client Editor测试API

在前后端分离项目,前后端通过api进行通信。如果用postman免费版进行api测试的话,由于无法保存测试脚本到文件,不方便前端查看。 你可以选择付费版。也可以利用IDEA自带的HTTP Client Edito...

hutaishi
31分钟前
0
0
解决“只能通过Chrome网上应用商店安装该程序”的方法

摘要 : 最近有些用户反映某个Chrome插件在安装的时候,提示“只能通过Chrome网上应用商店安装该程序”,为了解决这一问题,Chrome插件网带来了相关的解决方法。 某些用户在Chrome插件网下载了...

沧海一刀
32分钟前
0
0
通过UNIX域套接字传递文件描述符

  传送文件描述符是高并发网络服务编程的一种常见实现方式。Nebula 高性能通用网络框架即采用了UNIX域套接字传递文件描述符设计和实现。本文详细说明一下传送文件描述符的应用。 1. TCP服务...

Bwar
35分钟前
0
0
python操作Excle

# -*- coding: utf-8 -*-from openpyxl import load_workbook, Workbook#index:第几个sheet页,第一个sheet页的index为0def readExcle(filename,index): # 加载excle文件 wb = l......

淺陌离殇
37分钟前
0
0
Apache爆日志文件漏洞

全球使用最广泛的Web服务器Apache近日被爆出了一个安全漏洞,该漏洞可能导致攻击者控制服务器。 该漏洞包含在mod_rewrite 模块中的do_rewritelog()日志函数中。由于该函数还无法完全过滤写入...

问题终结者
今天
0
0
阿里巴巴内部开发手册

现代软件架构的复杂性需要协同开发完成,如何高效地协同呢?无规矩不成方圆,无规范难以协同,比如,制订交通法规表面上是要限制行车权,实际上是保障公众的人身安全,试想如果没有限速,没有...

zbbmaster
今天
0
0
34.任务计划cron chkconfig systemctl管理服务 unit target

10.23 linux任务计划cron 10.24 chkconfig工具 10.25 systemd管理服务 10.26 unit介绍 10.27 target介绍 10.23 linux任务计划cron: 在linux中任务计划是必不可少的,因为可能我们凌晨的时候...

王鑫linux
今天
0
0
logback.xml for spring boot

logback.xml config <?xml version="1.0" encoding="UTF-8"?><configuration> <conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.Colo......

qwfys
今天
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部