Win32 OpenGL 编程(1)Win32下的OpenGL编程必须步骤

一、    前言

人生在于折腾,继续折腾吧。 OpenGL 编程的红宝书《 OpenGL Programming Guide 》在举例子的时候为了平台无关,使用的都是 GLUT 来管理窗口,个人感觉不爽 -_-! 要是针对 Windows 平台,个人倾向使用 Win32(MFC 也行 ) ,要是跨平台,我还会 Qt 嘛, Qt 对 OpenGL 也有很好的支持的,为啥还折腾个新的窗口管理组件?虽然说 GLUT 比较简单,但是还是不喜欢扭曲的适应之,何况我去看了下 GLUT 这个东西,最新的版本都是 2001 年发布的了,什么古董级的家伙啊,更加不想用了,还是在 Windows 平台上学习 OpenGL 吧。刚开始这样想的就这样做了,结果比我想象的稍微复杂一些,原来不光是熟悉 Win32 API 就能随便搞掂的,当时还看到有人专门为此写了篇论文 -_-!( 不知道学历 ) 吓到我了,没有那么高的学术研究价值吧?后来又看到 3 个研究生都开始为此写论文了(这还真是研究院中的人写的),感叹不已。。。。。。。。。。

二、  提要

本文主要介绍 Win32 下的 OpenGL 编程需要的一些操作,以 Andre LaMothe 的 T3D Game Console 为 Win32 框架实现一个 Win32 下的 OpenGL 游戏编程框架, 以参考资料 2 为蓝本,实现一些 OpenGL 示例。以后的讲解围绕此框架展开。本文假设读者已经具备基本的 Win32 编程知识,不讲解 Win32 编程中固有的要素,需要了解 Win32 编程的,建议学习 Charles Petzold 的《 Programming Windows 》。

三、    Win32 下 OpenGL 编程需要的操作步骤

下面会用到的全局变量:

// GLOBALS

HWND       ghWnd ; // 窗口句柄

HINSTANCE ghInstance ; // 程序实例句柄

HDC ghDC ;                             // GDI 设备环境句柄

HGLRC ghRC ;           // 渲染环境句柄

1.       头文件

#include <windows.h>

// OpenGL 需要的头文件

#include <GL/gl.h>

#include <GL/glu.h>

需要注意的就是,必须先包含 Windows.h ,然后才能包含 gl.h 和 glu.h 。因为 gl.h 与 glu.h 中可能包含 Windows.h 中定义的宏。

2.       链接库

此步完全可以通过功能配置来完成,需要包含的库 为 opengl32.lib 何 glu32.lib ,事实上,为了方便,可以通过如下语句来完成( VC++ 特有特性),但是我们讨论的是 Win32 下的 OpenGL ,这样也能接受了。

// 定义程序链接时所需要调用的OpenGL 程序库, 简化工程配置

#pragma comment ( lib , "opengl32.lib" )

#pragma comment ( lib , "glu32.lib" )

3.       像素格式 (Pixel Format) 设置

需要用到的函数的原型:

int ChoosePixelFormat(
  HDC  hdc,  // device context to search for a best pixel format 
             // match
  CONST PIXELFORMATDESCRIPTOR *  ppfd
             // pixel format for which a best match is sought

);

BOOL SetPixelFormat(
  HDC  hdc,  // device context whose pixel format the function 
             // attempts to set
  int  iPixelFormat,
             // pixel format index (one-based)
  CONST PIXELFORMATDESCRIPTOR *  ppfd
             // pointer to logical pixel format specification
);

这是 Win32 下的 OpenGL 编程必做的事情之一,为 DC 设置像素的格式。

// 设置像素格式

PIXELFORMATDESCRIPTOR pfd ;

int iFormat ;

ghDC = GetDC ( ghWnd );

ZeroMemory ( & pfd , sizeof ( pfd ) );

pfd . nSize = sizeof ( pfd );

pfd . nVersion = 1;      // 版本,一般设为

pfd . dwFlags =   PFD_DRAW_TO_WINDOW | // 一组表明象素缓冲特性的标志位

PFD_SUPPORT_OPENGL ;

pfd . iPixelType = PFD_TYPE_RGBA ;   // 明象素数据类型是RGBA 还是颜色索引;

pfd . cColorBits = 32;     // 每个颜色缓冲区中颜色位平面的数目,对颜色索引方式是缓冲区大小

pfd . iLayerType = PFD_MAIN_PLANE ; // 被忽略,为了一致性而包含的

iFormat = ChoosePixelFormat ( ghDC , & pfd ); // 选择一个像素格式

SetPixelFormat ( ghDC , iFormat , & pfd ); // 设置到DC 中

这样的函数完成了像素格式的设置,事实上还可以进行更多的操作,比如设置缓冲区等,下面的代码就是一个设置双重缓冲区的代码。

PIXELFORMATDESCRIPTOR pfd;

ZeroMemory( &pfd, sizeof( pfd ) );

pfd.nSize = sizeof( pfd );

pfd.nVersion = 1;

pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL |

PFD_DOUBLEBUFFER;

pfd.iPixelType = PFD_TYPE_RGBA;

pfd.cColorBits = 32;

pfd.cDepthBits = 32;

pfd.iLayerType = PFD_MAIN_PLANE;

int iFormat = ChoosePixelFormat( hDC, &pfd );

SetPixelFormat( hDC, iFormat, &pfd );

具体的每个参数的意义,最好还是去查查 MSDN 啦,查看 PIXELFORMATDESCRIPTOR 结构的解释就行。

4.       渲染器环境 (Render Context) 创建

