为了更好的排版,请到以下博客查看此教程

OpenGl 环境搭建与介绍​iamazing.cn

1. 搭建 OpenGL 环境

取决于你的编程平台,有以下教程:

  • 基于 C++(也可以看译者自己写的教程)
  • 基于 Java:JOGL 或者 LWJGL
  • 安卓平台

1.1 例子 1:设置 OpenGL 与 GLUT(GL01Hello.cpp)

确保你能够运行以下程序:

/** GL01Hello.cpp: Test OpenGL/GLUT C/C++ Setup* Tested under Eclipse CDT with MinGW/Cygwin and CodeBlocks with MinGW* To compile with -lfreeglut -lglu32 -lopengl32*/
#include <windows.h>  // for MS Windows
#include <GL/glut.h>  // GLUT, include glu.h and gl.h/* Handler for window-repaint event. Call back when the window first appears andwhenever the window needs to be re-painted. */
void display() {glClearColor(0.0f, 0.0f, 0.0f, 1.0f); // Set background color to black and opaqueglClear(GL_COLOR_BUFFER_BIT);         // Clear the color buffer (background)// Draw a Red 1x1 Square centered at originglBegin(GL_QUADS);              // Each set of 4 vertices form a quadglColor3f(1.0f, 0.0f, 0.0f); // RedglVertex2f(-0.5f, -0.5f);    // x, yglVertex2f( 0.5f, -0.5f);glVertex2f( 0.5f,  0.5f);glVertex2f(-0.5f,  0.5f);glEnd();glFlush();  // Render now
}/* Main function: GLUT runs as a console application starting at main()  */
int main(int argc, char** argv) {glutInit(&argc, argv);                 // Initialize GLUTglutCreateWindow("OpenGL Setup Test"); // Create a window with the given titleglutInitWindowSize(320, 320);   // Set the window's initial width & heightglutInitWindowPosition(50, 50); // Position the window's initial top-left cornerglutDisplayFunc(display); // Register display callback handler for window re-paintglutMainLoop();           // Enter the event-processing loopreturn 0;
}

#include <windows.h>

注意头文件 “windows.h” 仅在 Windows 平台需要。

#include <GL/glut.h>

我们还需要包含 GLUT 头文件,其已经包含了 “glu.h” 和 “gl.h”。

该程序的剩余部分会在相应的教程中解释。

2. 介绍

OpenGL(开放图形库,Open Graphics Library)是一个跨平台的,具备硬件加速的,语言无关的用于构建 3D(包含2D)图形的工业标准 API。现代计算机大多具备专门的带有独立内存的图像处理单元(GPU)用以加速图形渲染。OpenGL 就是这些图像处理硬件的软件接口。

在我们的 OpenGL 程序中使用了以下三组软件库:

  1. OpenGL 核心库(GL):包含数以百计的函数,以 “gl”开头(例如:glColorglVertexglTranslateglRotate)。OpenGL 核心库通过一组几何图元(例如点,线,多边形)来进行建模。
  2. OpenGL 实用程序库(GLU):基于 OpenGL 核心构建,提供一些重要的实用程序(例如:设置摄像机以及投影),以 “glu”开头(例如:gluLookAtgluPerspective)。
  3. OpenGL 实用工具包(GLUT):OpenGL 被设计为独立于操作系统。因此我们需要 GLUT 来与操作系统进行交互(例如:创建窗口,处理键盘和鼠标输入),其提供的函数以 “glut” 开头(例如:glutCreatewindowglutMouseFunc)。GLUT 是平台无关的,其基于平台相关的 OpenGL 扩展构建,例如对于 X Window 是 GLX,对于 Windows 系统是 WGL,对于 Mac OS 则是 AGL,CGL 或者 Cocoa。

3. 顶点,图元以及颜色

3.1 例子 2:顶点,图元以及颜色(GL02Primitive.cpp)

尝试编译运行以下 OpenGL / C++ 程序:

/** GL02Primitive.cpp: Vertex, Primitive and Color* Draw Simple 2D colored Shapes: quad, triangle and polygon.*/
#include <windows.h>  // for MS Windows
#include <GL/glut.h>  // GLUT, include glu.h and gl.h/* Initialize OpenGL Graphics */
void initGL() {// Set "clearing" or background colorglClearColor(0.0f, 0.0f, 0.0f, 1.0f); // Black and opaque
}/* Handler for window-repaint event. Call back when the window first appears andwhenever the window needs to be re-painted. */
void display() {glClear(GL_COLOR_BUFFER_BIT);   // Clear the color buffer with current clearing color// Define shapes enclosed within a pair of glBegin and glEndglBegin(GL_QUADS);              // Each set of 4 vertices form a quadglColor3f(1.0f, 0.0f, 0.0f); // RedglVertex2f(-0.8f, 0.1f);     // Define vertices in counter-clockwise (CCW) orderglVertex2f(-0.2f, 0.1f);     //  so that the normal (front-face) is facing youglVertex2f(-0.2f, 0.7f);glVertex2f(-0.8f, 0.7f);glColor3f(0.0f, 1.0f, 0.0f); // GreenglVertex2f(-0.7f, -0.6f);glVertex2f(-0.1f, -0.6f);glVertex2f(-0.1f,  0.0f);glVertex2f(-0.7f,  0.0f);glColor3f(0.2f, 0.2f, 0.2f); // Dark GrayglVertex2f(-0.9f, -0.7f);glColor3f(1.0f, 1.0f, 1.0f); // WhiteglVertex2f(-0.5f, -0.7f);glColor3f(0.2f, 0.2f, 0.2f); // Dark GrayglVertex2f(-0.5f, -0.3f);glColor3f(1.0f, 1.0f, 1.0f); // WhiteglVertex2f(-0.9f, -0.3f);glEnd();glBegin(GL_TRIANGLES);          // Each set of 3 vertices form a triangleglColor3f(0.0f, 0.0f, 1.0f); // BlueglVertex2f(0.1f, -0.6f);glVertex2f(0.7f, -0.6f);glVertex2f(0.4f, -0.1f);glColor3f(1.0f, 0.0f, 0.0f); // RedglVertex2f(0.3f, -0.4f);glColor3f(0.0f, 1.0f, 0.0f); // GreenglVertex2f(0.9f, -0.4f);glColor3f(0.0f, 0.0f, 1.0f); // BlueglVertex2f(0.6f, -0.9f);glEnd();glBegin(GL_POLYGON);            // These vertices form a closed polygonglColor3f(1.0f, 1.0f, 0.0f); // YellowglVertex2f(0.4f, 0.2f);glVertex2f(0.6f, 0.2f);glVertex2f(0.7f, 0.4f);glVertex2f(0.6f, 0.6f);glVertex2f(0.4f, 0.6f);glVertex2f(0.3f, 0.4f);glEnd();glFlush();  // Render now
}/* Main function: GLUT runs as a console application starting at main()  */
int main(int argc, char** argv) {glutInit(&argc, argv);          // Initialize GLUTglutCreateWindow("Vertex, Primitive & Color");  // Create window with the given titleglutInitWindowSize(320, 320);   // Set the window's initial width & heightglutInitWindowPosition(50, 50); // Position the window's initial top-left cornerglutDisplayFunc(display);       // Register callback handler for window re-paint eventinitGL();                       // Our own OpenGL initializationglutMainLoop();                 // Enter the event-processing loopreturn 0;
}

预期得到如下输出:

坐标关系图:

我将会在接下来的部分详细解释该程序。

3.2 OpenGL 与状态机

OpenGL 像一个状态机一样运作,并且其维护了一组状态变量(例如:前景色,背景色)。在一个状态机中,一旦设置了状态变量的值,该值将持续存在,直到给出新的值。

举个例子,我们在 initGL() 函数中将 clearing 颜色设置为黑色。我们将在 display() 函数中反复地使用此项设置,注意 display() 函数在窗口重新绘制时被调用。

// In initGL(), set the "clearing" or background color
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);  // black and opaque// In display(), clear the color buffer (i.e., set background) with the current "clearing" color
glClear(GL_COLOR_BUFFER_BIT);

另一个例子:如果我们使用 glColor 函数设置当前前景色为红色,之后红色将被用于后续所有顶点地着色,除非我们再次调用 glColor 函数改变前景色。

总之,在一个状态机中,任何值都将保持不变除非你显式地改变它。

3.3 OpenGL 命名约定

