现在的位置: 首页 > 自动控制 > 工业·编程 > 正文

windows下opengl(二):freeglut和glew开发库

2014-10-28 19:25 工业·编程 ⁄ 共 7839字 ⁄ 字号 暂无评论

     第一章介绍了如何在Windows下仅使用VS或SDK自带库创建OpenGL环境的方法,并极简单的介绍了Windows平台上OpenGL的历史。微软开发OpenGL的历史如此之短,之后就全力开发自己的亲儿子:DirectX。从此之后Windows平台对OpenGL的内置支持就停留在OpenGL 1.1版。当然,在Vista之后,微软甚至开发了一个OpenGL到DirectX的转换层,支持到OpenGL 1.4,这使得系统在没有安装支持OpenGL的显卡驱动时仍然能够运行一部分基于OpenGL的程序。

        OpenGL并非为Windows而生,事实上在除了Windows的其它几乎所有操作系统平台上,OpenGL都扮演着更为重要的角色。

那么,在Windows平台上如何使用最新版本的OpenGL函数呢?这些函数从何而来呢?这一章主要谈论这两个问题。

2.1 访问OpenGL最新版本中的函数

        在第一章提到过,与其他任何库一样,OpenGL必须有能力操作系统中的显示设备。而现代操作系统都不允许应用直接操作硬件设备,因此对硬件的操作要么需要开发硬件驱动,要么需要使用操作系统提供的操作接口。

        微软已经差不多18年没有对OpenGL的支持做过重大更新了,显然windows操作系统不可能提供更多有关OpenGL的接口函数。这样,新的OpenGL函数只能通过驱动程序提供。

        事实也正是如此,OpenGL提供了一组规范,但这些规范不是给应用程序人员用的,是提供给显卡开发商的,根据这组规范,显卡开发商开发的显卡驱动可以提供对OpenGL的支持。比如在我的机器上,GeForce 605的显卡驱动就提供了对OpenGL4.3.0的支持。

        所有库的开发中,能够设计一个预留的“升级接口”是非常高明的,OpenGL就是如此。opengl32.dll是windows系统自带的对OpenGL驱动访问的接口库,从windows 95开始,只要装好系统它就存在。在windows 7上通过VS的dumpbin.exe查看opengl32.dll可以看到它导出了368个函数。

      其中:

       125  7C 00003CED glGetString

       356  163 0003C245 wglGetProcAddress

       这两个函数非常重要,glGetString函数在第一章中已经使用过了,利用它可以了解系统中OpenGL相关的各种信息。

        wglGetProcAddress也是个非常重要的函数,通过给出OpenGL函数名,就可以利用wglGetProcAddress获取函数指针,从而可以访问到openGL最新版本中的所有函数。

注意:只要opengl32.dll能支持的OpenGL函数,当应用程序调用时,OpenGL32.dll总是将其“转发”给显卡驱动的实现,因此总是最新的实现,而不是1.1版本的实现。

        现在,可以整理一个在windows平台使用OpenGL最新版本函数的步骤:

         1) 使用第1章中的知识点创建OpenGL环境

         2) 从OpenGL官方网站或任何其它文档中查找OpenGL函数名

         3) 调用wglGetProcAddress(“functionname”)获得函数地址

         4) 组织参数,利用函数指针调用特定函数

2.2 使用第三方库

按照2.1节中的步骤去使用OpenGL在理论上是完全可行的,但在实践上存在两个主要困难:

         1)每次由程序员手工编码完成2.1节中的4个步骤是非常烦人的事情

         2)仅仅有了函数指针还是不够的,还需要知道调用这个函数所需要的数据类型,

            相关宏定义、相关枚举值,……,总之这是一个非常复杂的事情

因此,就有了第三方库,第三方库基本上就是根据OpenGL规范,由一群非常熟悉OpenGL的人弄出来的(否则怎么能知道每个枚举值?知道每个结构体的定义?)。

第三方库有不少,但最常见于各类书籍和代码的有两个:1)glut/freeglut  2)glew。当然其它的还有glee、glfw……。

2.2.1 GLEW库

        Glew更多的是提供了对OpenGL扩展函数的支持。当然也包括基本OpenGL函数的支持。但它不提供其它类似窗体,键盘事件的支持函数。在与freeglut混和使用时,应该将#include <gl/glew.h>放在#include<gl/glut.h>之前,这样可以保证使用了最新版本的OpenGL函数。

      Glew又分为了glew和glew_mx,前者是单线程版,后者是线程安全版。因为OpenGL的Rendering Context限制了每线程一个。如果需要在一个程序里同时出现多个OpenGL窗口,用glew mx是更好的方案。