调用 wglCreateContext 与 wglMakeCurrent 函数 ,这两个函数都是 Windows 下为了兼容 OpenGL 而特别提供的接口,以 wgl 开头。

函数原型:

HGLRC wglCreateContext(
  HDC  hdc   // device context of device that the rendering context 
             // will be suitable for
);
BOOL wglMakeCurrent(
  HDC  hdc,      // device context of device that OpenGL calls are 
                 // to be drawn on
  HGLRC  hglrc   // OpenGL rendering context to be made the calling 
                 // thread's current rendering context

);

调用方式如下:

ghRC = wglCreateContext ( ghDC );    // 创建渲染环境

wglMakeCurrent ( ghDC , ghRC );     // 使之成为当前渲染环境

5.       实际绘制

这个部分就与一般的 OpenGL 一致,在后面慢慢展开讲述。

6.       释放资源

首先取消当前的渲染环境选中,然后依次删除渲染环境与设备环境。

需要调用的函数原型:

BOOL wglDeleteContext(
  HGLRC  hglrc   // handle to the OpenGL rendering context to delete

);

// 取消OpenGL ,在程序结束前调用,释放渲染环境,设备环境以及最终窗口句柄。

void DisableOpenGL ()

{

wglMakeCurrent ( NULL , NULL );

wglDeleteContext ( ghRC );

ReleaseDC ( ghWnd , ghDC );

}

上述流程基本就是一个完整的 Win32 OpenGL 程序所需要的了。。。。。。实际上在参考 5 中,有较为详细的论述,但是事实上,你也可以作为论文发表,见参考 3.

四、    真正的 OpenGL 相关内容

1.       静态图形显示演示:一个矩形

见参考 2 中(即所谓的 OpenGL 红宝书 The Red Book )中的例子

基本流程分两部分,初始化和实际绘制:

//OpenGL 初始化开始

void SceneInit ( int w , int h )

{

glClearColor (0.0f, 0.0f, 0.0f, 0.0f);      // 黑色背景

glMatrixMode ( GL_PROJECTION );

glLoadIdentity ();

glOrtho (0.0, 1.0, 0.0, 1.0, -1.0, 1.0);

}

// 这里进行所有的绘图工作

void SceneShow ( GLvoid )

{

glClear ( GL_COLOR_BUFFER_BIT );

glColor3f (1.0, 1.0, 1.0);

glBegin ( GL_POLYGON );

glVertex3f (0.25, 0.25, 0.0);

glVertex3f (0.75, 0.25, 0.0);

glVertex3f (0.75, 0.75, 0.0);

glVertex3f (0.25, 0.75, 0.0);

glEnd ();

glFlush ();

}

显示效果非常简陋,就是黑色背景窗口中一个白色的矩形。 OpenGLGL 的每个函数意义不在此文中描述,本文的主要目的是讲述 win32 中 OpenGL 编程需要的操作。

然后就是将所有的部分串起来了,上述都是代码的片段。 全部源代码见我的放在 Google Code 上的 blog-sample-code 中 2009-9-27/Win32OpenGLTemplate 目录。取回方式见本文最后的说明。

2.       动画演示:一个旋转的矩形

因为整体的框架使用了 Andre LaMothe 的 T3D Game Console ,所以显示动画非常简单。只不过需要注意的是,这里为了显示效果更好利用了双缓冲,那么上面的设置像素格式一步需要用第二种设置方式。

全部的改动如下:

// 激活创建OpenGL 窗口

void EnableOpenGL ()

{

PIXELFORMATDESCRIPTOR pfd ;

int iFormat ;

ghDC = GetDC ( ghWnd );

ZeroMemory ( & pfd , sizeof ( pfd ) );

pfd . nSize = sizeof ( pfd );

pfd . nVersion = 1;      // 版本,一般设为

// 一组表明象素缓冲特性的标志位

pfd . dwFlags =   PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER ;

pfd . iPixelType = PFD_TYPE_RGBA ;   // 明象素数据类型是RGBA 还是颜色索引;

pfd . cColorBits = 32;     // 每个颜色缓冲区中颜色位平面的数目,对颜色索引方式是缓冲区大小

pfd . cDepthBits = 16;

pfd . iLayerType = PFD_MAIN_PLANE ; // 被忽略,为了一致性而包含的

iFormat = ChoosePixelFormat ( ghDC , & pfd ); // 选择一个像素格式

SetPixelFormat ( ghDC , iFormat , & pfd ); // 设置到DC 中

ghRC = wglCreateContext ( ghDC );    // 创建绘图描述表

wglMakeCurrent ( ghDC , ghRC );     // 使之成为当前绘图描述表

}

//OpenGL 初始化开始

void SceneInit ( int w , int h )

{

glClearColor (0.0f, 0.0f, 0.0f, 0.0f);      // 黑色背景

glColor3f (1.0f, 1.0f, 1.0f);

glShadeModel ( GL_FLAT );

glMatrixMode ( GL_PROJECTION );

glLoadIdentity ();

glOrtho (-50.0f, 50.0f, -50.0f, 50.0f, -1.0f, 1.0f);

}

// 这里进行所有的绘图工作

void SceneShow ( GLvoid )

{

// 旋转角度

static float fSpin = 0.0f;

fSpin += 2.0f;

if ( fSpin > 360.0f)

{

fSpin -= 360.0f;

}

glClear ( GL_COLOR_BUFFER_BIT );

glPushMatrix ();

// 旋转矩形的主要函数

glRotatef ( fSpin , 0.0f, 0.0f, 1.0f);

glRectf (-25.0, -25.0, 25.0, 25.0);

glPopMatrix ();

// 交换缓冲区

SwapBuffers ( ghDC );

}

五、    参考资料