OpenGL 函数命名约定

  • 以小写的 gl(对于 OpenGL 核心函数库),glu(对于 OpenGL 实用程序库)或者 glut(对于 OpenGL 实用工具包)开头。
  • 紧跟该函数的功能,采用驼峰式命名法,例如 glColor 用于指定要绘制的颜色,glVertex 用于指定顶点的位置。
  • 紧跟参数描述,例如 glColor3f 函数要求三个浮点数作为参数,glVectex2i 函数要求两个整数作为参数。(为什么不直接使用函数重载?因为 C 语言不支持函数重载)。
  • 最后面是一个 v 代表参数需要是一个数组。

OpenGL 数据类型命名约定

typedef unsigned int    GLenum;
typedef unsigned char   GLboolean;
typedef unsigned int    GLbitfield;
typedef void            GLvoid;
typedef signed char     GLbyte;         /* 1-byte signed */
typedef short           GLshort;        /* 2-byte signed */
typedef int             GLint;          /* 4-byte signed */
typedef unsigned char   GLubyte;        /* 1-byte unsigned */
typedef unsigned short  GLushort;       /* 2-byte unsigned */
typedef unsigned int    GLuint;         /* 4-byte unsigned */
typedef int             GLsizei;        /* 4-byte signed */
typedef float           GLfloat;        /* single precision float */
typedef float           GLclampf;       /* single precision float in [0,1] */
typedef double          GLdouble;       /* double precision float */
typedef double          GLclampd;       /* double precision float in [0,1] */

OpenGL 常量命名约定

  • 以 GL,GLU 或者 GLUT 开头.
  • 下划线进行分割。
  • 全大写。

例如:GL_COLOR_BUFFER_BIT。

3.4 initGL()

initGL() 函数用于初始化那些只需要设置一次的任务,例如设置 clearing 颜色。initGL() 函数仅在 main() 函数中被调用一次。

3.5 display()

函数 display() 是一个事件处理回调函数。当一个事件发生时(例如按键被按下,鼠标点击,窗口绘制),相应的事件处理回调函数被调用。

当窗口第一次出现以及之后每次窗口重绘时调用 display() 函数。

注意该函数就是用户创建的一个普通的函数,名字可以任意,将此函数作为参数传递给 glutDisplayFunc(functionName) ,即所谓的向 OpenGL 注册绘制函数,这样 OpenGL 才知道当窗口绘制的时候应该调用哪一个函数。

3.6 设置 GLUT

GLUT 提供了一些较高层次封装的实用函数以简化 OpenGL 编程,尤其是当与操作系统交互时(例如创建窗口,处理键盘和鼠标输入)。在上述程序中用到了以下 GLUT 函数:

  • void glutInit(int *argc, char **argv):初始化 GLUT,需要在任何其它 GL/GLUT 函数前被调用,其参数与 main 函数一样。
  • int glutCreateWindow(char *title):创建一个窗口并设置窗口标题。
  • void glutInitWindowSize(int width, int height):指定窗口的宽度与高度,单位为像素。
  • void glutInitWindowPosition(int x, int y):指定窗口的位置,圆心为屏幕的左上角,x 轴正方向为向右,y 轴正方向则是向下。
  • void glutDisplayFunc(void (*func)(void)):注册处理窗口绘制事件的函数,传入的参数即函数名。
  • void glutMainLoop():进入事件处理循环,OpenGL 图形系统等待事件发生并调用相应的处理函数处理事件。

3.7 颜色

我们使用 glColor设置前景色,使用 glClearColor 函数设置背景色,即所谓的 clearing 颜色。

void glColor3f(GLfloat red, GLfloat green, GLfloat blue)
void glColor3fv(GLfloat *colorRGB)
void glColor4f(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha)
void glColor4fv(GLfloat *colorRGBA)void glClearColor(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha)// GLclampf in the range of 0.0f to 1.0f

注意:

  • 颜色通常是浮点数且范围在 0.0f 与 1.0f 之间。
  • 颜色可以使用 RGB 或者 RGBA 模式指定。A (alpha)代表透明度,值为 1 时完全不透明,值为 0 时完全透明。

在上述例子中,我们通过 initGL() 中的 glClearColor 设置背景色。

// In initGL(), set the "clearing" or background color
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);  // Black and opague

在 display() 函数中,我们通过 glColor3f() 函数设置后续的顶点的颜色。

// In display(), set the foreground color of the pixel
glColor3f(1.0f, 0.0f, 0.0f);  // Red

3.8 几何图元

在 OpenGL 中,物体是由诸如三角形,四边形,线段,点之类的几何图元构成的,而图元又由一个或者多个点构成。OpenGL 支持以下图元:

几何图元可以通过 glVertex 函数指定其顶点,并由一对 glBegin 和 glEnd 包裹来定义。

void glBegin(GLenum shape)void glVertex[234][sifd] (type x, type y, type z, ...)void glVertex[234][sifd]v (type *coords)
void glEnd()

glBegin 指定几何体的类型,例如 GL_POINTS,GL_LINES,GL_QUADS,GL_TRIANGLES 以及 GL_POLYGON。对于以 S 结尾的类型,你可以在每一组 glBegin / glEnd 定义多个相同类型的几何体。例如对于 GL_TRIANGLES,每三个顶点定义一个三角形。

顶点通常以单精度浮点数指定。这是因为整型不合适三角运算,而单精度浮点数的精度足够用于中间运算并最后将图形以像素为单位渲染到屏幕上,并且通常没有必要使用双精度浮点数。

在上述例子中:

glBegin(GL_QUADS);.... 4 quads with 12x glVertex() ....
glEnd();

我们使用了 12 个 glVertex() 函数定义了 3 个具有颜色的四边形。

glColor3f(1.0f, 0.0f, 0.0f);
glVertex2f(-0.8f, 0.1f);
glVertex2f(-0.2f, 0.1f);
glVertex2f(-0.2f, 0.7f);
glVertex2f(-0.8f, 0.7f);

我们将颜色设置为红色(R=1,G=0,B=0)。所有后续定义的顶点都会是红色。请注意在 OpenGL 中,颜色与许多其它的属性应用于顶点而非图元。图元的颜色由其顶点的颜色插值得来。

第二个四边形定义为为绿色。

对于第三个多边形,其顶点的颜色各不相同。四边形平面的颜色由其顶点的颜色插值而来,如程序的输出所示,结果是白色到深灰色的阴影。

glColor3f(0.2f, 0.2f, 0.2f);  // Dark Gray
glVertex2f(-0.9f, -0.7f);
glColor3f(1.0f, 1.0f, 1.0f);  // White
glVertex2f(-0.5f, -0.7f);
glColor3f(0.2f, 0.2f, 0.2f);  // Dark Gray
glVertex2f(-0.5f, -0.3f);
glColor3f(1.0f, 1.0f, 1.0f);  // White
glVertex2f(-0.9f, -0.3f);

3.9 二维坐标系以及默认视图

下面的图展示了 OpenGL 的二维坐标系统,其与圆心在左下角的直角坐标系相同。

默认的 OpenGL 2D 裁剪区域(即相机捕获的区域)是 x 和 y 分别在-1.0 到 1.0 范围内的正交视图,即以原点为中心的 2x2 正方形。该裁剪区域被映射到屏幕上的视口(viewport)。视口以像素为单位。

研究以上示例,以使自己确信你所创建的 2D 图形在屏幕上被正确定位。

4. 裁剪区域与视口

尝试拉伸窗口使其变大或变小,注意到我们所绘制的形状变形了。我们可以通过 reshape() 回调函数手动处理窗口拉伸事件。

裁剪区域:即能看到的区域,以 OpenGL 坐标系进行衡量。

函数 gluOrtho2D 可被用于设置裁剪区域为 2D 正交视图。在裁剪区域之外的物体会被裁剪掉以至于无法被看到。

void gluOrtho2D(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top)// The default clipping area is (-1.0, 1.0, -1.0, 1.0) in OpenGL coordinates, // i.e., 2x2 square centered at the origin.

要想设置裁剪区域,我们首先需要设置要操作的矩阵,这里即投影矩阵 GL_PROJECTION,将其重置为单位矩阵,最后设置为正交视图以及裁剪区域的上下左右的参数。

// Set to 2D orthographic projection with the specified clipping area
glMatrixMode(GL_PROJECTION);      // Select the Projection matrix for operation
glLoadIdentity();                 // Reset Projection matrix
gluOrtho2D(-1.0, 1.0, -1.0, 1.0); // Set clipping area's left, right, bottom, top