2.2.2 glut/freeglut

        Glut和freeglut,前者已经停止开发了,至少是进展缓慢。后者是前者的替代品,目前正在迅速开发中,能保持与最新的OpenGL一致。该库的主要优点是提供了一组简单的“窗体、鼠标、键盘事件”操作函数,能方便OpenGL学习程序的开发。当然该库也支持基本的OpenGL函数。

2.3 如何选择第三方库

       如果只是需要使用最新的OpenGL函数,那么就选择Glew库,这时只需要使用第1章中的知识创建好OpenGL环境,然后使用glew中导出的函数即可。

        如果想按照一些书籍上的例子进行实践,那一般是要选择freeglut或将其与glew结合使用。

        既然有了第三方库,后续的章节就不再研究如何使用“纯粹”的windows自带的OpenGL库,而是研究如何使用第三方库进行开发。

2.4使用glew的示例

这一节对1.3中的测试程序进行尽可能少的扩充,使其成为一个简单的仅仅使用glew库,且是窗口形式的OpenGL程序。创建一个空的win32项目,链接库需要opengl32.lib 和glew32.lib

#include <Windows.h>

#include <gl/glew.h>

#include <GL/wglew.h>

///////////////////////////////////////////////////////////////////////////////

//窗口回调函数

LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 

switch (uMsg)   

    {   

case WM_SIZE://与OpenGL交互

        glColor3f(1.0,1.0,0.0); 

        glBegin(GL_TRIANGLES); 

        glVertex2f(-1.0,0.0); 

        glVertex2f(1.0,0.0); 

        glVertex2f(0.0,1.0); 

        glEnd(); 

        glFlush(); 

break; 

case WM_ACTIVATE://与OpenGL交互

        glColor3f(1.0,0.0,1.0); 

        glBegin(GL_TRIANGLES); 

        glVertex2f(-1.0,0.0); 

        glVertex2f(1.0,0.0); 

        glVertex2f(0.0,1.0); 

        glEnd(); 

        glFlush(); 

break; 

case WM_CLOSE:   

if(MessageBox(hWnd,TEXT("你要关闭窗口吗?"),TEXT("提示!"),MB_OKCANCEL) == IDOK)   

            DestroyWindow(hWnd);   

break;   

case WM_DESTROY:   

        PostQuitMessage(0);   

return 0;   

default:   

return DefWindowProc(hWnd,uMsg,wParam,lParam);   

    }   

return 0;   

////////////////////////////////////////////////////////////////////

// 入口函数

int _stdcall WinMain(HINSTANCE hInst,HINSTANCE hPreInstance,LPSTR lpCmdLine,int nShowCmd) 

HWND         hWnd; 

    HGLRC        hRC; 

HDC          hDC; 

HINSTANCE    hInstance; 

    hInstance = hInst; 

    WNDCLASS     windClass; 

TCHAR szWindowName[50] =  TEXT("OpenGL_Window"); 

TCHAR szClassName[50]  =  TEXT("OpenGL_Class"); 

//初始化窗口结构体

    windClass.lpszClassName = szClassName; 

    windClass.lpfnWndProc   = (WNDPROC)WndProc; 

    windClass.hInstance     = hInstance;  

    windClass.hCursor       = LoadCursor(NULL, IDC_ARROW); 

    windClass.hIcon         = LoadIcon(NULL, IDI_WINLOGO); 

    windClass.hbrBackground = NULL;   

    windClass.lpszMenuName  = NULL;     

    windClass.style         = CS_HREDRAW | CS_OWNDC | CS_VREDRAW;  

    windClass.cbClsExtra    = 0;    

    windClass.cbWndExtra    = 0;  

//注册窗口类

if(!RegisterClass( &windClass )) return 1; 

//创建窗口

    hWnd = CreateWindowEx(0,     // 窗体扩展风格

        szClassName,    // 窗体类名称

        szWindowName,   // 窗体名称

        0,              // 窗体风格

        0,              // 窗体在桌面上的位置,x坐标

        0,              // 窗体在桌面上的位置, y坐标

        0,              // 宽

        0,              // 高

        NULL,           // 父窗口

        NULL,           // 菜单

        hInstance,      // 实例

        NULL); 

    hDC = GetDC(hWnd); 

    PIXELFORMATDESCRIPTOR pfd; 

    SetPixelFormat( hDC, 1,&pfd); 

    hRC = wglCreateContext( hDC ); 

    wglMakeCurrent( hDC, hRC ); 

//一旦wgl被初始化,可以撤消相关环境

//之后可以利用wgl函数,调用最新版的OpenGL建立真正使用的环境

    GLenum ret = glewInit(); 

if (GLEW_OK != ret) 

    { 

        MessageBox(NULL,(LPCSTR)glewGetErrorString(ret),"glew初始化",MB_OK); 

    } 

//释放OpenGL环境,wgl函数已经在调用glewInit()时完成了初始化