1.      《 OpenGL Reference Manual 》, OpenGL 参考手册

2.      《 OpenGL 编程指南》(《 OpenGL Programming Guide 》), Dave Shreiner , Mason Woo , Jackie Neider , Tom Davis 著,徐波译,机械工业出版社

3.      《 Win32 环境下的 OpenGL 编程 》,郑竞华,《现代电子技术》,空军雷达学院

4.      《 Win32 下使用 OpenGL 》

5.      《 OpenGL Win32 Tutorial 》,(讲解比较透彻)

6.      MSDN ,让人惊讶的是 MSDN 中有比较完整的 OpenGL 参考资料,并且还有很多是讲 X Window 的 OpenGL 的 -_-!

Win32 OpenGL 编程 (2) 寻找缺失的 OpenGL 函数

一、    提要

以前在《  Win32 OpenGL 编程( 1 ) Win32 下的 OpenGL 编程必须步骤 》一文中提供了一个较为完整的 Win32 下编写 OpenGL 程序的框架,但是仅仅提到的是此 Win32 框架所需要的一些东西,事实上由于 MS 为了维护自家的 DirectX ,所以很早就放弃了对 OpenGL 的支持,在 Windows XP 平台上实现的 OpenGL 还是 1.1 版 ( 新版的 Windows 看资料好像会好一点,但是事实上 OpenGL 最新版已经是 3.2 了 ) 。本文讲述的就是使用本机显卡支持的最新的 OpenGL 所需要的东西,顺便还讲了一下 GLUT ,这样基本形成一个较为完整的 Win32 下的 OpenGL 变成学习环境。由于工作很简单,所以本文会很短,最长的可能就是提要 ^^

另外,事实上参考资料 4 中的文章已经较为详细的介绍了本文所要讲的大部分内容,大家可以直接去看,写作此文的目的主要还是希望此系列完整,在后面的文章中已经有个完整可用的编程环境。顺面补上 glew 和 glut 简单的安装使用说明。

二、    寻找缺失的 OpenGL 函数

OpenGL 是由显卡直接支持并实现的,当时在设计的时候考虑到显卡厂商可能会比操作系统 / 开发软件制造商先行一步提供新的扩展,所以有提供获取新的扩展的方法,在 Windows 下就是利用 wglGetProcAddress 函数:

PROC wglGetProcAddress(

LPCSTR   lpszProc    // name of the extension function

);

此函数与 GetProcAddress 功能类似,实现也类似,无非就是从动态库中通过名字直接获取到函数的地址。

FARPROC WINAPI GetProcAddress(

__in          HMODULE hModule ,

__in          LPCSTR lpProcName

);

因为 wglGetProcAddress 很明确的知道该从哪个 Dll 获取函数,所以省略了第一参数模块名,仅仅通过函数名就能获取到对应的函数地址,使用方法也是类似,通过函数名获取到函数地址后,通过强制转换的方式使用。此方法虽然可行,但是用到大量函数的时候比较麻烦。事实上,此方法提供的时候是作为一种补充的手段,是在显卡生厂商走在操作系统和开发工具提供商前面时的一种暂时应付手段,但是,很不幸的是, Windows 是在落后太多了。。。。。。。。

三、    Windows 下使用 OpenGL 的救星

MS 当年作为 OpenGL 的发起人之一,仅仅对 OpenGL 支持到 1.1 ,然后就退出了 OpenGL 组织,自己发展 DirectX 系统去了,我们要在 Windows 上使用 OpenGL 光靠 MS 的话,那么 OpenGL 1.1 后的所有函数都得通过上述手段来完成,那简直是让人吐血的事情 -_-!

以前似乎有关官方( SGI )的 OpenGL SDK 下载,但是现在没有那么好了, OpenGL 的官方网站已经明确说明,不再提供 SDK 下载,叫咱们自己到其他网站上找去 -_-! 在 OpenGL.org 上的 OpenGL SDK 中的 Library 为我们提供了几个开源项目,其中有两个就是与扩展有关的。

GLee is a free cross-platform extension loading library that takes the burden off your application. GLee makes it easy to check for OpenGL extension and core version availability, automatically setting up the entry points with no effort on your part.

GLEW is an open-source cross-platform extension loading library with thread-safe support for multiple rendering contexts and automatic code generation capability. GLEW provides easy-to-use and efficient methods for checking OpenGL extensions and core functionality.

我选择了 GLEW ,这个有线程安全支持的项目,大家各取所需吧。不是开源社区的努力,要在 Windows 下使用 OpenGL 还真是让人吐血了 -_-! 说是救星也不为过。

1.       glew 的安装

上面的链接地址有下载, glew 有提供 Windows 的二进制版本,使用上可以直接在工程中包含头文件,库,然后在程序的运行目录包含动态库,因为考虑到别人不一定下载了 glew ,按道理来说不论是源代码分发还是可执行文件的分发最好都是利用此方式使用。但是作为学习来说,要在每个工程中都做这么多额外的工作比较让人受不了,所以推荐讲头文件放在 windows SDK 的 include/GL 目录下(可以通过找到 gl/gl.h 的位置找到), lib 放在 windows SDK 的 lib 目录下,动态库放到 windows/system32 这个系统目录下,那么以后的 OpenGL 工程就不需要额外的配置了,以后文章的工程都是在这个环境下建立的。。。。要运行程序要编译代码请首先搭建好环境。。。。。真是方便了我一个,辛苦了大家了 ^^-_-!

2.       glew 的使用

以后在使用 OpenGL 的时候,都请通过下述代码

#include <GL/glew.h>

#include <GL/wglew.h>

包含必要的头文件

通过

#pragma comment ( lib , "glew32.lib" )