视口:即屏幕上的可视区域,以屏幕坐标系进行衡量,单位为像素。

裁剪区域被映射到视口,我们可以使用函数 glViewport 配置视口。

void glViewport(GLint xTopLeft, GLint yTopLeft, GLsizei width, GLsizei height)

假设裁剪区域的参数 left, right, bottom, top 分别为 -1.0,1.0,-1.0,1.0(在 OpenGL 坐标系下),视口的参数 xTopLeft, xTopRight, width, height 分别为 0, 0, 640, 480(在屏幕坐标系下,单位为像素),则裁剪区域的左下角 (-1.0, -1.0) 被映射为 视口的 (0, 0),右上角 (1.0, 1.0) 被映射为 (639, 479)。很明显如果裁剪区域的长宽比与视口的长宽比不一致,物体就会变形。

4.3 例子 3:裁剪区域与视口(GL03Viewport.cpp)

/** GL03Viewport.cpp: Clipping-area and Viewport* Implementing reshape to ensure same aspect ratio between the* clipping-area and the viewport.*/
#include <windows.h>  // for MS Windows
#include <GL/glut.h>  // GLUT, include glu.h and gl.h/* Initialize OpenGL Graphics */
void initGL() {// Set "clearing" or background colorglClearColor(0.0f, 0.0f, 0.0f, 1.0f); // Black and opaque
}void display() {glClear(GL_COLOR_BUFFER_BIT);   // Clear the color buffer with current clearing color// Define shapes enclosed within a pair of glBegin and glEndglBegin(GL_QUADS);              // Each set of 4 vertices form a quadglColor3f(1.0f, 0.0f, 0.0f); // RedglVertex2f(-0.8f, 0.1f);     // Define vertices in counter-clockwise (CCW) orderglVertex2f(-0.2f, 0.1f);     //  so that the normal (front-face) is facing youglVertex2f(-0.2f, 0.7f);glVertex2f(-0.8f, 0.7f);glColor3f(0.0f, 1.0f, 0.0f); // GreenglVertex2f(-0.7f, -0.6f);glVertex2f(-0.1f, -0.6f);glVertex2f(-0.1f,  0.0f);glVertex2f(-0.7f,  0.0f);glColor3f(0.2f, 0.2f, 0.2f); // Dark GrayglVertex2f(-0.9f, -0.7f);glColor3f(1.0f, 1.0f, 1.0f); // WhiteglVertex2f(-0.5f, -0.7f);glColor3f(0.2f, 0.2f, 0.2f); // Dark GrayglVertex2f(-0.5f, -0.3f);glColor3f(1.0f, 1.0f, 1.0f); // WhiteglVertex2f(-0.9f, -0.3f);glEnd();glBegin(GL_TRIANGLES);          // Each set of 3 vertices form a triangleglColor3f(0.0f, 0.0f, 1.0f); // BlueglVertex2f(0.1f, -0.6f);glVertex2f(0.7f, -0.6f);glVertex2f(0.4f, -0.1f);glColor3f(1.0f, 0.0f, 0.0f); // RedglVertex2f(0.3f, -0.4f);glColor3f(0.0f, 1.0f, 0.0f); // GreenglVertex2f(0.9f, -0.4f);glColor3f(0.0f, 0.0f, 1.0f); // BlueglVertex2f(0.6f, -0.9f);glEnd();glBegin(GL_POLYGON);            // These vertices form a closed polygonglColor3f(1.0f, 1.0f, 0.0f); // YellowglVertex2f(0.4f, 0.2f);glVertex2f(0.6f, 0.2f);glVertex2f(0.7f, 0.4f);glVertex2f(0.6f, 0.6f);glVertex2f(0.4f, 0.6f);glVertex2f(0.3f, 0.4f);glEnd();glFlush();  // Render now
}/* Handler for window re-size event. Called back when the window first appears andwhenever the window is re-sized with its new width and height */
void reshape(GLsizei width, GLsizei height) {  // GLsizei for non-negative integer// Compute aspect ratio of the new windowif (height == 0) height = 1;                // To prevent divide by 0GLfloat aspect = (GLfloat)width / (GLfloat)height;// Set the viewport to cover the new windowglViewport(0, 0, width, height);// Set the aspect ratio of the clipping area to match the viewportglMatrixMode(GL_PROJECTION);  // To operate on the Projection matrixglLoadIdentity();             // Reset the projection matrixif (width >= height) {// aspect >= 1, set the height from -1 to 1, with larger widthgluOrtho2D(-1.0 * aspect, 1.0 * aspect, -1.0, 1.0);} else {// aspect < 1, set the width to -1 to 1, with larger heightgluOrtho2D(-1.0, 1.0, -1.0 / aspect, 1.0 / aspect);}
}/* Main function: GLUT runs as a console application starting at main() */
int main(int argc, char** argv) {glutInit(&argc, argv);          // Initialize GLUTglutInitWindowSize(640, 480);   // Set the window's initial width & height - non-squareglutInitWindowPosition(50, 50); // Position the window's initial top-left cornerglutCreateWindow("Viewport Transform");  // Create window with the given titleglutDisplayFunc(display);       // Register callback handler for window re-paint eventglutReshapeFunc(reshape);       // Register callback handler for window re-size eventinitGL();                       // Our own OpenGL initializationglutMainLoop();                 // Enter the infinite event-processing loopreturn 0;
}

当窗口第一次出现以及任何时候当窗口被重新调整大小时,reshape() 函数被调用,用以确保裁剪区域与视口的长宽比的一致性。图形子系统会将以像素为单位的窗口的宽度与高度作为参数传递给 reshape() 函数。

GLfloat aspect = (GLfloat)width / (GLfloat)height;

glViewport(0, 0, width, height);

我们通过设置视口以使其覆盖调整过大小后的窗口。例如,如果我们想设置视口仅覆盖窗口的四分之一(右下角),可以通过调用函数:glViewport(0, 0, width/2, height/2)

glMatrixMode(GL_PROJECTION);
glLoadIdentity();
if (width >= height) {gluOrtho2D(-1.0 * aspect, 1.0 * aspect, -1.0, 1.0);
} else {gluOrtho2D(-1.0, 1.0, -1.0 / aspect, 1.0 / aspect);
}

我们在此处设置裁剪区域的长宽比以使其匹配视口。具体的设置步骤,我们首先需要选择在映射矩阵上进行操作:glMatrixMode(GL_PROJECTION)。OpenGL 具有两个矩阵,一个负责处理摄像机投影的映射矩阵,一个用于将物体从其本地坐标系转换到世界坐标系的模型视图矩阵。我们通过glLoadIdentity()来重置映射矩阵。

最终,我们调用gluOrtho2D()设置裁剪区域以使其长宽比与视口匹配。如下图所示,较短的一边的范围为 -1 到 1.

我们需要通过主函数中的 glutReshapeFunc()注册reshape()回调函数。

int main(int argc, char** argv) {glutInitWindowSize(640, 480);......glutReshapeFunc(reshape);
}

在上述主函数中,我们指定了初始窗口尺寸为 640x480,其并不是方形的。尝试拉伸窗口并观察变化。

注意reshape()函数在窗口第一次出现时至少运行一次,之后当窗口被重新调整大小时都会被调用。另外,initGL() 函数只运行一次,而display()函数每当窗口被重绘时都会被调用。

5. 平移和旋转

在上述例子中,我们通过指定顶点来摆放形状,而这些顶点都参照同一个坐标原点,即在所谓的世界坐标系下进行定义。我花了很长时间才弄清楚这些顶点的绝对坐标。

这无疑不是很方便,我们可以通过参照图形自身的中心来定义其顶点,即在所谓的模型坐标系下进行定义。之后我们可以使用平移和旋转来在世界坐标系下将图形摆放到目标位置。

5.1 例子 4:平移与旋转(GL04ModelTransform.cpp)