//释放环境后仍然可以使用

    wglMakeCurrent(NULL, NULL); 

    wglDeleteContext(hRC); 

    ReleaseDC(hWnd, hDC); 

//DestroyWindow(hWnd);

//下面创建真正可用的OpenGL渲染环境

//第1)步:创建一个真正可用的窗口

DWORD dwExtStyle; 

DWORD dwWindStyle; 

    dwExtStyle  = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE; 

    dwWindStyle = WS_OVERLAPPEDWINDOW; 

    ShowCursor(TRUE); 

// Create the window again

    hWnd = CreateWindowEx(dwExtStyle,     // 窗体扩展风格

        szClassName,    // 窗体类名称

        szWindowName,   // 窗体名称

        dwWindStyle | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,// 窗体风格

        CW_USEDEFAULT,              // 窗体在桌面上的位置,x坐标

        CW_USEDEFAULT,              // 窗体在桌面上的位置, y坐标

        200,              // 宽

        200,              // 高

        NULL,           // 父窗口

        NULL,           // 菜单

        hInstance,      // 实例

        NULL); 

    hDC = GetDC(hWnd); 

int nPixCount = 0; 

//利用OpenGL查询函数查找最符合要求的OpenGL环境特性

//需要#include <gl/wglew.h>

int pixAttribs[] = { WGL_SUPPORT_OPENGL_ARB, GL_TRUE,  //要求支持OpenGL

        WGL_DRAW_TO_WINDOW_ARB, GL_TRUE,                   //可以绘制到某个窗口

        WGL_ACCELERATION_ARB,   WGL_FULL_ACCELERATION_ARB, //必须完全支持硬件加速

        WGL_RED_BITS_ARB,       8,                         //红色为8位精度

        WGL_GREEN_BITS_ARB,     8,                         //绿色为8位精度

        WGL_BLUE_BITS_ARB,      8,                         //蓝色为8位精度

        WGL_DEPTH_BITS_ARB,     16,                        //深度为16位精度

        WGL_PIXEL_TYPE_ARB,      WGL_TYPE_RGBA_ARB,        //使用RGBA类型的像素

        0};                                                //必须以0结尾

int nPixelFormat  = -1; 

//查询系统中是否存在满足要求的环境像素格式

    wglChoosePixelFormatARB(hDC, &pixAttribs[0], NULL, 1, &nPixelFormat, (UINT*)&nPixCount); 

if(nPixelFormat == -1)  

    { 

//不存在满足要求的环境,则释放数据,结束程序

        ReleaseDC(hWnd, hDC); 

        DestroyWindow(hWnd); 

        MessageBox(NULL,"OpenGL不支持查询的格式","错误",MB_OK); 

return 1; 

    } 

else

    { 

//找到满足要求的格式,则设置其为当前像素格式,并创建OpenGL环境

        SetPixelFormat( hDC, nPixelFormat, &pfd ); 

//指定OpenGL版本为1.10

        GLint attribs[] = {WGL_CONTEXT_MAJOR_VERSION_ARB, 1,WGL_CONTEXT_MINOR_VERSION_ARB,  1,  0 }; 

        hRC = wglCreateContextAttribsARB(hDC, 0, attribs); 

if (hRC == NULL) 

        { 

            MessageBox(NULL,"无法创建OpenGL环境","错误",MB_OK);      

return 2; 

        } 

//成功则绑定OpenGL渲染环境(hRC)至窗口设备环境(hDC)

        wglMakeCurrent( hDC, hRC ); 

//显示窗口

        ShowWindow( hWnd, SW_SHOW ); 

        UpdateWindow(hWnd); 

//使用旧风格的OpenGL创建一个三角形

//新风格的OpenGL编程需要创建GLSL文件

//为了简单,先使用这种古老的glBegin...glEnd风格

        glColor3f(1.0,0.0,0.0); 

        glBegin(GL_TRIANGLES); 

        glVertex2f(-1.0,0.0); 

        glVertex2f(1.0,0.0); 

        glVertex2f(0.0,1.0); 

        glEnd(); 

        glFlush(); 

    } 

//进入窗口主循环

    MSG Msg;   

while(GetMessage(&Msg,NULL,NULL,NULL))   

    {   

        TranslateMessage(&Msg);   

        DispatchMessage(&Msg);   

    }   

    ReleaseDC(hWnd,hDC); 

return 0; 

2.5 运行结果

调整窗口大小时,消息处理函数会收到WM_SIZE消息,窗口从最小化或被遮挡等状态重新变成“激活”状态时,消息处理函数会收到WM_ACTIVATED消息,为了显示与OpenGL的交互,在两个消息中绘制了不同颜色的三角形。

说明:

左图:初始状态下绘制的三角形                             

中图:调整窗口大小时绘制的三角形

右图:激活窗口时绘制的三角形

给我留言

留言无头像?