#pragma comment ( lib , "glew32s.lib" )

然后,在初始化 OpenGL 的时候,不要忘了将 glew 初始化,可以这样:

GLenum err = glewInit ();

if (err != GLEW_OK )

{

exit (-2);

}

这样就能使用缺失的函数,就像它们原来就被操作系统支持那样,而不用自己通过 wglGetProcAddress 去获取了,说是能像原来就被操作系统支持的那样使用其实不完成正确,起码在 VS 中,由于都是通过强转后的函数指针使用, VS 和 VA 都不能正确的解析其函数原型,使得代码编写时智能提醒函数原型的功能不能正常使用,这也算是个小瑕疵吧。。。。。。。。。

四、    为了红宝书《 OpenGL Programming Guide 》安装 glut

事实上,在《  Win32 OpenGL 编程( 1 ) Win32 下的 OpenGL 编程必须步骤 》中我就提到了不想使用 glut 的窗口管理系统,但是想完整的学习 《 OpenGL Programming Guide 》 要完全避过 glut 好像比较难,就算不想使用其窗口管理系统,但是一些简单易用的快捷函数要是不用,那就太麻烦了。。。。。我想画个立方体都得先去想办法才行 -_-! 茶壶那就更。。。。。无语了。这里将 glut 的安装使用顺面也讲一下吧。

glut 在 http://www.opengl.org/resources/libraries/glut/ 上有介绍,下载页是: http://www.opengl.org/resources/libraries/glut/glut_downloads.php

也有 Windows 的二进制版本下载,安装方法同 glew, 拷贝头文件,库,动态库到相应的位置,使用方法嘛。。。见红宝书 -_-! 呵呵,其实 glut 包含了平台相关的头文件和 gl.h,glu.h ,所以在使用的时候包含 glut.h 一个文件就行了。

包含了 glut 后,基本上 Win32 下 OpenGL 的工程可以以下面的代码开始了:

#include <GL/glew.h>

#include <GL/wglew.h>

#include <GL/glut.h>

#pragma comment ( lib , "opengl32.lib" )

#pragma comment ( lib , "glu32.lib" )

#pragma comment ( lib , "glew32.lib" )

#pragma comment ( lib , "glew32s.lib" )

#pragma comment ( lib , "glut32.lib" )

这些都是必须的。(事实上由于glut32.lib会自动的包含OpenGL核心所需的库,包含glut32.lib时可以省略opengl32.lib和glu32.lib)

五、    完整的示例

这里提供一个使用了 glew , glut 的完整示例,这个示例在上述两个库都安装完成工程配置完成的时候才能正确运行,也可以用此示例来检验安装,完整的示例从 《 OpenGL  编程指南》(《 OpenGL Programming Guide 》) 的示例代码 mvarray.c 改过来的。其中的宏 GL_VERSION_1_3 是由 glew 定义用来表示支持 OpenGL1.3 版本,其中的函数 glMultiDrawElementsEXT 就是 Windows 本身不支持,用了 glew 后才能使用的 OpenGL 扩展接口(首先要显卡支持啊)。 glut 的使用那就更不用说了。完整的示例在博客示例代码的 2009-9-29/TestOpenGL 目录下可以找到,下载方式见本文最后的说明。

/*

* Copyright (c) 1993-2003, Silicon Graphics, Inc.

* All Rights Reserved

*

* Permission to use, copy, modify, and distribute this software for any

* purpose and without fee is hereby granted, provided that the above

* copyright notice appear in all copies and that both the copyright

* notice and this permission notice appear in supporting documentation,

* and that the name of Silicon Graphics, Inc. not be used in

* advertising or publicity pertaining to distribution of the software

* without specific, written prior permission.

*

* THE MATERIAL EMBODIED ON THIS SOFTWARE IS PROVIDED TO YOU "AS-IS" AND

* WITHOUT WARRANTY OF ANY KIND, EXPRESS, IMPLIED OR OTHERWISE,

* INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF MERCHANTABILITY OR

* FITNESS FOR A PARTICULAR PURPOSE.  IN NO EVENT SHALL SILICON

* GRAPHICS, INC.  BE LIABLE TO YOU OR ANYONE ELSE FOR ANY DIRECT,

* SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,

* OR ANY DAMAGES WHATSOEVER, INCLUDING WITHOUT LIMITATION, LOSS OF

* PROFIT, LOSS OF USE, SAVINGS OR REVENUE, OR THE CLAIMS OF THIRD

* PARTIES, WHETHER OR NOT SILICON GRAPHICS, INC.  HAS BEEN ADVISED OF

* THE POSSIBILITY OF SUCH LOSS, HOWEVER CAUSED AND ON ANY THEORY OF

* LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE POSSESSION, USE

* OR PERFORMANCE OF THIS SOFTWARE.

*

* US Government Users Restricted Rights

* Use, duplication, or disclosure by the Government is subject to

* restrictions set forth in FAR 52.227.19(c)(2) or subparagraph

* (c)(1)(ii) of the Rights in Technical Data and Computer Software

* clause at DFARS 252.227-7013 and/or in similar or successor clauses

* in the FAR or the DOD or NASA FAR Supplement.  Unpublished - rights

* reserved under the copyright laws of the United States.

*

* Contractor/manufacturer is:

*    Silicon Graphics, Inc.

*    1500 Crittenden Lane

*    Mountain View, CA  94043

*    United State of America

*

* OpenGL(R) is a registered trademark of Silicon Graphics, Inc.

*/

/*

*  mvarray.c

*  This program demonstrates multiple vertex arrays,

*  specifically the OpenGL routine glMultiDrawElements().

*/