/** GL04ModelTransform.cpp: Model Transform - Translation and Rotation* Transform primitives from their model spaces to world space.*/
#include <windows.h>  // for MS Windows
#include <GL/glut.h>  // GLUT, include glu.h and gl.h/* Initialize OpenGL Graphics */
void initGL() {// Set "clearing" or background colorglClearColor(0.0f, 0.0f, 0.0f, 1.0f); // Black and opaque
}/* Handler for window-repaint event. Call back when the window first appears andwhenever the window needs to be re-painted. */
void display() {glClear(GL_COLOR_BUFFER_BIT);    // Clear the color bufferglMatrixMode(GL_MODELVIEW);      // To operate on Model-View matrixglLoadIdentity();                // Reset the model-view matrixglTranslatef(-0.5f, 0.4f, 0.0f); // Translate left and upglBegin(GL_QUADS);               // Each set of 4 vertices form a quadglColor3f(1.0f, 0.0f, 0.0f);  // RedglVertex2f(-0.3f, -0.3f);     // Define vertices in counter-clockwise (CCW) orderglVertex2f( 0.3f, -0.3f);     //  so that the normal (front-face) is facing youglVertex2f( 0.3f,  0.3f);glVertex2f(-0.3f,  0.3f);glEnd();glTranslatef(0.1f, -0.7f, 0.0f); // Translate right and downglBegin(GL_QUADS);               // Each set of 4 vertices form a quadglColor3f(0.0f, 1.0f, 0.0f); // GreenglVertex2f(-0.3f, -0.3f);glVertex2f( 0.3f, -0.3f);glVertex2f( 0.3f,  0.3f);glVertex2f(-0.3f,  0.3f);glEnd();glTranslatef(-0.3f, -0.2f, 0.0f); // Translate left and downglBegin(GL_QUADS);                // Each set of 4 vertices form a quadglColor3f(0.2f, 0.2f, 0.2f); // Dark GrayglVertex2f(-0.2f, -0.2f);glColor3f(1.0f, 1.0f, 1.0f); // WhiteglVertex2f( 0.2f, -0.2f);glColor3f(0.2f, 0.2f, 0.2f); // Dark GrayglVertex2f( 0.2f,  0.2f);glColor3f(1.0f, 1.0f, 1.0f); // WhiteglVertex2f(-0.2f,  0.2f);glEnd();glTranslatef(1.1f, 0.2f, 0.0f); // Translate right and upglBegin(GL_TRIANGLES);          // Each set of 3 vertices form a triangleglColor3f(0.0f, 0.0f, 1.0f); // BlueglVertex2f(-0.3f, -0.2f);glVertex2f( 0.3f, -0.2f);glVertex2f( 0.0f,  0.3f);glEnd();glTranslatef(0.2f, -0.3f, 0.0f);     // Translate right and downglRotatef(180.0f, 0.0f, 0.0f, 1.0f); // Rotate 180 degreeglBegin(GL_TRIANGLES);               // Each set of 3 vertices form a triangleglColor3f(1.0f, 0.0f, 0.0f); // RedglVertex2f(-0.3f, -0.2f);glColor3f(0.0f, 1.0f, 0.0f); // GreenglVertex2f( 0.3f, -0.2f);glColor3f(0.0f, 0.0f, 1.0f); // BlueglVertex2f( 0.0f,  0.3f);glEnd();glRotatef(-180.0f, 0.0f, 0.0f, 1.0f); // Undo previous rotateglTranslatef(-0.1f, 1.0f, 0.0f);      // Translate right and downglBegin(GL_POLYGON);                  // The vertices form one closed polygonglColor3f(1.0f, 1.0f, 0.0f); // YellowglVertex2f(-0.1f, -0.2f);glVertex2f( 0.1f, -0.2f);glVertex2f( 0.2f,  0.0f);glVertex2f( 0.1f,  0.2f);glVertex2f(-0.1f,  0.2f);glVertex2f(-0.2f,  0.0f);glEnd();glFlush();   // Render now
}/* Handler for window re-size event. Called back when the window first appears andwhenever the window is re-sized with its new width and height */
void reshape(GLsizei width, GLsizei height) {  // GLsizei for non-negative integer// Compute aspect ratio of the new windowif (height == 0) height = 1;                // To prevent divide by 0GLfloat aspect = (GLfloat)width / (GLfloat)height;// Set the viewport to cover the new windowglViewport(0, 0, width, height);// Set the aspect ratio of the clipping area to match the viewportglMatrixMode(GL_PROJECTION);  // To operate on the Projection matrixglLoadIdentity();if (width >= height) {// aspect >= 1, set the height from -1 to 1, with larger widthgluOrtho2D(-1.0 * aspect, 1.0 * aspect, -1.0, 1.0);} else {// aspect < 1, set the width to -1 to 1, with larger heightgluOrtho2D(-1.0, 1.0, -1.0 / aspect, 1.0 / aspect);}
}/* Main function: GLUT runs as a console application starting at main() */
int main(int argc, char** argv) {glutInit(&argc, argv);          // Initialize GLUTglutInitWindowSize(640, 480);   // Set the window's initial width & height - non-squareglutInitWindowPosition(50, 50); // Position the window's initial top-left cornerglutCreateWindow("Model Transform");  // Create window with the given titleglutDisplayFunc(display);       // Register callback handler for window re-paint eventglutReshapeFunc(reshape);       // Register callback handler for window re-size eventinitGL();                       // Our own OpenGL initializationglutMainLoop();                 // Enter the infinite event-processing loopreturn 0;
}
glMatrixMode(GL_MODELVIEW); // To operate on model-view matrix
glLoadIdentity();           // Reset

平移与旋转是所谓的模型变换的一部分,其将模型从模型坐标系下转换到世界坐标系。要想进行模型变换,我们需要首先将当前矩阵模式设置为模型视图矩阵(GL_MODELVIEW)并将其重置为单位矩阵。

OpenGL 作为状态机运行。也就是说,一旦状态被设置,该状态持续存在除非其被改变。结合此处的例子,一旦坐标被平移或旋转了,所有后续的操作都会基于这些被平移或旋转后的坐标。

通过 glTranslate 函数来进行平移:

void gltranslatef (GLfloat x, GLfloat y, GLfloat z)// where (x, y, z) is the translational vector

注意glTranslatef 函数必须要放置在 glBegin/glEnd 的外面,而glColor 函数则可以放置在glBegin/glEnd里面。

通过glRotatef函数来进行旋转:

void glRotatef (GLfloat angle, GLfloat x, GLfloat y, GLfloat z)// where angle specifies the rotation in degree, (x, y, z) forms the axis of rotation

注意旋转的角度的单位为度,而非弧度。

在上述例子中,我们在 x-y 平面对物体进行了平移操作,并围绕 z 轴旋转物体。

6. 动画

6.1 空闲函数

要实现动画(例如旋转图形),你可以在 GLUT 中通过 glutIdleFunc 函数来注册一个空闲回调函数 idle()来处理空闲事件。图形系统将在没有其他事件发生时调用该空闲函数。

void glutIdleFunc(void (*func)(void))

idle()函数中,你可以调用glutPostRedisplay 函数让窗口重新渲染,该函数又会调用display()函数。

void idle() {glutPostRedisplay();   // Post a re-paint request to activate display()
}

注意上述代码与下面直接将 display()注册为空闲函数的代码等价。

// main
glutIdleFunc(display);

6.2 双重缓冲

双重缓冲使用两个显示缓冲区来使动画更加流畅。要显示的下一张屏幕的内容在后缓存中存储,而当前屏幕上显示的内容则在前缓冲中存储。一旦准备过程完成,你就可以使用 glutSwapBuffer 函数来交换前后缓冲区。

要想使用双重缓冲,你需要在代码中做以下两处改变:

  1. 首先在 main() 中包含以下代码,注意需要在创建窗口之前:

glutInitDisplayMode(GLUT_DOUBLE); // Set double buffered mode

  1. display()函数中,将 glFlush()替换为 glutSwapBuffers(),其作用为交换前后缓冲区。

在动画中,我们需要使用双重缓冲。对于静态的显示,单缓冲区足够了。(大多数图形硬件一直使用双重缓冲)。

6.3 例子 5:使用空闲函数的动画(GL05IdleFunc.cpp)

下述程序使用空闲函数以及双重缓冲旋转我们在之前的例子中创建的图形。

