1.设置OpenGL
要设置OpenGL,取决于您的编程平台,请阅读:
- 如何在C / C ++中编写OpenGL程序。
- 如何在Java中编写OpenGL程序:JOGL或LWJGL。
- 如何在Android中编写OpenGL | ES程序。
1.1示例1:设置OpenGL和GLUT(GL01Hello.cpp)
确保您可以运行GL01Hello.cpp
“ 如何使用C / C ++编写OpenGL程序 ”中描述的“ ”,如下所示:
1
2
3
4
五
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
三十
31
32
33
34
35
36 |
#include
#include
void display(){
glClearColor(0.0f,0.0f,0.0f,1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glBegin(GL_QUADS) ;
glColor3f(1.0f,0.0f,0.0f);
glVertex2f(-0.5f,-0.5f);
glVertex2f(0.5f,-0.5f);
glVertex2f(0.5f,0.5f);
glVertex2f(-0.5f,0.5f);
glEnd();
glFlush();
}
int main(int argc,char ** argv){
glutInit(&argc,argv);
glutCreateWindow(“OpenGL Setup Test”);
glutInitWindowSize(320,320) ;
glutInitWindowPosition(50,50);
glutDisplayFunc(display);
glutMainLoop();
返回0;
} |
#include
标题“ windows.h
”仅用于Windows平台。
#include
我们还包括GLUT头文件,它确保包含“ glu.h
”(用于GL实用程序)和“ gl.h
”(用于核心OpenGL)。
程序的其余部分将在适当的时候解释。
2.介绍
OpenGL(开放图形库)是一种用于生成3D(包括2D)图形的跨平台,硬件加速的,与语言无关的工业标准API。现代计算机拥有专用GPU(图形处理单元)和自己的内存,可加速图形渲染速度。OpenGL是图形硬件的软件接口。换句话说,由您的应用程序发出的OpenGL图形渲染命令可以被引导到图形硬件并加速。
我们在OpenGL程序中使用3套库:
- 核心的OpenGL(GL) :由数百个命令,这与前缀“开头的
gl
”(如glColor
,glVertex
,glTranslate
,glRotate
)。Core OpenGL通过一系列几何图元(如点,线和多边形)对对象进行建模。
- OpenGL实用程序库(GLU):建立在核心OpenGL之上,提供重要的实用程序(如设置摄像头视图和投影)以及更多建筑模型(如qradric曲面和多边形镶嵌)。GLU命令以前缀“
glu
”开始(例如gluLookAt
,,gluPerspective
)。
- OpenGL实用程序工具包(GLUT):OpenGL被设计为独立于窗口系统或操作系统。GLUT需要与操作系统交互(例如创建窗口,处理键和鼠标输入); 它还提供了更多的建筑模型(如球体和圆环)。GLUT命令以“
glut
”(例如glutCreatewindow
,,glutMouseFunc
)的前缀开头。GLUT是平台独立的,它建立在特定于平台的OpenGL扩展之上,例如用于X Window系统的GLX,用于Microsoft Window的WGL以及用于Mac OS的AGL,CGL或Cocoa。
从opengl.org引用:“GLUT专为构建中小型OpenGL程序而设计,虽然GLUT非常适合学习OpenGL和开发简单的OpenGL应用程序,但GLUT并不是一个全功能的工具包,因此需要复杂用户界面的大型应用程序最好使用本机窗口系统工具包,GLUT简单,简单,小巧。 “
GLUT的替代方案包括SDL,....
- OpenGL Extension Wrangler Library(GLEW):“GLEW是一个跨平台的开源C / C ++扩展加载库,GLEW提供高效的运行时机制来确定哪些OpenGL扩展在目标平台上受支持。” 源代码和预编译二进制文件可在http://glew.sourceforge.net/找到。一个名为“
glewinfo.exe
”(在“ bin
”目录下)的独立实用程序可用于生成图形系统支持的OpenGL函数列表。
- 其他。
3.顶点,原始和颜色
3.1例2:顶点,基元和颜色(GL02Primitive.cpp)
尝试构建并运行这个OpenGL C / C ++程序:
1
2
3
4
五
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
三十
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80 |
#include
#include
void initGL(){
glClearColor(0.0f,0.0f,0.0f,1.0f);
}
void display(){
glClear(GL_COLOR_BUFFER_BIT);
glBegin(GL_QUADS)中的 ;
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);
glColor3f(0.0f,1.0f,0.0f);
glVertex2f(-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);
glVertex2f(-0.9f,-0.7f);
glColor3f(1.0f,1.0f,1.0f);
glVertex2f(-0.5f,-0.7f);
glColor3f(0.2f,0.2f,0.2f);
glVertex2f(-0.5f,-0.3f);
glColor3f(1.0f,1.0f,1.0f);
glVertex2f(-0.9f,-0.3f);
glEnd();
在glBegin(GL_TRIANGLES);
glColor3f(0.0f,0.0f,1.0f);
glVertex2f(0.1f,-0.6f);
glVertex2f(0.7f,-0.6f);
glVertex2f(0.4f,-0.1f);
glColor3f(1.0f,0.0f,0.0f);
glVertex2f(0.3f,-0.4f);
glColor3f(0.0f,1.0f,0.0f);
glVertex2f(0.9f,-0.4f);
glColor3f(0.0f,0.0f,1.0f);
glVertex2f(0.6f,-0.9f);
glEnd();
在glBegin(GL_POLYGON);
glColor3f(1.0f,1.0f,0.0f);
glVertex2f(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();
}
int main(int argc,char ** argv){
glutInit(&argc,argv);
glutCreateWindow(“Vertex,Primitive&Color”);
glutInitWindowSize(320,320) ;
glutInitWindowPosition(50,50);
glutDisplayFunc(display);
initGL();
glutMainLoop();
返回0;
} |
预期输出和坐标如下。请注意,4个形状具有纯色,并且2个形状从其顶点具有颜色混合。
我将在下面的章节中解释这个程序。
3.2作为状态机的OpenGL
OpenGL作为一个状态机运行,并维护一组状态变量(如前景色,背景色等等)。在状态机中,一旦设置了状态变量的值,该值就会一直存在,直到给出新的值。
例如,我们设置了“清理”(背景)颜色为黑色,一旦在initGL()
。我们使用此设置display()
重复清除窗口(display()
每当有窗口重新绘制请求时都会调回) - 清除颜色在整个程序中不会更改。
glClearColor(0.0f,0.0f,0.0f,1.0f);
glClear(GL_COLOR_BUFFER_BIT));
另一个例子:如果我们使用glColor
函数将当前前景色设置为“红色”,那么“红色”将用于所有后续顶点,直到我们使用另一个glColor
函数来改变前景色。
在状态机中,一切都应该保留,直到你明确地改变它!
3.3 OpenGL函数的命名约定
一个OpenGL的功能:
- 以小写字母
gl
(对于核心OpenGL),glu
(对于OpenGL实用程序)或glut
(对于OpenGL Utility Toolkit)开头。
- 其次是该函数的目的,在骆驼情况下(初始大写),例如
glColor
指定绘图颜色,glVertex
以定义顶点的位置。
- 随后是参数的规格,例如,
glColor3f
需要三个float
参数。glVectex2i
需要两个int
参数。
(这是必需的,因为C语言不支持函数重载。不同版本的函数需要写入不同的参数列表。)
公约可以表述如下:
returnType glFunction[234] [sifd](类型值,...);
returnType glFunction[234] [sifd] v(type * value);
函数可能需要2,3或4个参数,类型为s
(GLshort
),i
(GLint
),f
(GLfloat
)或d
(GLdouble
)。在“ v
”(用于矢量)表示该参数保持在2,3,或4个元素的数组,并传递到函数作为数组的指针。
OpenGL定义了自己的数据类型:
- 有符号整数:
GLbyte
(8位),GLshort
(16位),GLint
(32位)。
- 无符号整数:
GLubyte
(8位),GLushort
(16位),GLuint
(32位)。
- 浮点数:
GLfloat
(32位),GLdouble
(64位)GLclampf
和GLclampd
(0.0到1.0之间)。
GLboolean
(unsigned char,0代表false,非0代表true)。
GLsizei
(32位非负整数)。
GLenum
(32位枚举整数)。
OpenGL类型typedef
在“ gl.h
” 中定义如下:
typedef unsigned int GLenum;
typedef unsigned char GLboolean;
typedef unsigned int GLbitfield;
typedef void GLvoid;
typedef signed char GLbyte;
typedef short GLshort;
typedef int GLint;
typedef unsigned char GLubyte;
typedef unsigned short GLushort;
typedef unsigned short GLuint;
typedef int GLsizei;
typedef float GLfloat;
typedef float GLclampf;
typedef double GLdouble;
typedef double GLclampd;
OpenGL的常量以“ GL_
”,“ GLU_
”或“ GLUT_
”开头,大写字母用下划线分隔,例如GL_COLOR_BUFFER_BIT
。
举些例子,
glVertex3f(1.1f,2.2f,3.3f);
glVertex2i(4,5);
glColor4f(0.0f,0.0f,0.0f,1.0f);
GLdouble aVertex [] = {1.1,2.2,3.3};
glVertex3fv(aVertex);
3.4一次性初始化initGL()
这initGL()
意味着执行一次性OpenGL初始化任务,例如设置清除颜色。initGL()
被调用一次(并且仅一次)main()
。
3.5回调处理程序显示()
该函数display()
被称为回调事件处理程序。事件处理程序提供对特定事件的响应(如按键,鼠标单击,窗口绘制)。该函数旨在成为窗口绘制事件的处理程序。OpenGL图形系统 响应于重新绘制窗口的窗口绘制请求(例如,首先出现窗口,最小化后窗口被恢复,窗口被调整大小)而回调。回调意味着该函数由系统调用,而不是由您的程序调用。display()
display()
在Display()
当窗口第一次出现的运行每一次后续的再绘制请求。注意我们在display()
函数中包含了OpenGL图形渲染代码,以便在窗口第一次出现时和每次重新绘制请求时重新绘制整个窗口。
3.6设置GLUT - main()
GLUT提供高级实用程序来简化OpenGL编程,尤其是与操作系统交互(例如创建窗口,处理键和鼠标输入)。上述程序中使用了以下GLUT函数:
glutInit
:初始化GLUT,必须在其他GL / GLUT函数之前调用。它采用与。的相同的论点main()
。void glutInit(int * argc,char ** argv)
glutCreateWindow
:用给定的标题创建一个窗口。int glutCreateWindow(char * title)
glutInitWindowSize
:指定初始窗口宽度和高度(以像素为单位)。void glutInitWindowSize(int width,int height)
glutInitWindowPosition
:将初始窗口的左上角定位在(x,y)处。以像素为单位的坐标(x,y)以窗口坐标测量,即原点(0,0)位于屏幕的左上角; x轴指向右,y轴指向下。void glutInitWindowPosition(int x,int y)
glutDisplayFunc
:注册回调函数(或事件处理程序)以处理窗口绘制事件。OpenGL图形系统在收到窗口重新绘制请求时调用此处理程序。在这个例子中,我们将该函数注册display()
为处理程序。void glutDisplayFunc(void(* func)(void))
glutMainLoop
:进入无限事件处理循环,即让OpenGL图形系统等待事件(如重绘),并触发相应的事件处理程序(如display()
)。void glutMainLoop()
在main()
这个例子的 功能中:
glutInit(&argc,argv);
glutCreateWindow(“Vertex,Primitive&Color”);
glutInitWindowSize(320,320);
glutInitWindowPosition(50,50);
我们初始化GLUT并创建一个标题,初始大小和位置的窗口。
glutDisplayFunc(显示器);
我们注册display()
函数作为window-paint事件的回调处理程序。也就是说,display()
当窗口第一次出现时以及每当有重新绘制窗口的请求时运行。
initGL();
我们称之为initGL()
执行所有的一次性初始化操作。在这个例子中,我们设置清除(背景)颜色一次,并在display()
函数中重复使用它。
glutMainLoop();
然后,我们将程序放入事件处理循环中,等待事件(如窗口绘制请求)触发相应的事件处理程序(如display()
)。
3.7颜色
我们使用glColor
函数来设置前景色,并使用glClearColor
函数来设置背景(或清除)颜色。
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)
笔记:
- 颜色通常指定在
float
范围0.0f
和1.0f
。
- 可以使用RGB(红 - 绿 - 蓝)或RGBA(红 - 绿 - 蓝 - 阿)组件指定颜色。'A'(或alpha)指定透明度(或不透明度)索引,值为1表示不透明(不透明且不能透视),值0表示总透明度。我们稍后会讨论alpha。
在上面的例子中,我们通过glClearColor
in 设置背景颜色initGL()
,其中R = 0,G = 0,B = 0(黑色)和A = 1(不透明且不能透视)。
glClearColor(0.0f,0.0f,0.0f,1.0f);
在中display()
,我们glColor3f
为后续顶点设置顶点颜色通孔。例如,R = 1,G = 0,B = 0(红色)。
glColor3f(1.0f,0.0f,0.0f) ;
3.8几何图元
在OpenGL中,一个对象由三角形,四边形,线段和点等几何图元组成。基元由一个或多个顶点组成。OpenGL支持以下原语:
的几何图元是通过指定通过其顶点定义glVertex
功能,一对封闭的内glBegin
和glEnd
。
void glBegin(GLenum 形状)
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
,每个三glVertex
的定义一个三角形。
顶点通常以float
精度指定。这是因为整数不适合三角运算(需要执行转换等转换)。精度float
足以执行中间操作,并将对象最终呈现为屏幕上的像素(分辨率为800x600,积分精度)。double
精度往往是没有必要的。
在上面的例子中:
在glBegin(GL_QUADS);
glEnd();
我们定义了3个颜色四边形(GL_QUADS
)和12个glVertex()
函数。
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);
glVertex2f(-0.9f,-0.7f);
glColor3f(1.0f,1.0f,1.0f);
glVertex2f(-0.5f,-0.7f);
glColor3f(0.2f,0.2f,0.2f);
glVertex2f(-0.5f,-0.3f);
glColor3f(1.0f,1.0f,1.0f);
glVertex2f(-0.9f,-0.3f);
3.9 2D坐标系和默认视图
下图显示了OpenGL 2D坐标系,它与位于左下角的原始2D笛卡尔坐标相对应。
默认的OpenGL二维裁剪区域(即摄像机捕捉的区域)是x和y在-1.0和1.0范围内的正交视图,即以原点为中心的2x2正方形。此裁剪区域被映射到屏幕上的视口。视口以像素为单位进行测量。
研究上述示例以说服您自己创建的2D图形在屏幕上正确定位。
4.剪辑区域和视口
尝试拖动窗口的角落以使其变大或变小。注意到所有的形状都是扭曲的。
我们可以通过回调处理程序来处理窗口的大小调整reshape()
,该处理程序可以根据窗口的高宽比调整OpenGL裁剪区域。
剪切区域:剪切区域是指在OpenGL坐标系中可以看到的区域(即由摄像机拍摄)。
该功能gluOrtho2D
可用于设置2D正交视图的裁剪区域。裁剪区域外的对象将被裁剪掉,无法看到。
void gluOrtho2D(GLdouble left,GLdouble right,GLdouble bottom,GLdouble top) -1.0,1.0 ,-1.0,1.0 )
为了设置裁剪区域,我们需要发出如下一系列命令:我们首先选择所谓的投影矩阵进行操作,并将投影矩阵重置为标识。然后,我们通过选择所需剪贴区域的二维正交视图gluOrtho2D()
。
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(-1.0,1.0,-1.0,1.0);
视口:视口引用窗口(屏幕)上的显示区域,屏幕坐标(不包括标题栏)以像素为单位测量。
裁剪区域被映射到视口。我们可以使用glViewport
函数来配置视口。
void glViewport(GLint xTopLeft,GLint yTopLeft,GLsizei width,GLsizei height)
假设裁剪区域的(左,右,底部,顶部)是(-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)。很显然,如果裁剪区域和视口的纵横比不相同,则形状将会失真。
请注意,在前面的示例中,320x320的窗口大小呈方形,其纵横比与默认的2x2方形剪切区域一致。
4.1示例3:裁剪区域和视口(GL03Viewport.cpp)
1
2
3
4
五
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
三十
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102 |
#include
#include
void initGL(){
glClearColor(0.0f,0.0f,0.0f,1.0f);
}
void display(){
glClear(GL_COLOR_BUFFER_BIT);
glBegin(GL_QUADS)中的 ;
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);
glColor3f(0.0f,1.0f,0.0f);
glVertex2f(-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);
glVertex2f(-0.9f,-0.7f);
glColor3f(1.0f,1.0f,1.0f);
glVertex2f(-0.5f,-0.7f);
glColor3f(0.2f,0.2f,0.2f);
glVertex2f(-0.5f,-0.3f);
glColor3f(1.0f,1.0f,1.0f);
glVertex2f(-0.9f,-0.3f);
glEnd();
在glBegin(GL_TRIANGLES);
glColor3f(0.0f,0.0f,1.0f);
glVertex2f(0.1f,-0.6f);
glVertex2f(0.7f,-0.6f);
glVertex2f(0.4f,-0.1f);
glColor3f(1.0f,0.0f,0.0f);
glVertex2f(0.3f,-0.4f);
glColor3f(0.0f,1.0f,0.0f);
glVertex2f(0.9f,-0.4f);
glColor3f(0.0f,0.0f,1.0f);
glVertex2f(0.6f,-0.9f);
glEnd();
在glBegin(GL_POLYGON);
glColor3f(1.0f,1.0f,0.0f);
glVertex2f(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();
}
void reshape(GLsizei width,GLsizei height){
if(height == 0)height = 1;
GLfloat aspect =(GLfloat)width /(GLfloat)height;
glViewport(0,0,width,height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
if(width> = height){
gluOrtho2D(-1.0 *方面,1.0 *方面,-1.0,1.0);
} else {
gluOrtho2D(-1.0,1.0,-1.0 /方面,1.0 /方面);
}
}
int main(int argc,char ** argv){
glutInit(&argc,argv);
glutInitWindowSize(640,480);
glutInitWindowPosition(50,50);
glutCreateWindow(“Viewport Transform”);
glutDisplayFunc(display) ;
glutReshapeFunc(reshape);
initGL();
glutMainLoop();
返回0;
} |
甲reshape()
功能,这就是所谓背面当窗口第一次出现的时候的尺寸重新调整窗口,可用于确保限幅区域和视口之间是一致的纵横比,如示于上面的示例。图形子系统将窗口的宽度和高度(以像素为单位)传递给reshape()
。
GLfloat aspect =(GLfloat)width /(GLfloat)height;
我们计算新的重新调整大小的窗口的纵横比,给定它的新图形子系统width
并将其height
提供给回调函数reshape()
。
glViewport(0,0,width,height);
我们将视口设置为覆盖整个新的重新调整大小的窗口,以像素为单位。
尝试将视口设置为仅覆盖窗口的四分之一(右下角的qradrant)glViewport(0, 0, width/2, height/2)
。
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
if(width> = height){
gluOrtho2D(-1.0 *方面,1.0 *方面,-1.0,1.0);
} else {
gluOrtho2D(-1.0,1.0,-1.0 /方面,1.0 /方面);
}
我们将裁剪区域的纵横比设置为与视口相匹配。为了设置剪辑区域,我们首先选择通过投影矩阵进行操作glMatrixMode(GL_PROJECTION)
。OpenGL有两个矩阵,一个投影矩阵(处理摄像机投影,例如设置剪辑区域)和一个模型视图矩阵(用于将物体从其本地空间转换为公共世界空间)。我们通过重置投影矩阵glLoadIdentity()
。
最后,我们调用gluOrtho2D()
设置与视口匹配的宽高比的剪贴区域。短边的范围从-1到+1,如下所示:
我们需要注册reshape()
通过与GLUT回调处理程序glutReshapeFunc()
中main()
,如下所示:
int main(int argc,char ** argv){
glutInitWindowSize(640,480);
......
glutReshapeFunc(重塑);
}
在上面的main()
函数中,我们指定初始窗口大小640x480
,这是非方形的。尝试重新调整窗口大小并观察更改。
请注意,窗口首次出现时reshape()
至少运行一次。然后每当窗口重新塑形时就会被回调。另一方面,initGL()
运行一次(也只有一次); 以及display()
响应于窗口重新绘制请求的运行(例如,在窗口重新调整大小之后)。
5.翻译和旋转
在上面的示例中,我们通过分别定义相同的原点(称为世界空间)来定义每个形状的顶点。我花了很长时间才弄清楚这些顶点的绝对坐标。
相反,我们可以通过定义它们各自的顶点(称为模型空间或局部空间)来定位每个形状。然后,我们可以使用平移和/或旋转将形状定位在世界空间中的所需位置,如下面的修改display()
函数所示。
5.1例4:平移和旋转(GL04ModelTransform.cpp)
1
2
3
4
五
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
三十
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118 |
#include
#include
void initGL(){
glClearColor(0.0f,0.0f,0.0f,1.0f);
}
void display(){
glClear(GL_COLOR_BUFFER_BIT);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(-0.5f,0.4f,0.0f);
glBegin(GL_QUADS);
glColor3f(1.0f,0.0f,0.0f);
glVertex2f(-0.3f,-0.3f);
glVertex2f(0.3f,-0.3f);
glVertex2f(0.3f,0.3f);
glVertex2f(-0.3f,0.3f);
glEnd();
glTranslatef(0.1f,-0.7f,0.0f);
glBegin(GL_QUADS);
glColor3f(0.0f,1.0f,0.0f);
glVertex2f(-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);
glBegin(GL_QUADS);
glColor3f(0.2f,0.2f,0.2f);
glVertex2f(-0.2f,-0.2f);
glColor3f(1.0f,1.0f,1.0f);
glVertex2f(0.2f,-0.2f);
glColor3f(0.2f,0.2f,0.2f);
glVertex2f(0.2f,0.2f);
glColor3f(1.0f,1.0f,1.0f);
glVertex2f(-0.2f,0.2f);
glEnd();
glTranslatef(1.1f,0.2f,0.0f);
glBegin(GL_TRIANGLES);
glColor3f(0.0f,0.0f,1.0f);
glVertex2f(-0.3f,-0.2f);
glVertex2f(0.3f,-0.2f);
glVertex2f(0.0f,0.3f);
glEnd();
glTranslatef(0.2f,-0.3f,0.0f);
glRotatef(180.0f,0.0f,0.0f,1.0f);
glBegin(GL_TRIANGLES);
glColor3f(1.0f,0.0f,0.0f);
glVertex2f(-0.3f,-0.2f);
glColor3f(0.0f,1.0f,0.0f);
glVertex2f(0.3f,-0.2f);
glColor3f(0.0f,0.0f,1.0f);
glVertex2f(0.0f,0.3f);
glEnd();
glRotatef(-180.0f,0.0f,0.0f,1.0f);
glTranslatef(-0.1f,1.0f,0.0f);
glBegin(GL_POLYGON);
glColor3f(1.0f,1.0f,0.0f);
glVertex2f(-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();
}
void reshape(GLsizei width,GLsizei height){
if(height == 0)height = 1;
GLfloat aspect =(GLfloat)width /(GLfloat)height;
glViewport(0,0,width,height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
if(width> = height){
gluOrtho2D(-1.0 *方面,1.0 *方面,-1.0,1.0);
} else {
gluOrtho2D(-1.0,1.0,-1.0 /方面,1.0 /方面);
}
}
int main(int argc,char ** argv){
glutInit(&argc,argv);
glutInitWindowSize(640,480);
glutInitWindowPosition(50,50);
glutCreateWindow(“Model Transform”);
glutDisplayFunc(display) ;
glutReshapeFunc(reshape);
initGL();
glutMainLoop();
返回0;
} |
glMatrixMode(GL_MODELVIEW);
glLoadIdentity()上操作;
平移和旋转是所谓的模型变换的一部分,它从物体从局部空间(或模型空间)转换到共同的世界空间。为了进行模型转换,我们将矩阵模式设置为模式视图矩阵(GL_MODELVIEW
)并重置矩阵。(回想一下在前面的例子中,我们将矩阵模式设置为投影矩阵(GL_PROJECTION
)来设置剪切区域。)
OpenGL是作为一个状态机运行的。也就是说,一旦设置了状态,状态的值就会一直保持到它被更改。换句话说,一旦坐标被翻译或旋转,所有后续的操作将基于这个坐标。
翻译通过glTranslate
功能完成:
void gltranslatef(GLfloat x,GLfloat y,GLfloat z)
请注意,glTranslatef
函数必须放在glBegin
/ 之外glEnd
,因为glColor
它可以放在glBegin
/ 里面glEnd
。
旋转是通过glRotatef
函数完成的:
void glRotatef(GLfloat angle,GLfloat x,GLfloat y,GLfloat z)
请注意,旋转角度在OpenGL中以度(而不是弧度)度量。
在上面的例子中,我们在xy平面内(z = 0)进行平移,并围绕z轴(与xy平面垂直)旋转。
6.动画
6.1空闲功能
要执行动画(例如,旋转形状),可以idle()
通过glutIdleFunc
命令向GLUT 注册回调处理程序。idle()
当没有其他事件需要处理时,图形系统会回调函数。
void glutIdleFunc(void(* func)(void))
在这个idle()
函数中,你可以发出glutPostRedisplay
命令来发布一个窗口重新绘制请求,而这个请求又会激活display()
函数。
void idle(){
glutPostRedisplay();
}
请注意,上述内容等同于注册display()
为idle
功能。
glutIdleFunc(显示);
6.2双缓冲
双缓冲使用两个显示缓冲区来平滑动画。下一个屏幕准备在后台缓冲区中,而当前屏幕保存在前台缓冲区中。准备工作完成后,您可以使用glutSwapBuffer
命令交换前后缓冲区。
要使用双缓冲,您需要进行两项更改:
- 在
main()
创建窗口之前包含这一行:glutInitDisplayMode(GLUT_DOUBLE);
- 在
display()
函数中,替换glFlush()
为glutSwapBuffers()
,交换前后缓冲区。
动画中应该使用双缓冲。对于静态显示,单缓冲就足够了。(许多图形硬件总是双重缓冲,所以很难看出差异。)
6.3示例5:使用空闲函数的动画(GL05IdleFunc.cpp)
以下程序使用双缓冲空闲功能旋转我们前面示例中创建的所有形状。
1
2
3
4
五
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
三十
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147 |
#include
#include
GLfloat角度= 0.0f;
void initGL(){
glClearColor(0.0f,0.0f,0.0f,1.0f);
}
void idle(){
glutPostRedisplay();
}
void display(){
glClear(GL_COLOR_BUFFER_BIT);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glPushMatrix();
glTranslatef(-0.5f,0.4f,0.0f);
glRotatef(角度,0.0f,0.0f,1.0f);
glBegin(GL_QUADS);
glColor3f(1.0f,0.0f,0.0f);
glVertex2f(-0.3f,-0.3f);
glVertex2f(0.3f,-0.3f);
glVertex2f(0.3f,0.3f);
glVertex2f(-0.3f,0.3f);
glEnd();
glPopMatrix();
glPushMatrix();
glTranslatef(-0.4f,-0.3f,0.0f);
glRotatef(角度,0.0f,0.0f,1.0f);
在glBegin(GL_QUADS);
glColor3f(0.0f,1.0f,0.0f);
glVertex2f(-0.3f,-0.3f);
glVertex2f(0.3f,-0.3f);
glVertex2f(0.3f,0.3f);
glVertex2f(-0.3f,0.3f);
glEnd();
glPopMatrix();
glPushMatrix();
glTranslatef(-0.7f,-0.5f,0.0f);
glRotatef(角度,0.0f,0.0f,1.0f);
在glBegin(GL_QUADS);
glColor3f(0.2f,0.2f,0.2f);
glVertex2f(-0.2f,-0.2f);
glColor3f(1.0f,1.0f,1.0f);
glVertex2f(0.2f,-0.2f);
glColor3f(0.2f,0.2f,0.2f);
glVertex2f(0.2f,0.2f);
glColor3f(1.0f,1.0f,1.0f);
glVertex2f(-0.2f,0.2f);
glEnd();
glPopMatrix();
glPushMatrix();
glTranslatef(0.4f,-0.3f,0.0f);
glRotatef(角度,0.0f,0.0f,1.0f);
在glBegin(GL_TRIANGLES);
glColor3f(0.0f,0.0f,1.0f);
glVertex2f(-0.3f,-0.2f);
glVertex2f(0.3f,-0.2f);
glVertex2f(0.0f,0.3f);
glEnd();
glPopMatrix();
glPushMatrix();
glTranslatef(0.6f,-0.6f,0.0f);
glRotatef(180.0f +角度,0.0f,0.0f,1.0f);
在glBegin(GL_TRIANGLES);
glColor3f(1.0f,0.0f,0.0f);
glVertex2f(-0.3f,-0.2f);
glColor3f(0.0f,1.0f,0.0f);
glVertex2f(0.3f,-0.2f);
glColor3f(0.0f,0.0f,1.0f);
glVertex2f(0.0f,0.3f);
glEnd();
glPopMatrix();
glPushMatrix();
glTranslatef(0.5f,0.4f,0.0f);
glRotatef(角度,0.0f,0.0f,1.0f);
在glBegin(GL_POLYGON);
glColor3f(1.0f,1.0f,0.0f);
glVertex2f(-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();
glutSwapBuffers();
angle + = 0.2f;
}
void reshape(GLsizei width,GLsizei height){
if(height == 0)height = 1;
GLfloat aspect =(GLfloat)width /(GLfloat)height;
glViewport(0,0,width,height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
if(width> = height){
gluOrtho2D(-1.0 *方面,1.0 *方面,-1.0,1.0);
} else {
gluOrtho2D(-1.0,1.0,-1.0 /方面,1.0 /方面);
}
}
int main(int argc,char ** argv){
glutInit(&argc,argv);
glutInitDisplayMode(GLUT_DOUBLE);
glutInitWindowSize(640,480);
glutInitWindowPosition(50,50);
glutCreateWindow(“通过空闲函数动画”);
glutDisplayFunc(display) ;
glutReshapeFunc(reshape);
glutIdleFunc(idle);
initGL();
glutMainLoop();
返回0;
} |
在上面的例子中,我们glPushMatrix
用来保存当前状态,执行转换并通过恢复保存的状态,而不是累积所有翻译并撤消旋转glPopMatrix
。(在上面的例子中,我们也可以使用glLoadIdentity
在下一次转换之前重置矩阵。)
GL浮块角度= 0.0f;
我们定义了一个全局变量,angle
用于跟踪所有形状的旋转角度。我们稍后将使用glRotatef
旋转所有形状到这个角度。
角度+ = 0.2f;
在每次刷新(in display()
)结束时,我们更新所有形状的旋转角度。
glutSwapBuffers();
glutInitDisplayMode(GLUT_DOUBLE);
而不是glFlush()
立即刷新帧缓冲器glutSwapBuffer()
以供显示,我们启用双缓冲并用于在VSync期间交换前后缓冲器以平滑显示。
void idle(){
glutPostRedisplay();
}
glutIdleFunc(空闲);
我们定义一个idle()
函数,display()
如果没有未完成的事件发布重新绘制请求并调用。我们idle()
在main()
via中注册这个函数glutIdleFunc()
。
6.4双缓冲和刷新率
启用双缓冲时,glutSwapBuffers
与屏幕刷新间隔(VSync)同步。也就是说,当显示器放置一个新的帧时,缓冲区将被同时交换。因此,idle()
功能最多可以以与显示器刷新率相同的速率刷新动画(60Hz用于LCD / LED显示器)。它可以以显示器刷新频率的一半(如果计算花费超过1个刷新间隔),三分之一,四分之一等等操作,因为它需要等待VSync。
6.5定时器功能
有了idle()
,我们无法控制刷新间隔。我们可以Timer()
通过GLUT 注册一个函数glutTimerFunc
。该Timer()
功能将以指定的固定时间间隔回叫。
void glutTimerFunc(unsigned int millis,void(* func)(int value),value)
6.6例6:通过定时器功能的动画(GL06TimerFunc.cpp)
以下修改将先前示例中创建的所有形状每30毫秒逆时针旋转2度。
1
2
3
4
五
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
三十
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149 |
#include
#include
GLfloat angle = 0.0f;
int refreshMills = 30;
void initGL(){
glClearColor(0.0f,0.0f,0.0f,1.0f);
}
void Timer(int value){
glutPostRedisplay();
glutTimerFunc(refreshMills,Timer,0);
}
void display(){
glClear(GL_COLOR_BUFFER_BIT);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glPushMatrix();
glTranslatef(-0.5f,0.4f,0.0f);
glRotatef(角度,0.0f,0.0f,1.0f);
glBegin(GL_QUADS);
glColor3f(1.0f,0.0f,0.0f);
glVertex2f(-0.3f,-0.3f);
glVertex2f(0.3f,-0.3f);
glVertex2f(0.3f,0.3f);
glVertex2f(-0.3f,0.3f);
glEnd();
glPopMatrix();
glPushMatrix();
glTranslatef(-0.4f,-0.3f,0.0f);
glRotatef(角度,0.0f,0.0f,1.0f);
在glBegin(GL_QUADS);
glColor3f(0.0f,1.0f,0.0f);
glVertex2f(-0.3f,-0.3f);
glVertex2f(0.3f,-0.3f);
glVertex2f(0.3f,0.3f);
glVertex2f(-0.3f,0.3f);
glEnd();
glPopMatrix();
glPushMatrix();
glTranslatef(-0.7f,-0.5f,0.0f);
glRotatef(角度,0.0f,0.0f,1.0f);
在glBegin(GL_QUADS);
glColor3f(0.2f,0.2f,0.2f);
glVertex2f(-0.2f,-0.2f);
glColor3f(1.0f,1.0f,1.0f);
glVertex2f(0.2f,-0.2f);
glColor3f(0.2f,0.2f,0.2f);
glVertex2f(0.2f,0.2f);
glColor3f(1.0f,1.0f,1.0f);
glVertex2f(-0.2f,0.2f);
glEnd();
glPopMatrix();
glPushMatrix();
glTranslatef(0.4f,-0.3f,0.0f);
glRotatef(角度,0.0f,0.0f,1.0f);
在glBegin(GL_TRIANGLES);
glColor3f(0.0f,0.0f,1.0f);
glVertex2f(-0.3f,-0.2f);
glVertex2f(0.3f,-0.2f);
glVertex2f(0.0f,0.3f);
glEnd();
glPopMatrix();
glPushMatrix();
glTranslatef(0.6f,-0.6f,0.0f);
glRotatef(180.0f +角度,0.0f,0.0f,1.0f);
在glBegin(GL_TRIANGLES);
glColor3f(1.0f,0.0f,0.0f);
glVertex2f(-0.3f,-0.2f);
glColor3f(0.0f,1.0f,0.0f);
glVertex2f(0.3f,-0.2f);
glColor3f(0.0f,0.0f,1.0f);
glVertex2f(0.0f,0.3f);
glEnd();
glPopMatrix();
glPushMatrix();
glTranslatef(0.5f,0.4f,0.0f);
glRotatef(角度,0.0f,0.0f,1.0f);
在glBegin(GL_POLYGON);
glColor3f(1.0f,1.0f,0.0f);
glVertex2f(-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();
glutSwapBuffers();
角度+ = 2.0f;
}
void reshape(GLsizei width,GLsizei height){
if(height == 0)height = 1;
GLfloat aspect =(GLfloat)width /(GLfloat)height;
glViewport(0,0,width,height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
if(width> = height){
gluOrtho2D(-1.0 *方面,1.0 *方面,-1.0,1.0);
} else {
gluOrtho2D(-1.0,1.0,-1.0 /方面,1.0 /方面);
}
}
int main(int argc,char ** argv){
glutInit(&argc,argv);
glutInitDisplayMode(GLUT_DOUBLE);
glutInitWindowSize(640,480);
glutInitWindowPosition(50,50);
glutCreateWindow(“通过空闲函数动画”);
glutDisplayFunc(display) ;
glutReshapeFunc(reshape);
glutTimerFunc(0,Timer,0);
initGL();
glutMainLoop();
返回0;
} |
void Timer(int value){
glutPostRedisplay();
glutTimerFunc(refreshMills,Timer,0);
}
我们用一个idle()
函数来替换函数,该timer()
函数display()
在计时器过期后发布重新绘制请求来调用。
glutTimerFunc(0,Timer,0);
在main()
,我们注册该timer()
功能,并timer()
立即激活(初始计时器= 0)。
6.7更多GLUT功能
6.8例7:弹跳球(GL07BouncingBall.cpp)
这个例子显示了一个在窗口内弹跳的球。请注意,在OpenGL中,圆不是原始的几何形状。这个例子TRIANGLE_FAN
用来组成一个圆。
1
2
3
4
五
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
三十
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122 |
#include
#include
#include < >
#define PI 3.14159265f
char title [] =“Bouncing Ball(2D)”;
int windowWidth = 640;
int windowHeight = 480;
int windowPosX = 50;
int windowPosY = 50;
GLfloat ballRadius = 0.5f;
GLfloat ballX = 0.0f;
GLfloat ballY = 0.0f;
GLfloat ballXMax,ballXMin,ballYMax,ballYMin;
GLfloat xSpeed = 0.02f;
GLfloat ySpeed = 0.007f;
int refreshMillis = 30;
GLdouble clipAreaXLeft,clipAreaXRight,clipAreaYBottom,clipAreaYTop;
void initGL(){
glClearColor(0.0,0.0,0.0,1.0);
}
void display(){
glClear(GL_COLOR_BUFFER_BIT);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(ballX,ballY,0.0f);
在glBegin(GL_TRIANGLE_FAN);
glColor3f(0.0f,0.0f,1.0f);
glVertex2f(0.0f,0.0f);
int numSegments = 100;
GLfloat角;
for(int i = 0; i <= numSegments; i ++){
角度 = i * 2.0f * PI / numSegments;
glVertex2f(cos(angle)* ballRadius,sin(angle)* ballRadius);
}
glEnd();
glutSwapBuffers();
ballX + = xSpeed;
ballY + = ySpeed;
if(ballX> ballXMax){
ballX = ballXMax;
xSpeed = -xSpeed;
} else if(ballX
ballX = ballXMin;
xSpeed = -xSpeed;
}
如果(ballY> ballYMax){
ballY = ballYMax;
ySpeed = -ySpeed;
} else if(ballY
ballY = ballYMin;
ySpeed = -ySpeed;
}
}
void reshape(GLsizei width,GLsizei height){
高 if(height == 0)height = 1;
GLfloat aspect =(GLfloat)width /(GLfloat)height;
glViewport(0,0,width,height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
if(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;
}
void Timer(int value){
glutPostRedisplay();
glutTimerFunc(refreshMillis,Timer,0);
}
int main(int argc,char ** argv){
glutInit(&argc,argv);
glutInitDisplayMode(GLUT_DOUBLE);
glutInitWindowSize(windowWidth,windowHeight);
glutInitWindowPosition(windowPosX,windowPosY);
glutCreateWindow(title);
glutDisplayFunc(display) ;
glutReshapeFunc(reshape);
重构的 glutTimerFunc(0,Timer,0);
initGL();
glutMainLoop();
返回0;
} |
[TODO]说明
7.使用GLUT处理键盘输入
我们可以注册回调函数来分别处理普通键和特殊键的键盘输入。
7.1例8:在全屏模式和窗口模式(GL08FullScreen.cpp)之间切换
对于弹跳球程序,以下特殊键处理程序使用F1键在全屏模式和窗口模式之间切换。
1
2
3
4
五
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
三十
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145 |
#include
#include
#include < >
#define PI 3.14159265f
char title [] =“全屏和窗口模式”;
int windowWidth = 640;
int windowHeight = 480;
int windowPosX = 50;
int windowPosY = 50;
GLfloat ballRadius = 0.5f;
GLfloat ballX = 0.0f;
GLfloat ballY = 0.0f;
GLfloat ballXMax,ballXMin,ballYMax,ballYMin;
GLfloat xSpeed = 0.02f;
GLfloat ySpeed = 0.007f;
int refreshMillis = 30;
GLdouble clipAreaXLeft,clipAreaXRight,clipAreaYBottom,clipAreaYTop;
bool fullScreenMode = true;
void initGL(){
glClearColor(0.0,0.0,0.0,1.0);
}
void display(){
glClear(GL_COLOR_BUFFER_BIT);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(ballX,ballY,0.0f);
在glBegin(GL_TRIANGLE_FAN);
glColor3f(0.0f,0.0f,1.0f);
glVertex2f(0.0f,0.0f);
int numSegments = 100;
GLfloat角;
for(int i = 0; i <= numSegments; i ++){
角度 = i * 2.0f * PI / numSegments;
glVertex2f(cos(angle)* ballRadius,sin(angle)* ballRadius);
}
glEnd();
glutSwapBuffers();
ballX + = xSpeed;
ballY + = ySpeed;
if(ballX> ballXMax){
ballX = ballXMax;
xSpeed = -xSpeed;
} else if(ballX
ballX = ballXMin;
xSpeed = -xSpeed;
}
如果(ballY> ballYMax){
ballY = ballYMax;
ySpeed = -ySpeed;
} else if(ballY
ballY = ballYMin;
ySpeed = -ySpeed;
}
}
void reshape(GLsizei width,GLsizei height){
高 if(height == 0)height = 1;
GLfloat aspect =(GLfloat)width /(GLfloat)height;
glViewport(0,0,width,height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
if(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;
}
void Timer(int value){
glutPostRedisplay();
glutTimerFunc(refreshMillis,Timer,0);
}
void specialKeys(int key,int x,int y){
开关(键){
case GLUT_KEY_F1:
fullScreenMode =!fullScreenMode;
if(fullScreenMode){
windowPosX = glutGet(GLUT_WINDOW_X);
windowPosY = glutGet(GLUT_WINDOW_Y);
windowWidth = glutGet(GLUT_WINDOW_WIDTH);
windowHeight = glutGet(GLUT_WINDOW_HEIGHT);
glutFullScreen();
} else {
glutReshapeWindow(windowWidth,windowHeight);
glutPositionWindow(windowPosX,windowPosX);
}
打破;
}
}
int main(int argc,char ** argv){
glutInit(&argc,argv);
glutInitDisplayMode(GLUT_DOUBLE);
glutInitWindowSize(windowWidth,windowHeight);
glutInitWindowPosition(windowPosX,windowPosY);
glutCreateWindow(title);
glutDisplayFunc(display) ;
glutReshapeFunc(reshape);
重构的 glutTimerFunc(0,Timer,0);
glutSpecialFunc(specialKeys);
glutFullScreen();
initGL();
glutMainLoop();
返回0;
} |
[TODO]说明
[TODO]使用glVertex
绘制圆是低效的(由于计算密集型sin()
和cos()
函数)。尝试使用GLU的二次曲面。
7.2示例9:密钥控制(GL09KeyControl.cpp)
对于弹跳球程序,以下按键和特殊键处理程序通过ESC(27)提供出口,使用上/下箭头键增加/减少速度,使用左/右箭头键增加/减少x速度,使用PageUp / PageDown键增加/减少球的半径。
1
2
3
4
五
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
三十
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177 |
#include
#include
#include < >
#define PI 3.14159265f
char title [] =“全屏和窗口模式”;
int windowWidth = 640;
int windowHeight = 480;
int windowPosX = 50;
int windowPosY = 50;
GLfloat ballRadius = 0.5f;
GLfloat ballX = 0.0f;
GLfloat ballY = 0.0f;
GLfloat ballXMax,ballXMin,ballYMax,ballYMin;
GLfloat xSpeed = 0.02f;
GLfloat ySpeed = 0.007f;
int refreshMillis = 30;
GLdouble clipAreaXLeft,clipAreaXRight,clipAreaYBottom,clipAreaYTop;
bool fullScreenMode = true;
void initGL(){
glClearColor(0.0,0.0,0.0,1.0);
}
void display(){
glClear(GL_COLOR_BUFFER_BIT);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(ballX,ballY,0.0f);
在glBegin(GL_TRIANGLE_FAN);
glColor3f(0.0f,0.0f,1.0f);
glVertex2f(0.0f,0.0f);
int numSegments = 100;
GLfloat角;
for(int i = 0; i <= numSegments; i ++){
角度 = i * 2.0f * PI / numSegments;
glVertex2f(cos(angle)* ballRadius,sin(angle)* ballRadius);
}
glEnd();
glutSwapBuffers();
ballX + = xSpeed;
ballY + = ySpeed;
if(ballX> ballXMax){
ballX = ballXMax;
xSpeed = -xSpeed;
} else if(ballX
ballX = ballXMin;
xSpeed = -xSpeed;
}
如果(ballY> ballYMax){
ballY = ballYMax;
ySpeed = -ySpeed;
} else if(ballY
ballY = ballYMin;
ySpeed = -ySpeed;
}
}
void reshape(GLsizei width,GLsizei height){
高 if(height == 0)height = 1;
GLfloat aspect =(GLfloat)width /(GLfloat)height;
glViewport(0,0,width,height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
if(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;
}
void Timer(int value){
glutPostRedisplay();
glutTimerFunc(refreshMillis,Timer,0);
}
void keyboard(unsigned char key,int x,int y){
开关(键){
情况27:
出口(0);
打破;
}
}
void specialKeys(int key,int x,int y){
开关(键){
case GLUT_KEY_F1:
fullScreenMode =!fullScreenMode;
if(fullScreenMode){
windowPosX = glutGet(GLUT_WINDOW_X);
windowPosY = glutGet(GLUT_WINDOW_Y);
windowWidth = glutGet(GLUT_WINDOW_WIDTH);
windowHeight = glutGet(GLUT_WINDOW_HEIGHT);
glutFullScreen();
} else {
glutReshapeWindow(windowWidth,windowHeight);
glutPositionWindow(windowPosX,windowPosX);
}
打破;
case GLUT_KEY_RIGHT:
xSpeed * = 1.05f; 打破;
case GLUT_KEY_LEFT:
xSpeed * = 0.95f; 打破;
GLUT_KEY_UP:
ySpeed * = 1.05f; 打破;
GLUT_KEY_DOWN:
ySpeed * = 0.95f; 打破;
GLUT_KEY_PAGE_UP:
ballRadius * = 1.05f;
ballXMin = clipAreaXLeft + ballRadius;
ballXMax = clipAreaXRight - ballRadius;
ballYMin = clipAreaYBottom + ballRadius;
ballYMax = clipAreaYTop - ballRadius;
打破;
GLUT_KEY_PAGE_DOWN:
ballRadius * = 0.95f;
ballXMin = clipAreaXLeft + ballRadius;
ballXMax = clipAreaXRight - ballRadius;
ballYMin = clipAreaYBottom + ballRadius;
ballYMax = clipAreaYTop - ballRadius;
打破;
}
}
int main(int argc,char ** argv){
glutInit(&argc,argv);
glutInitDisplayMode(GLUT_DOUBLE);
glutInitWindowSize(windowWidth,windowHeight);
glutInitWindowPosition(windowPosX,windowPosY);
glutCreateWindow(title);
glutDisplayFunc(display) ;
glutReshapeFunc(reshape);
重构的 glutTimerFunc(0,Timer,0);
glutSpecialFunc(specialKeys);
glutKeyboardFunc(keyboard); glutFullScreen();
initGL();
glutMainLoop();
返回0;
} |
[TODO]说明
8.使用GLUT处理鼠标输入
同样,我们可以注册回调函数来处理鼠标点击和鼠标移动。
8.1实施例10:小鼠控制的(GL10MouseControl.cpp)
对于弹跳球程序,以下鼠标处理程序通过鼠标左键点击暂停移动,然后通过鼠标右键点击恢复。
1
2
3
4
五
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
三十
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196 |
#include
#include
#include < >
#define PI 3.14159265f
char title [] =“全屏和窗口模式”;
int windowWidth = 640;
int windowHeight = 480;
int windowPosX = 50;
int windowPosY = 50;
GLfloat ballRadius = 0.5f;
GLfloat ballX = 0.0f;
GLfloat ballY = 0.0f;
GLfloat ballXMax,ballXMin,ballYMax,ballYMin;
GLfloat xSpeed = 0.02f;
GLfloat ySpeed = 0.007f;
int refreshMillis = 30;
GLdouble clipAreaXLeft,clipAreaXRight,clipAreaYBottom,clipAreaYTop;
bool fullScreenMode = true;
bool paused = false;
GLfloat xSpeedSaved,ySpeedSaved;
void initGL(){
glClearColor(0.0,0.0,0.0,1.0);
}
void display(){
glClear(GL_COLOR_BUFFER_BIT);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(ballX,ballY,0.0f);
在glBegin(GL_TRIANGLE_FAN);
glColor3f(0.0f,0.0f,1.0f);
glVertex2f(0.0f,0.0f);
int numSegments = 100;
GLfloat角;
for(int i = 0; i <= numSegments; i ++){
角度 = i * 2.0f * PI / numSegments;
glVertex2f(cos(angle)* ballRadius,sin(angle)* ballRadius);
}
glEnd();
glutSwapBuffers();
ballX + = xSpeed;
ballY + = ySpeed;
if(ballX> ballXMax){
ballX = ballXMax;
xSpeed = -xSpeed;
} else if(ballX
ballX = ballXMin;
xSpeed = -xSpeed;
}
如果(ballY> ballYMax){
ballY = ballYMax;
ySpeed = -ySpeed;
} else if(ballY
ballY = ballYMin;
ySpeed = -ySpeed;
}
}
void reshape(GLsizei width,GLsizei height){
高 if(height == 0)height = 1;
GLfloat aspect =(GLfloat)width /(GLfloat)height;
glViewport(0,0,width,height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
if(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;
}
void Timer(int value){
glutPostRedisplay();
glutTimerFunc(refreshMillis,Timer,0);
}
void keyboard(unsigned char key,int x,int y){
开关(键){
情况27:
出口(0);
打破;
}
}
void specialKeys(int key,int x,int y){
开关(键){
case GLUT_KEY_F1:
fullScreenMode =!fullScreenMode;
if(fullScreenMode){
windowPosX = glutGet(GLUT_WINDOW_X);
windowPosY = glutGet(GLUT_WINDOW_Y);
windowWidth = glutGet(GLUT_WINDOW_WIDTH);
windowHeight = glutGet(GLUT_WINDOW_HEIGHT);
glutFullScreen();
} else {
glutReshapeWindow(windowWidth,windowHeight);
glutPositionWindow(windowPosX,windowPosX);
}
打破;
case GLUT_KEY_RIGHT:
xSpeed * = 1.05f; 打破;
case GLUT_KEY_LEFT:
xSpeed * = 0.95f; 打破;
GLUT_KEY_UP:
ySpeed * = 1.05f; 打破;
GLUT_KEY_DOWN:
ySpeed * = 0.95f; 打破;
GLUT_KEY_PAGE_UP:
ballRadius * = 1.05f;
ballXMin = clipAreaXLeft + ballRadius;
ballXMax = clipAreaXRight - ballRadius;
ballYMin = clipAreaYBottom + ballRadius;
ballYMax = clipAreaYTop - ballRadius;
打破;
GLUT_KEY_PAGE_DOWN:
ballRadius * = 0.95f;
ballXMin = clipAreaXLeft + ballRadius;
ballXMax = clipAreaXRight - ballRadius;
ballYMin = clipAreaYBottom + ballRadius;
ballYMax = clipAreaYTop - ballRadius;
打破;
}
}
void mouse(int button,int state,int x,int y){
if(button == GLUT_LEFT_BUTTON && state == GLUT_DOWN){
暂停=!暂停;
如果(暂停){
xSpeedSaved = xSpeed;
ySpeedSaved = ySpeed;
xSpeed = 0;
ySpeed = 0;
} else {
xSpeed = xSpeedSaved;
ySpeed = ySpeedSaved;
}
}
}
int main(int argc,char ** argv){
glutInit(&argc,argv);
glutInitDisplayMode(GLUT_DOUBLE);
glutInitWindowSize(windowWidth,windowHeight);
glutInitWindowPosition(windowPosX,windowPosY);
glutCreateWindow(title);
glutDisplayFunc(display) ;
glutReshapeFunc(reshape);
重构的 glutTimerFunc(0,Timer,0);
glutSpecialFunc(specialKeys);
glutKeyboardFunc(keyboard);
glutFullScreen();
glutMouseFunc(鼠标);
initGL();
glutMainLoop();
返回0;
} |
[TODO]说明
8.2例11:一个简单的画图程序
[TODO]使用鼠标移动和GL_LINE_STRIP
。
链接到OpenGL /计算机图形参考和资源