#include <GL/glew.h>

#include <GL/wglew.h>

#include <GL/glut.h>

#include <stdio.h>

#pragma comment ( lib , "opengl32.lib" )

#pragma comment ( lib , "glu32.lib" )

#pragma comment ( lib , "glew32.lib" )

#pragma comment ( lib , "glew32s.lib" )

#pragma comment ( lib , "glut32.lib" )

#ifdef GL_VERSION_1_3

void setupPointer (void )

{

static GLint vertices [] = {25, 25,

75, 75,

100, 125,

150, 75,

200, 175,

250, 150,

300, 125,

100, 200,

150, 250,

200, 225,

250, 300,

300, 250};

glEnableClientState (GL_VERTEX_ARRAY );

glVertexPointer (2, GL_INT , 0, vertices );

}

void init (void )

{

GLenum err = glewInit ();

if (err != GLEW_OK )

{

exit (-2);

}

glClearColor (0.0, 0.0, 0.0, 0.0);

glShadeModel (GL_SMOOTH );

setupPointer ();

}

void display (void )

{

static GLubyte oneIndices [] = {0, 1, 2, 3, 4, 5, 6};

static const GLubyte twoIndices [] = {1, 7, 8, 9, 10, 11};

static GLsizei count [] = {7, 6};

static const GLvoid * indices [2] = {oneIndices , twoIndices };

glClear (GL_COLOR_BUFFER_BIT );

glColor3f (1.0, 1.0, 1.0);

glMultiDrawElementsEXT (GL_LINE_STRIP , count , GL_UNSIGNED_BYTE ,

indices , 2);

// 上面的一句相当于下面的两句

//glDrawElements(GL_LINE_STRIP, count[0], GL_UNSIGNED_BYTE, oneIndices);

//glDrawElements(GL_LINE_STRIP, count[1], GL_UNSIGNED_BYTE, twoIndices);

glFlush ();

}

void reshape (int w , int h )

{

glViewport (0, 0, (GLsizei ) w , (GLsizei ) h );

glMatrixMode (GL_PROJECTION );

glLoadIdentity ();

gluOrtho2D (0.0, (GLdouble ) w , 0.0, (GLdouble ) h );

}

void keyboard (unsigned char key , int x , int y )

{

switch (key ) {

case 27:

exit (0);

break ;

}

}

int main (int argc , char ** argv )

{

glutInit (&argc , argv );

glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB );

glutInitWindowSize (350, 350);

glutInitWindowPosition (100, 100);

glutCreateWindow (argv [0]);

init ();

glutDisplayFunc (display );

glutReshapeFunc (reshape );

glutKeyboardFunc (keyboard );

glutMainLoop ();

return 0;

}

#else

int main(int argc, char** argv)

{

fprintf (stderr, "This program demonstrates a feature which is not in OpenGL Version 1.0./n");

fprintf (stderr, "If your implementation of OpenGL Version 1.0 has the right extensions,/n");

fprintf (stderr, "you may be able to modify this program to make it run./n");

return 0;

}

#endif

六、    参考资料

1.        《 OpenGL Reference Manual 》, OpenGL 参考手册

2.        《 OpenGL  编程指南》(《 OpenGL Programming Guide 》), Dave Shreiner , Mason Woo , Jackie Neider , Tom Davis  著,徐波译,机械工业出版社

3.        MSDN ,事实上我收回 MSDN 上有完整的 OpenGL 参考的话,应该是说 MSDN 上有 OpenGL1.1 前版本的较完整资料。

4.        《 如何使用 OpenGL 扩展 》,曹添著 已经包含了本文的大部分内容

Win32 OpenGL 编程 ( 3 ) 基本图元(点,直线,多边形)的绘制

一、    提要

在前面两篇相关文章

《  Win32 OpenGL 编程(1)Win32下的OpenGL编程必须步骤 》

《 Win32 OpenGL编程(2) 寻找缺失的OpenGL函数 》

中,我们已经建立了一个较为全面的 Win32 OpenGL 编程环境及一个简单的框架,并且,实际上掌握了 OpenGL 在 Windows 下与 Win32 窗口交互的方法,在此基础上,总算是可以正式进行一些 OpenGL 相关知识的学习,前面的那些也就算是热身。本文的目的是将 OpenGL 中基本图元(点,线,多边形)的绘制大概的讲解一遍,最后可以组合的使用这些技术用 OpenGL 完成较为复杂的 2D 图形。

二、    基本图元相关概念

首先讲讲相关的概念,在 OpenGL 中,即使是复杂的图形,实际上也是由一些非常基本的图元组成,即点,直线,多边形,多边形中用的较多的又是三角形和矩形。在数学中,两点确定一条直线,三点确定一个三角形和一个面,同一个面上的四个点确定一个四边形。。。。。。在 OpenGL 中也大致的利用此方式来确定直线和多边形,也就是说,当你想画一个直线或者一个多边形的时候,只需要告诉 OpenGL 能确定此直线或者多边形的点即可。用参考 2 中的描述是:“在 OpenGL 中,所有的几何物体最终都描述成一组有序的顶点”。有此基本的概念后就可以看下面的例子了。

三、    OpenGL 的 Hello World 示例分析

这里的 Hello World 程序指的是一个利用 OpenGL 完成的矩形绘制程序,相对于在系列文章 1 中的 Win32 OpenGL 编程框架,简化了很多东西,只剩下最最基本的 OpenGL 元素,但也是一个完善的 OpenGL 示例了。此示例显示的是一个白色的矩形,运行效果如附图 1 ,完整代码见我博客代码的 2009-10-12/SimpleRectangle 工程,具体下载及查看方法见本文最后的说明。