/** GL05IdleFunc.cpp: Translation and Rotation* Transform primitives from their model spaces to world space (Model Transform).*/
#include <windows.h>  // for MS Windows
#include <GL/glut.h>  // GLUT, include glu.h and gl.h// Global variable
GLfloat angle = 0.0f;  // Current rotational angle of the shapes/* Initialize OpenGL Graphics */
void initGL() {// Set "clearing" or background colorglClearColor(0.0f, 0.0f, 0.0f, 1.0f); // Black and opaque
}/* Called back when there is no other event to be handled */
void idle() {glutPostRedisplay();   // Post a re-paint request to activate display()
}/* Handler for window-repaint event. Call back when the window first appears andwhenever the window needs to be re-painted. */
void display() {glClear(GL_COLOR_BUFFER_BIT);   // Clear the color bufferglMatrixMode(GL_MODELVIEW);     // To operate on Model-View matrixglLoadIdentity();               // Reset the model-view matrixglPushMatrix();                     // Save model-view matrix settingglTranslatef(-0.5f, 0.4f, 0.0f);    // TranslateglRotatef(angle, 0.0f, 0.0f, 1.0f); // rotate by angle in degreesglBegin(GL_QUADS);                  // Each set of 4 vertices form a quadglColor3f(1.0f, 0.0f, 0.0f);     // RedglVertex2f(-0.3f, -0.3f);glVertex2f( 0.3f, -0.3f);glVertex2f( 0.3f,  0.3f);glVertex2f(-0.3f,  0.3f);glEnd();glPopMatrix();                      // Restore the model-view matrixglPushMatrix();                     // Save model-view matrix settingglTranslatef(-0.4f, -0.3f, 0.0f);   // TranslateglRotatef(angle, 0.0f, 0.0f, 1.0f); // rotate by angle in degreesglBegin(GL_QUADS);glColor3f(0.0f, 1.0f, 0.0f); // GreenglVertex2f(-0.3f, -0.3f);glVertex2f( 0.3f, -0.3f);glVertex2f( 0.3f,  0.3f);glVertex2f(-0.3f,  0.3f);glEnd();glPopMatrix();                      // Restore the model-view matrixglPushMatrix();                     // Save model-view matrix settingglTranslatef(-0.7f, -0.5f, 0.0f);   // TranslateglRotatef(angle, 0.0f, 0.0f, 1.0f); // rotate by angle in degreesglBegin(GL_QUADS);glColor3f(0.2f, 0.2f, 0.2f); // Dark GrayglVertex2f(-0.2f, -0.2f);glColor3f(1.0f, 1.0f, 1.0f); // WhiteglVertex2f( 0.2f, -0.2f);glColor3f(0.2f, 0.2f, 0.2f); // Dark GrayglVertex2f( 0.2f,  0.2f);glColor3f(1.0f, 1.0f, 1.0f); // WhiteglVertex2f(-0.2f,  0.2f);glEnd();glPopMatrix();                      // Restore the model-view matrixglPushMatrix();                     // Save model-view matrix settingglTranslatef(0.4f, -0.3f, 0.0f);    // TranslateglRotatef(angle, 0.0f, 0.0f, 1.0f); // rotate by angle in degreesglBegin(GL_TRIANGLES);glColor3f(0.0f, 0.0f, 1.0f); // BlueglVertex2f(-0.3f, -0.2f);glVertex2f( 0.3f, -0.2f);glVertex2f( 0.0f,  0.3f);glEnd();glPopMatrix();                      // Restore the model-view matrixglPushMatrix();                     // Save model-view matrix settingglTranslatef(0.6f, -0.6f, 0.0f);    // TranslateglRotatef(180.0f + angle, 0.0f, 0.0f, 1.0f); // Rotate 180+angle degreeglBegin(GL_TRIANGLES);glColor3f(1.0f, 0.0f, 0.0f); // RedglVertex2f(-0.3f, -0.2f);glColor3f(0.0f, 1.0f, 0.0f); // GreenglVertex2f( 0.3f, -0.2f);glColor3f(0.0f, 0.0f, 1.0f); // BlueglVertex2f( 0.0f,  0.3f);glEnd();glPopMatrix();                      // Restore the model-view matrixglPushMatrix();                     // Save model-view matrix settingglTranslatef(0.5f, 0.4f, 0.0f);     // TranslateglRotatef(angle, 0.0f, 0.0f, 1.0f); // rotate by angle in degreesglBegin(GL_POLYGON);glColor3f(1.0f, 1.0f, 0.0f); // YellowglVertex2f(-0.1f, -0.2f);glVertex2f( 0.1f, -0.2f);glVertex2f( 0.2f,  0.0f);glVertex2f( 0.1f,  0.2f);glVertex2f(-0.1f,  0.2f);glVertex2f(-0.2f,  0.0f);glEnd();glPopMatrix();                      // Restore the model-view matrixglutSwapBuffers();   // Double buffered - swap the front and back buffers// Change the rotational angle after each display()angle += 0.2f;
}/* Handler for window re-size event. Called back when the window first appears andwhenever the window is re-sized with its new width and height */
void reshape(GLsizei width, GLsizei height) {  // GLsizei for non-negative integer// Compute aspect ratio of the new windowif (height == 0) height = 1;                // To prevent divide by 0GLfloat aspect = (GLfloat)width / (GLfloat)height;// Set the viewport to cover the new windowglViewport(0, 0, width, height);// Set the aspect ratio of the clipping area to match the viewportglMatrixMode(GL_PROJECTION);  // To operate on the Projection matrixglLoadIdentity();if (width >= height) {// aspect >= 1, set the height from -1 to 1, with larger widthgluOrtho2D(-1.0 * aspect, 1.0 * aspect, -1.0, 1.0);} else {// aspect < 1, set the width to -1 to 1, with larger heightgluOrtho2D(-1.0, 1.0, -1.0 / aspect, 1.0 / aspect);}
}/* Main function: GLUT runs as a console application starting at main() */
int main(int argc, char** argv) {glutInit(&argc, argv);          // Initialize GLUTglutInitDisplayMode(GLUT_DOUBLE);  // Enable double buffered modeglutInitWindowSize(640, 480);   // Set the window's initial width & height - non-squareglutInitWindowPosition(50, 50); // Position the window's initial top-left cornerglutCreateWindow("Animation via Idle Function");  // Create window with the given titleglutDisplayFunc(display);       // Register callback handler for window re-paint eventglutReshapeFunc(reshape);       // Register callback handler for window re-size eventglutIdleFunc(idle);             // Register callback handler if no other eventinitGL();                       // Our own OpenGL initializationglutMainLoop();                 // Enter the infinite event-processing loopreturn 0;
}

在上述例子中,我们使用 glPushMatrix 保存当前状态,执行变换,并通过 glPopMatrix来重置为之前保存的状态,而非累积之前的所有平移操作并撤销旋转操作。(不过在上述例子中,我们也可以使用 glLoadIdentity 来重置矩阵)

GLfloat angle = 0.0f;  // Current rotational angle of the shapes

我们定义了一个名为 angle 的全局变量记录所有形状的旋转角度。我们在之后将使用 glRotatef 函数旋转所有的图形这个角度。

angle += 0.2f;

在每次刷新的最后,我们更新这个旋转角度。

glutSwapBuffers();                 // Swap front- and back framebufferglutInitDisplayMode(GLUT_DOUBLE);  // In main(), enable double buffered mode

我们启用双重缓冲并使用 glutSwapBuffer() 来交换前后缓冲区而非使用glFlush() 刷新帧缓冲区以立即显示,这样做可以使动画更加流程。

void idle() {glutPostRedisplay();   // Post a re-paint request to activate display()
}glutIdleFunc(idle);       // In main() - Register callback handler if no other event

我们定义了一个 idle()函数,其请求重新绘制屏幕并调用display()函数。我们在 main() 中通过 glutIdleFunc() 注册了 idle() 函数。

6.4 双重缓冲以及刷新率

当启用双重缓冲时,glutSwapBuffers 与屏幕的刷新间隔(VSync)进行同步。也就是说,这些缓冲将在显示器显示新的一帧时被交换。作为其结果,idle() 函数在最好情况下与以屏幕的刷新率一致的频率刷新动画。它可能以屏幕刷新率的一半,三分之一,四分之一等等的速率运行,因为它必须等待 VSync。

6.5 计时器函数

使用idle()函数,我们无法控制刷新间隔。我们可以通过 GLUT 中的 glutTimerFunc函数注册一个 Timer()函数。该 Timer()函数将以指定的固定的时间间隔被调用。

void glutTimerFunc(unsigned int millis, void (*func)(int value), value)// where millis is the delay in milliseconds, value will be passed to the timer function.

6.6 例子 6:基于计时器函数的动画(GL06TimerFunc.cpp)

下述程序将每 30 毫秒按逆时针旋转我们之前创建的图形两度。

/** GL06TimerFunc.cpp: Translation and Rotation* Transform primitives from their model spaces to world space (Model Transform).*/
#include <windows.h>  // for MS Windows
#include <GL/glut.h>  // GLUT, include glu.h and gl.h// global variable
GLfloat angle = 0.0f;  // rotational angle of the shapes
int refreshMills = 30; // refresh interval in milliseconds/* Initialize OpenGL Graphics */
void initGL() {// Set "clearing" or background colorglClearColor(0.0f, 0.0f, 0.0f, 1.0f); // Black and opaque
}/* Called back when timer expired */
void Timer(int value) {glutPostRedisplay();      // Post re-paint request to activate display()glutTimerFunc(refreshMills, Timer, 0); // next Timer call milliseconds later
}/* Handler for window-repaint event. Call back when the window first appears andwhenever the window needs to be re-painted. */
void display() {glClear(GL_COLOR_BUFFER_BIT);   // Clear the color bufferglMatrixMode(GL_MODELVIEW);     // To operate on Model-View matrixglLoadIdentity();               // Reset the model-view matrixglPushMatrix();                     // Save model-view matrix settingglTranslatef(-0.5f, 0.4f, 0.0f);    // TranslateglRotatef(angle, 0.0f, 0.0f, 1.0f); // rotate by angle in degreesglBegin(GL_QUADS);                  // Each set of 4 vertices form a quadglColor3f(1.0f, 0.0f, 0.0f);     // RedglVertex2f(-0.3f, -0.3f);glVertex2f( 0.3f, -0.3f);glVertex2f( 0.3f,  0.3f);glVertex2f(-0.3f,  0.3f);glEnd();glPopMatrix();                      // Restore the model-view matrixglPushMatrix();                     // Save model-view matrix settingglTranslatef(-0.4f, -0.3f, 0.0f);   // TranslateglRotatef(angle, 0.0f, 0.0f, 1.0f); // rotate by angle in degreesglBegin(GL_QUADS);glColor3f(0.0f, 1.0f, 0.0f); // GreenglVertex2f(-0.3f, -0.3f);glVertex2f( 0.3f, -0.3f);glVertex2f( 0.3f,  0.3f);glVertex2f(-0.3f,  0.3f);glEnd();glPopMatrix();                      // Restore the model-view matrixglPushMatrix();                     // Save model-view matrix settingglTranslatef(-0.7f, -0.5f, 0.0f);   // TranslateglRotatef(angle, 0.0f, 0.0f, 1.0f); // rotate by angle in degreesglBegin(GL_QUADS);glColor3f(0.2f, 0.2f, 0.2f); // Dark GrayglVertex2f(-0.2f, -0.2f);glColor3f(1.0f, 1.0f, 1.0f); // WhiteglVertex2f( 0.2f, -0.2f);glColor3f(0.2f, 0.2f, 0.2f); // Dark GrayglVertex2f( 0.2f,  0.2f);glColor3f(1.0f, 1.0f, 1.0f); // WhiteglVertex2f(-0.2f,  0.2f);glEnd();glPopMatrix();                      // Restore the model-view matrixglPushMatrix();                     // Save model-view matrix settingglTranslatef(0.4f, -0.3f, 0.0f);    // TranslateglRotatef(angle, 0.0f, 0.0f, 1.0f); // rotate by angle in degreesglBegin(GL_TRIANGLES);glColor3f(0.0f, 0.0f, 1.0f); // BlueglVertex2f(-0.3f, -0.2f);glVertex2f( 0.3f, -0.2f);glVertex2f( 0.0f,  0.3f);glEnd();glPopMatrix();                      // Restore the model-view matrixglPushMatrix();                     // Save model-view matrix settingglTranslatef(0.6f, -0.6f, 0.0f);    // TranslateglRotatef(180.0f + angle, 0.0f, 0.0f, 1.0f); // Rotate 180+angle degreeglBegin(GL_TRIANGLES);glColor3f(1.0f, 0.0f, 0.0f); // RedglVertex2f(-0.3f, -0.2f);glColor3f(0.0f, 1.0f, 0.0f); // GreenglVertex2f( 0.3f, -0.2f);glColor3f(0.0f, 0.0f, 1.0f); // BlueglVertex2f( 0.0f,  0.3f);glEnd();glPopMatrix();                      // Restore the model-view matrixglPushMatrix();                     // Save model-view matrix settingglTranslatef(0.5f, 0.4f, 0.0f);     // TranslateglRotatef(angle, 0.0f, 0.0f, 1.0f); // rotate by angle in degreesglBegin(GL_POLYGON);glColor3f(1.0f, 1.0f, 0.0f); // YellowglVertex2f(-0.1f, -0.2f);glVertex2f( 0.1f, -0.2f);glVertex2f( 0.2f,  0.0f);glVertex2f( 0.1f,  0.2f);glVertex2f(-0.1f,  0.2f);glVertex2f(-0.2f,  0.0f);glEnd();glPopMatrix();                      // Restore the model-view matrixglutSwapBuffers();   // Double buffered - swap the front and back buffers// Change the rotational angle after each display()angle += 2.0f;
}/* Handler for window re-size event. Called back when the window first appears andwhenever the window is re-sized with its new width and height */
void reshape(GLsizei width, GLsizei height) {  // GLsizei for non-negative integer// Compute aspect ratio of the new windowif (height == 0) height = 1;                // To prevent divide by 0GLfloat aspect = (GLfloat)width / (GLfloat)height;// Set the viewport to cover the new windowglViewport(0, 0, width, height);// Set the aspect ratio of the clipping area to match the viewportglMatrixMode(GL_PROJECTION);  // To operate on the Projection matrixglLoadIdentity();if (width >= height) {// aspect >= 1, set the height from -1 to 1, with larger widthgluOrtho2D(-1.0 * aspect, 1.0 * aspect, -1.0, 1.0);} else {// aspect < 1, set the width to -1 to 1, with larger heightgluOrtho2D(-1.0, 1.0, -1.0 / aspect, 1.0 / aspect);}
}/* Main function: GLUT runs as a console application starting at main() */
int main(int argc, char** argv) {glutInit(&argc, argv);          // Initialize GLUTglutInitDisplayMode(GLUT_DOUBLE);  // Enable double buffered modeglutInitWindowSize(640, 480);   // Set the window's initial width & height - non-squareglutInitWindowPosition(50, 50); // Position the window's initial top-left cornerglutCreateWindow("Animation via Idle Function");  // Create window with the given titleglutDisplayFunc(display);       // Register callback handler for window re-paint eventglutReshapeFunc(reshape);       // Register callback handler for window re-size eventglutTimerFunc(0, Timer, 0);     // First timer call immediatelyinitGL();                       // Our own OpenGL initializationglutMainLoop();                 // Enter the infinite event-processing loopreturn 0;
}
void Timer(int value) {glutPostRedisplay();                   // Post re-paint request to activate display()glutTimerFunc(refreshMills, Timer, 0); // next Timer call milliseconds later
}

我们将 idle() 函数替换为 timer() 函数,其在计时器超时时发起重绘请求以调用 display()函数。

glutTimerFunc(0, Timer, 0);     // First timer call immediately

main() 函数中,我们注册 timer() 函数并将其立刻激活(通过将初始的计时间隔设置为 0)。

6.7 更多的 GLUT 函数

glutInitDisplayMode :请求以指定模式进行显示,例如颜色模式(GLUT_RGB, GLUT_RGBA, GLUT_INDEX),单/双缓冲(GLUT_SINGLE, GLUT_DOUBLE),启用深度(GLUT_DEPTH),通过位操作符OR ‘|’ 将这些模式结合起来。

void glutInitDisplayMode(unsigned int displayMode)

例如:

glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH);// Use RGBA color, enable double buffering and enable depth buffer

6.8 例子 7:弹跳小球(GL07BouncingBall.cpp)

这个例子展示了一个小球在窗口中弹跳。注意在 OpenGL 中圆并非一个基本几何形状。该例子使用 TRIANGLE_FAN 组成一个圆。