此示例 OpenGL 相关的主要就是两部分。

//OpenGL 初始化开始

void SceneInit ( int w , int h )

{

}

// 这里进行所有的绘图工作

void SceneShow ( GLvoid )

{

glClear ( GL_COLOR_BUFFER_BIT );

glBegin ( GL_QUADS );

glVertex3f (-0.5, -0.5, 0.0);

glVertex3f (0.5, -0.5, 0.0);

glVertex3f (0.5, 0.5, 0.0);

glVertex3f (-0.5, 0.5, 0.0);

glEnd ();

glFlush ();

}

我们下面从 OpenGL 的角度来分析此程序。作为 HelloWorld 级的程序,我简化了很多东西,首先 OpenGL 的初始化省略了,用的都是 OpenGL 的默认值,具体有哪些,后面一步一步说。以下是按步骤说明每个 OpenGL 函数。

1.       glClear

OpenGL 参考手册 :

glClear takes a single argument that is the bitwise OR of several values indicating which buffer is to be cleared.

GL_COLOR_BUFFER_BIT Indicates the buffers currently enabled for color writing.

作用是清除颜色缓冲区。类似于我们使用一块新申请的内存时先用 memset/ZeroMemory 去清零一下,用这样的 Clear 操作为我们需要使用的颜色缓冲区 (Color Buffer) 清零一下。假如没有这样的操作,以前留在显存 / 内存 ( 不确定 ) 中的值会影响我们的操作,并且,这样的问题往往是非常难以调试和发现的,这一点大家可以尝试一下。

2.       glBegin , glEnd

OpenGL 参考手册 :

glBegin, glEnd - delimit the vertices of a primitive or a group of like primitives

OpenGL 代码的主体部分是:

glBegin ( GL_QUADS );

glVertex3f (-0.5, -0.5, 0.0);

glVertex3f (0.5, -0.5, 0.0);

glVertex3f (0.5, 0.5, 0.0);

glVertex3f (-0.5, 0.5, 0.0);

glEnd ();

glBegin 与 glEnd 很明显是一对, 标志着一组 OpenGL 操作的开始和结束。 并且在参数中告诉了 OpenGL 下面的操作是针对什么图形进行的,此例中 GL_QUADS 是表示四边形。事实上还有很多其他的参数来表示各类图形,在《 OpenGL Programming Guide 》的 此页 中 Figure 2-7 : Geometric Primitive Types 一图形象的说明了各个参数的作用。

3.       glVertex *

glVertex3f 就是在 基本图元相关概念 一节提到的 OpenGL 中确定顶点的函数。简而言之,上面的 4 句 glVertex3f 确定了矩形的 4 个顶点。(注意顺序)然后, OpenGL 就会自动根据 glBegin 指定的参数去完成相关的绘制任务了, 此例中 GL_QUADS 是表示四边形,所以最后的效果是一个矩形,实际的其他参数读者可以自己尝试一下。

4.       OpenGL 默认坐标系

我们再看一下 glVertex* 指定顶点的代码:

glVertex3f (-0.5, -0.5, 0.0);

glVertex3f (0.5, -0.5, 0.0);

glVertex3f (0.5, 0.5, 0.0);

glVertex3f (-0.5, 0.5, 0.0);

为什么上述就指定了一个矩形的四个顶点呢?需要说明的是,在 OpenGL 中默认坐标体系与 Windows 中常用的不同, Windows 中常用的坐标体系(仅 2D )是用户区的左上角为坐标原点,即 (0.0,0.0) 点,右为坐标轴的 X 轴正方向,下为 Y 轴正方向, OpenGL 中的坐标轴( 3D )默认以客户区中心点为坐标原点( 0.0,0.0,0.0 ),右为坐标轴的 X 轴正方向,上为 Y 轴正方向,垂直指出屏幕的方向为 Z 轴正方向。长度定义是将客户区范围为按单位长度定义,即整个客户区恰好是 (-1,-1) (左下)到 (1,1) (右上)。附图 2 是一个上述示例程序附上 OpenGL 的平面坐标系的图,也可以作为 OpenGL 默认坐标系的参考图。

5.       OpenGL 函数命名

这里顺面介绍一下 OpenGL 函数的命名规范,因为 C 语言天生的弱点及丰富的数据类型,在 OpenGL 中凡是牵涉到与参数数量和数据类型相关的函数,一般的命名方式都是 xxxx[n][t] 。

xxxx 表示函数的意义, [t] 用于表示此函数对应的类型。一般用单个的字母表示参数的类型, s 表示 16 位整数( OpenGL 中将这个类型定义为 GLshort ), i 表示 32 位整数( OpenGL 中将这个类型定义为 GLint 和 GLsizei ), f 表示 32 位浮点数( OpenGL 中将这个类型定义为 GLfloat ), d 表示 64 位浮点数( OpenGL 中将这个类型定义为 GLdouble )。此例中使用的是 32 位浮点数,所以是 f 。这是 C 语言没有函数重载机制的天生弱点导致的扭曲应对方案。(用 C++ 就不需要这么麻烦了)

然后是数字,因为同样的原因,在 C 语言中一个同样意义的函数不能同时有不同个数的参数,所以 OpenGL 用一个数字来表示参数的个数,此例中是 3 ,表示以 3 个参数(即点的 X,Y,Z 坐标)来表示顶点。(事实上还有 glVertex2* , glVertex4* )

比如此例中,用如下代码效果是一样的:

glVertex2f (-0.5, -0.5);

glVertex2f (0.5, -0.5);

glVertex2f (0.5, 0.5) ;

glVertex2f (-0.5, 0.5);

在 glVertex2f 中, Z 轴默认为 0.

这里我依照参考 2 的用法,以 * 作为通配符来表示一组函数, * 既可以表示代表参数数量的数字也可以表示代表类型的字母。

6.       glFlush

OpenGL 参考手册 :

glFlush - force execution of GL commands in finite time

说白了就是强制执行已经指定的 OpenGL 命令,与 fflush 命名类似作用也类似。

7.       小结

以上的 5 个 OpenGL 函数就构成了一个基本的 OpenGL 程序(不包括模板中使用的那些),由 glClear 清空颜色缓冲区获得干净的环境,由 glBegin 指定开始一组顶点操作的开始,并确认绘制图形,由 glVertex* 指定顶点,由 glEnd 表示操作结束,由 glFlush 强制开始绘图。运行效果如附图 1 。

四、    基本图元

事实上,上一节已经包含了所有的知识,各个图元在 OpenGL 中绘制方式的不同仅仅在于 glBegin 的参数不同而已,这里将基本图元简单介绍一下。

1.       点

点是最常用的图元之一了,而且所有的图像都可以看做是由点构成的,事实上屏幕也就是由一个一个像素点构成了图像:)

画点的方式是使用 GL_POINTS 为参数调用 glBegin 。那么,每一个 glVertex* 指定顶点就会绘制成一个单独的点。点的默认大小为 1 个像素,可以通过 glPointSize 函数来改变点的大小,在点比较大并且没有开启抗锯齿时,是按照一个正方形来绘制的。比如上例中,仅仅将 GL_QUADS 换成 GL_POINTS ,将是绘制上述矩形的 4 个顶点,为了截图效果显著,调用 glPointSize 将点的大小改为 20 。如下代码:

// 这里进行所有的绘图工作

void SceneShow ( GLvoid )

{

glClear ( GL_COLOR_BUFFER_BIT );

glPointSize (20);

glBegin ( GL_POINTS );

glVertex3f (-0.5, -0.5, 0.0);

glVertex3f (0.5, -0.5, 0.0);

glVertex3f (0.5, 0.5, 0.0);

glVertex3f (-0.5, 0.5, 0.0);

glEnd ();

glFlush ();

}

效果如附图 3 所示。

光是点也可以做很多有趣的应用, Windows 的屏保模拟星空即是其一,事实上,在原来学习简单的图形编程时,我用多种语言尝试了用点做一些有意思的东西。见简单图形编程学习系列:

《 简单图形编程的学习( 2) ---点 (Qt实现 ) 》

《 简单图形编程的学习( 2) ---点 (Windows GDI实现 ) 》

《 简单图形编程的学习( 2) ---点 (small basic实现 ) 》

其中点与文字结合形成的那个星空文字效果我是印象深刻,我一直将其作为“简单的编程技术 + 创意”也能很强大的例子。

2.       直线

直线也是很基础的东西了,但是 OpenGL 中的直线与数学概念上有些区别,不知道外国的那些专家在命名时为啥乱了,其实 OpenGL 中的直线与数学中的线段概念一致,有两个端点确认长度。事实上,在 OpenGL 绘制中,指定的也就是线段的两个端点。用 GL_LINES 调用 glBegin 时,表示绘制的是直线。默认情况下宽度为一个像素,同样, OpenGL 也提供了函数 glLineWidth 用以改变直线的宽度。比如上例中,仅仅将 GL_QUADS 换成 GL_LINES 将是两条平行的直线,并用 glLineWidth 将直线宽度改为 5 ,如下代码:

// 这里进行所有的绘图工作

void SceneShow ( GLvoid )

{

glClear ( GL_COLOR_BUFFER_BIT );

glLineWidth (5);

glBegin ( GL_LINES );

glVertex3f (-0.5, -0.5, 0.0);

glVertex3f (0.5, -0.5, 0.0);

glVertex3f (0.5, 0.5, 0.0);

glVertex3f (-0.5, 0.5, 0.0);

glEnd ();

glFlush ();

}

运行效果如附图 4 所示。

直线的应用也是非常广泛的,还记得 Windows 的屏保中的变幻线吗?千变万变,无论多么绚烂的效果,其实也仅仅是一条条变化的线段而已。

3.       多边形

其实可以将多边形看成是从点到线到面的一种扩展,这里的面自然也是有范围的,那么就成了多边形了,在绘制图形中用的最多的是三角形,因为三角形肯定在同一个面上,这样可以简化很多计算的处理。 GL_TRIANGLES 调用 glBegin 表示开始绘制三角形。然后还有 GL_QUADS 表示四边形(上例中的用法), GL_POLYGON 表示多边形(必须是凸的)。

4.       小结

其实除了上面讲到的那些参数,还有一些额外的参数,比如 GL_LINE_STRIP,GL_TRIANGLES_STRIP 等,表示绘制的时候绘制一些列连续的图形,这些参数用文字解释起来不够形象,还是推荐参考 《 OpenGL Programming Guide 》的 此页 中 Figure 2-7 : Geometric Primitive Types 一图,此图形象的展示了各个参数时对点的解释方式和顺序。在此图的下面,还有个表总结了一下各个参数的作用。

在一个 glBegin 和 glEnd 对中可以连续的制定多个顶点,甚至超出你指定的图形的数目,比如绘制三角形时可以指定 6 个点,那么此时,将会绘制两个三角形而不是一个,这样而当指定 4 , 5 个点时用 GL_TRIANGLES 参数时将会丢弃,用 GL_TRIANGLE_STRIP 参数时将会连续绘制两个三角形,具体的解释方法也是要看 glBegin 参数的,建议还是参考上述图片。

五、    参考资料