/** GL07BouncingBall.cpp: A ball bouncing inside the window*/
#include <windows.h>  // for MS Windows
#include <GL/glut.h>  // GLUT, includes glu.h and gl.h
#include <Math.h>     // Needed for sin, cos
#define PI 3.14159265f// Global variables
char title[] = "Bouncing Ball (2D)";  // Windowed mode's title
int windowWidth  = 640;     // Windowed mode's width
int windowHeight = 480;     // Windowed mode's height
int windowPosX   = 50;      // Windowed mode's top-left corner x
int windowPosY   = 50;      // Windowed mode's top-left corner yGLfloat ballRadius = 0.5f;   // Radius of the bouncing ball
GLfloat ballX = 0.0f;         // Ball's center (x, y) position
GLfloat ballY = 0.0f;
GLfloat ballXMax, ballXMin, ballYMax, ballYMin; // Ball's center (x, y) bounds
GLfloat xSpeed = 0.02f;      // Ball's speed in x and y directions
GLfloat ySpeed = 0.007f;
int refreshMillis = 30;      // Refresh period in milliseconds// Projection clipping area
GLdouble clipAreaXLeft, clipAreaXRight, clipAreaYBottom, clipAreaYTop;/* Initialize OpenGL Graphics */
void initGL() {glClearColor(0.0, 0.0, 0.0, 1.0); // Set background (clear) color to black
}/* Callback handler for window re-paint event */
void display() {glClear(GL_COLOR_BUFFER_BIT);  // Clear the color bufferglMatrixMode(GL_MODELVIEW);    // To operate on the model-view matrixglLoadIdentity();              // Reset model-view matrixglTranslatef(ballX, ballY, 0.0f);  // Translate to (xPos, yPos)// Use triangular segments to form a circleglBegin(GL_TRIANGLE_FAN);glColor3f(0.0f, 0.0f, 1.0f);  // BlueglVertex2f(0.0f, 0.0f);       // Center of circleint numSegments = 100;GLfloat angle;for (int i = 0; i <= numSegments; i++) { // Last vertex same as first vertexangle = i * 2.0f * PI / numSegments;  // 360 deg for all segmentsglVertex2f(cos(angle) * ballRadius, sin(angle) * ballRadius);}glEnd();glutSwapBuffers();  // Swap front and back buffers (of double buffered mode)// Animation Control - compute the location for the next refreshballX += xSpeed;ballY += ySpeed;// Check if the ball exceeds the edgesif (ballX > ballXMax) {ballX = ballXMax;xSpeed = -xSpeed;} else if (ballX < ballXMin) {ballX = ballXMin;xSpeed = -xSpeed;}if (ballY > ballYMax) {ballY = ballYMax;ySpeed = -ySpeed;} else if (ballY < ballYMin) {ballY = ballYMin;ySpeed = -ySpeed;}
}/* Call back when the windows is re-sized */
void reshape(GLsizei width, GLsizei height) {// Compute aspect ratio of the new windowif (height == 0) height = 1;                // To prevent divide by 0GLfloat aspect = (GLfloat)width / (GLfloat)height;// Set the viewport to cover the new windowglViewport(0, 0, width, height);// Set the aspect ratio of the clipping area to match the viewportglMatrixMode(GL_PROJECTION);  // To operate on the Projection matrixglLoadIdentity();             // Reset the projection matrixif (width >= height) {clipAreaXLeft   = -1.0 * aspect;clipAreaXRight  = 1.0 * aspect;clipAreaYBottom = -1.0;clipAreaYTop    = 1.0;} else {clipAreaXLeft   = -1.0;clipAreaXRight  = 1.0;clipAreaYBottom = -1.0 / aspect;clipAreaYTop    = 1.0 / aspect;}gluOrtho2D(clipAreaXLeft, clipAreaXRight, clipAreaYBottom, clipAreaYTop);ballXMin = clipAreaXLeft + ballRadius;ballXMax = clipAreaXRight - ballRadius;ballYMin = clipAreaYBottom + ballRadius;ballYMax = clipAreaYTop - ballRadius;
}/* Called back when the timer expired */
void Timer(int value) {glutPostRedisplay();    // Post a paint request to activate display()glutTimerFunc(refreshMillis, Timer, 0); // subsequent timer call at milliseconds
}/* Main function: GLUT runs as a console application starting at main() */
int main(int argc, char** argv) {glutInit(&argc, argv);            // Initialize GLUTglutInitDisplayMode(GLUT_DOUBLE); // Enable double buffered modeglutInitWindowSize(windowWidth, windowHeight);  // Initial window width and heightglutInitWindowPosition(windowPosX, windowPosY); // Initial window top-left corner (x, y)glutCreateWindow(title);      // Create window with given titleglutDisplayFunc(display);     // Register callback handler for window re-paintglutReshapeFunc(reshape);     // Register callback handler for window re-shapeglutTimerFunc(0, Timer, 0);   // First timer call immediatelyinitGL();                     // Our own OpenGL initializationglutMainLoop();               // Enter event-processing loopreturn 0;
}

TODO:详细的代码解释

7. 使用 GLUT 处理键盘输入

我们可以分别为常规按键和特殊按键分别组成回调函数。

  • glutKeyboardFunc 函数:注册处理键盘输入事件的回调函数