1.          《 OpenGL Reference Manual 》, OpenGL 参考手册

2.          《 OpenGL  编程指南》(《 OpenGL Programming Guide 》), Dave Shreiner , Mason Woo , Jackie Neider , Tom Davis  著,徐波译,机械工业出版社

3.        《 Nehe OpenGL Tutorials 》, Nehe 著,在 http://nehe.gamedev.net/ 上可以找到教程及相关的代码下载,(有 PDF 版本教程下载) Nehe 自己还做了一个面向对象的框架,作为演示程序来说,这样的框架非常合适。也有 中文版 ,各取所需吧。

4.        《 OpenGL 入门学习》 , eastcowboy 著,这是我在网上找到的一个比较好的教程,较为完善,而且非常通俗。这是第一篇的地址: http://bbs.pfan.cn/post-184355.html

六、    系列其他文章

1.    《  Win32 OpenGL 编程(1)Win32下的OpenGL编程必须步骤 》

2.        《 Win32 OpenGL编程(2) 寻找缺失的OpenGL函数 》

七、    附图

1.       图 1 SimpleRectangle

图2 SimpleRectangle with coordinate

图3 SimplePoints

图4 SimpleLines

Win32 OpenGL 编程相关推荐

  1. Win32 OpenGL 编程(1)Win32下的OpenGL编程必须步骤

    分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow 也欢迎大家转载本篇文章.分享知识,造福人民,实现我们中华民族伟大复兴! Win3 ...

  2. Win32 OpenGL编程 5 顶点数组详细介绍

    分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow 也欢迎大家转载本篇文章.分享知识,造福人民,实现我们中华民族伟大复兴! Win3 ...

  3. Win32 OpenGL编程(9) 投影变换

    Win32 OpenGL编程(9) 投影变换 write by 九天雁翎(JTianLing) -- blog.csdn.net/vagrxie 讨论新闻组及文件 提要 在前文(系列文章(7),以下简 ...

  4. Win32 OpenGL编程系列 2D例子 -- 七巧板图形绘制

    write by 九天雁翎(JTianLing) -- blog.csdn.net/vagrxie 讨论新闻组及文件 OpenGL系列文章都已经写到3D部分了,但是感觉2D的例子还是少了点,特意弄个外 ...

  5. Win32 OpenGL编程(4) 2D图形基础(颜色及坐标体系进阶知识)

    Win32 OpenGL 编程 (4) 2D 图形基础(颜色及坐标体系进阶知识) write by 九天雁翎 (JTianLing) -- blog.csdn.net/vagrxie 讨论新闻组及文件 ...

  6. Qt下的OpenGL 编程(3)绘制平面几何体

    一. 提要 之前的一篇教程已经搭建好了Qt下的OpenGL的编程环境,几天要来学习的就是OpenGL的2D绘图. 2D作为绘图的基础,还是很值得去好好学习,比如迪卡尔坐标,透视设置等等,而所谓的3D, ...

  7. OpenGL编程轻松入门(一)

    OpenGL编程轻松入门(一) (由同事黄燕创作)   本文介绍了有关OpenGL的基本知识,主要涉及颜色.绘制几何体.坐标变换.堆栈操作.显示列表.光照和材质.纹理映射.特殊效果.曲面和曲线的绘制. ...

  8. OpenGL编程入门学习

    OpenGL编程入门学习  非常详细的教程,很适合初学者 本文转自:http://www.cppblog.com/doing5552/archive/2009/01/08/71532.html === ...

  9. Win32 OpenGL标准例子

    原文:http://www.cnblogs.com/wurui1994/p/6058882.html Win32 OpenGL标准例子 在VS2008的MSDN中有一个标准的OpenGL例子,记录如下 ...

最新文章

  1. python科学计数法转换_柳小白Python学习笔记35 Excel之科学计数法类型转换及数据选取1...
  2. My lead media_src relative handling for Attachment download in GWaaS environment
  3. 在.net中加载dll的一种错误问题原因及处理
  4. IndexedDB 简单封装
  5. fastdfs中浏览器访问获取不到文件_SpringBoot 2.0 开发案例之整合FastDFS分布式文件系统...
  6. Excel数据分析案例一——业绩达成分析
  7. 英文java简历模板下载_java英文简历模板
  8. MyBatis 拦截器使用
  9. python 连通区域_opencv 查找连通区域 最大面积实例
  10. php中文搜索工具,Laravel 下 TNTSearch+jieba-PHP 实现中文全文搜索
  11. 我将进化成一条狗(5)——VR和AR
  12. stmdb和ldmia
  13. 私域引流工具及场景落地方法
  14. 【综合实训】图书管理系统——需求规格说明书
  15. mysql 错误码 1267_mysql错误1267的解决方法
  16. java bean 序列化_JAVA bean为何要实现序列化
  17. 电脑不用,不用电脑,你还会写字吗?
  18. cogs 1341 永无乡
  19. 赚钱:停更是不可能停的,但是有时候你不得不停。
  20. Java代码实现消消乐游戏中的消除功能

热门文章

  1. 【PAT A1094】The Largest Generation
  2. vite打包工具的介绍
  3. 浅谈LCD液晶屏和LED屏,它们有什么不同之处
  4. 国外计算机学校排名,国外财经学校排名,财经学校
  5. 淘宝模块中宝贝跳转链接
  6. 误报率、故障检测率、漏报率、虚警率、误警率等指标异同及计算公式
  7. Java核心技术(Java白皮书)卷Ⅰ 第一章 Java程序设计概述
  8. Swift学习笔记(四)
  9. 最便宜的方式学习阿里云产品之使用竞价式实例ECS
  10. 什么是抽象类?什么是抽象方法?