c++ void glutKeyboardFunc (void (*func)(unsigned char key, int x, int y) // key is the char pressed, e.g., 'a' or 27 for ESC // (x, y) is the mouse location in Windows' coordinates

  • glutSpecialFunc 函数:注册处理特殊按键(例如箭头按键和功能键)事件的回调函数

c++ void glutSpecialFunc (void (*func)(int specialKey, int x, int y) // specialKey: GLUT_KEY_* (* for LEFT, RIGHT, UP, DOWN, HOME, END, PAGE_UP, PAGE_DOWN, F1,...F12). // (x, y) is the mouse location in Windows' coordinates

7.1 例子 8:在全屏模式和窗口模式之间进行切换(GL08FullScreen.cpp)

对于上述的弹跳小球程序,下述程序实现了使用 F1 键将其从全屏模式以及窗口模式之间进行切换的功能。

/** GL08FullScreen.cpp: Switching between full-screen mode and windowed-mode*/
#include <windows.h>  // for MS Windows
#include <GL/glut.h>  // GLUT, includes glu.h and gl.h
#include <Math.h>     // Needed for sin, cos
#define PI 3.14159265f// Global variables
char title[] = "Full-Screen & Windowed Mode";  // Windowed mode's title
int windowWidth  = 640;     // Windowed mode's width
int windowHeight = 480;     // Windowed mode's height
int windowPosX   = 50;      // Windowed mode's top-left corner x
int windowPosY   = 50;      // Windowed mode's top-left corner yGLfloat ballRadius = 0.5f;   // Radius of the bouncing ball
GLfloat ballX = 0.0f;         // Ball's center (x, y) position
GLfloat ballY = 0.0f;
GLfloat ballXMax, ballXMin, ballYMax, ballYMin; // Ball's center (x, y) bounds
GLfloat xSpeed = 0.02f;      // Ball's speed in x and y directions
GLfloat ySpeed = 0.007f;
int refreshMillis = 30;      // Refresh period in milliseconds// Projection clipping area
GLdouble clipAreaXLeft, clipAreaXRight, clipAreaYBottom, clipAreaYTop;bool fullScreenMode = true; // Full-screen or windowed mode?/* Initialize OpenGL Graphics */
void initGL() {glClearColor(0.0, 0.0, 0.0, 1.0); // Set background (clear) color to black
}/* Callback handler for window re-paint event */
void display() {glClear(GL_COLOR_BUFFER_BIT);  // Clear the color bufferglMatrixMode(GL_MODELVIEW);    // To operate on the model-view matrixglLoadIdentity();              // Reset model-view matrixglTranslatef(ballX, ballY, 0.0f);  // Translate to (xPos, yPos)// Use triangular segments to form a circleglBegin(GL_TRIANGLE_FAN);glColor3f(0.0f, 0.0f, 1.0f);  // BlueglVertex2f(0.0f, 0.0f);       // Center of circleint numSegments = 100;GLfloat angle;for (int i = 0; i <= numSegments; i++) { // Last vertex same as first vertexangle = i * 2.0f * PI / numSegments;  // 360 deg for all segmentsglVertex2f(cos(angle) * ballRadius, sin(angle) * ballRadius);}glEnd();glutSwapBuffers();  // Swap front and back buffers (of double buffered mode)// Animation Control - compute the location for the next refreshballX += xSpeed;ballY += ySpeed;// Check if the ball exceeds the edgesif (ballX > ballXMax) {ballX = ballXMax;xSpeed = -xSpeed;} else if (ballX < ballXMin) {ballX = ballXMin;xSpeed = -xSpeed;}if (ballY > ballYMax) {ballY = ballYMax;ySpeed = -ySpeed;} else if (ballY < ballYMin) {ballY = ballYMin;ySpeed = -ySpeed;}
}/* Call back when the windows is re-sized */
void reshape(GLsizei width, GLsizei height) {// Compute aspect ratio of the new windowif (height == 0) height = 1;                // To prevent divide by 0GLfloat aspect = (GLfloat)width / (GLfloat)height;// Set the viewport to cover the new windowglViewport(0, 0, width, height);// Set the aspect ratio of the clipping area to match the viewportglMatrixMode(GL_PROJECTION);  // To operate on the Projection matrixglLoadIdentity();             // Reset the projection matrixif (width >= height) {clipAreaXLeft   = -1.0 * aspect;clipAreaXRight  = 1.0 * aspect;clipAreaYBottom = -1.0;clipAreaYTop    = 1.0;} else {clipAreaXLeft   = -1.0;clipAreaXRight  = 1.0;clipAreaYBottom = -1.0 / aspect;clipAreaYTop    = 1.0 / aspect;}gluOrtho2D(clipAreaXLeft, clipAreaXRight, clipAreaYBottom, clipAreaYTop);ballXMin = clipAreaXLeft + ballRadius;ballXMax = clipAreaXRight - ballRadius;ballYMin = clipAreaYBottom + ballRadius;ballYMax = clipAreaYTop - ballRadius;
}/* Called back when the timer expired */
void Timer(int value) {glutPostRedisplay();    // Post a paint request to activate display()glutTimerFunc(refreshMillis, Timer, 0); // subsequent timer call at milliseconds
}/* Callback handler for special-key event */
void specialKeys(int key, int x, int y) {switch (key) {case GLUT_KEY_F1:    // F1: Toggle between full-screen and windowed modefullScreenMode = !fullScreenMode;         // Toggle stateif (fullScreenMode) {                     // Full-screen modewindowPosX   = glutGet(GLUT_WINDOW_X); // Save parameters for restoring laterwindowPosY   = glutGet(GLUT_WINDOW_Y);windowWidth  = glutGet(GLUT_WINDOW_WIDTH);windowHeight = glutGet(GLUT_WINDOW_HEIGHT);glutFullScreen();                      // Switch into full screen} else {                                         // Windowed modeglutReshapeWindow(windowWidth, windowHeight); // Switch into windowed modeglutPositionWindow(windowPosX, windowPosX);   // Position top-left corner}break;}
}/* Main function: GLUT runs as a console application starting at main() */
int main(int argc, char** argv) {glutInit(&argc, argv);            // Initialize GLUTglutInitDisplayMode(GLUT_DOUBLE); // Enable double buffered modeglutInitWindowSize(windowWidth, windowHeight);  // Initial window width and heightglutInitWindowPosition(windowPosX, windowPosY); // Initial window top-left corner (x, y)glutCreateWindow(title);      // Create window with given titleglutDisplayFunc(display);     // Register callback handler for window re-paintglutReshapeFunc(reshape);     // Register callback handler for window re-shapeglutTimerFunc(0, Timer, 0);   // First timer call immediatelyglutSpecialFunc(specialKeys); // Register callback handler for special-key eventglutFullScreen();             // Put into full screeninitGL();                     // Our own OpenGL initializationglutMainLoop();               // Enter event-processing loopreturn 0;
}

TODO:详细的代码解释

注意,下面的部分请在我的博客网站上查看,因为本篇文章过长,知乎上无法发布。

OpenGl 环境搭建与介绍​iamazing.cn

7.2 例子 9:按键控制(GL09KeyControl.cpp)

8. 使用 GLUT 处理鼠标输入

8.1 例子 10:鼠标控制(GL10MouseControl.cpp)

8.2 例子 11:一个简单的绘图程序

TODO:使用鼠标移动事件以及 GL_LINE_STRIP。

注意

  • 原文链接:OpenGL Tutorial An Introduction on OpenGL with 2D Graphics
  • OpenGL / 计算机图形学参考与资料
  • 翻译的过程中,我增添与删除了一些内容
  • 如有谬误,还望指正
  • 禁止转载

c++ 判断硬件是否支持opengl_【译】OpenGL 教程:二维图形绘制相关推荐

  1. 【OpenGL ES】二维图形绘制

    目录 OpenGL ES 学习--2D 着色器语言基础知识 绘制纯色背景 JAVA版本 C++版本 绘制圆点.直线.三角形 JAVA版本 C++版本 绘制彩色三角形 JAVA版本 C++版本 绘制纯色 ...

  2. 初学计算机图形学——OpenGL实现二维图形平移,旋转(不使用opengl自带的函数,齐次方程实现)

    要求:1.给定窗口中绘制坐标系 2.给定坐标系内绘制三角形 3.完成平移.旋转等变换后的三角形绘制 4.采用OpenGL绘制 代码: #include<windows.h> #includ ...

  3. 二维图形平移变换c语言程序,[转载]计算机图形学Opengl实现二维图形的平移、旋转、缩放复合变换...

    参考课堂教学中关于模型变化的讲解,编写对一个三角形分别实现平移.缩放.旋转等变化的源码及效果图.请以该例为蓝本,实现3题的代码编写. 如下的几幅图,第一幅就是在给出的代码部分进行修改,将GL_FLAT ...

  4. OpenGL教程 用2D图形介绍OpenGL

    OpenGL教程 用2D图形介绍OpenGL 1.设置OpenGL 要设置OpenGL,取决于您的编程平台,请阅读: 如何在C / C ++中编写OpenGL程序. 如何在Java中编写OpenGL程 ...

  5. PHP如何判断一个数组是一维数组或者是二维数组?用什么函数?

    如题:如何判断一个数组是一维数组或者是二维数组?用什么函数? 判断数量即可 <?php if (count($array) == count($array, 1)) {echo '是一维数组'; ...

  6. OpenGL将二维图形显示为三维点云图

    全文参考:http://blog.csdn.net/sky_freebird/article/details/6695059 运用OpenGL实现二维图像的三维点云图显示. #include < ...

  7. OPENGL学习(三)GLUT二维图像绘制

    文章目录 1.Opengl的Hello world 2.初始化(调整试图) 3.增加Reshape函数 3.事件 4.动画 1.Opengl的Hello world 最基础的程序,画了个三角形,请确保 ...

  8. 【我的安卓进阶之旅】Opengl Es(5)三维图形绘制圆锥、圆柱和球体(附Github地址)

    之前的博客中,我们绘制了三角形.正方形.圆形.立方体,今天我们将绘制圆锥.圆柱和球体.能够绘制这些基本的常规几何形体后,其他的常见几何形体的绘制对于我们来说就基本没问题了. 绘制圆锥 由之前的博客,我 ...

  9. [译][Tkinter 教程02] Message 控件

    已获原作者授权. 原系列地址: Python Tkinter Message 控件 Message 控件用来展示一些文字短消息. Message 和 Label 控件有些类似, 但在展示文字方面比 L ...

最新文章

  1. CentOS7.4下DNS服务器软件BIND安装及相关的配置(一)
  2. java 视频切片_关于视频播放、视频切片、跨域访问视频
  3. 统一修改文件下所有图片大小 Python3
  4. linux服务器磁盘格式,linux下查看磁盘分区的文件系统格式
  5. 操作系统之I/O管理:2、SPOOLing技术(假脱机技术)
  6. python mobilenetssd android_tensorflow+ssd_mobilenet实现目标检测的训练
  7. SharePoint 2010: Claims-based Authentication
  8. SVN服务更换小记(由subversion更换为VisualSVN)
  9. 【Turbo】基于MATLAB的turbo编译码算法的仿真
  10. 【编译原理总结】由正则式构造等价的DFA并将其最小化
  11. CAD Voronoi图插件
  12. 计算机里pc是什么,什么是pc?pc是什么意思?
  13. python复数什么意思_python 复数是什么意思
  14. 适合购买免备案云服务器一般是哪些网站业务?
  15. 整理了一下浅墨大神的Visual C++/DirectX 9.0c的游戏开发手记
  16. 视频画面添加图片,这个方法分享给你
  17. 使用cublas实现矩阵乘法
  18. quartus ii引脚分配再学习下
  19. Android中根据dialog的展示与否控制软键盘的显示与隐藏
  20. Python学习之处理excel一:读取excel以及基本操作

热门文章

  1. 基于java设计一个可视化中智数计算器
  2. 小程序尺寸单位 rpx 和 px 的换算
  3. 基于PaddleOCR的FCENet论文总结和代码详解(持续更新)
  4. 2021 全球程序员收入报告出炉!字节高级码农年薪 274 万元排第 5
  5. 机器学习平台Angel 3.0
  6. Duilib的简单使用(一、duilib demo)
  7. 展豪说 41-80d
  8. Transmogrify.AI automl 库
  9. java 设置随机数种子_java设置随机数种子教程 菜鸟请进
  10. flutter 层叠布局Stack、Positioned