0%

由茶壶引发出来的OpenGL一系列问题

调试一个绘制茶壶程序,可是结果什么显示也没有。究竟忽略了哪些还不清楚的问题呢?在此,我来做一个学习笔记。


检查代码

首先在一系列检查下,发现问题可能就出在display()函数和myReshape()函数下。先来看下原程序代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
void display(void)
{
//设置清除屏幕的颜色,并清除屏幕和深度缓冲
glClearColor(0.0f,0.0f,0.0f,0.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

//设置成模型矩阵模式
glMatrixMode(GL_MODELVIEW);

//载入单位化矩阵
glLoadIdentity();
glRotatef(G_fAngle_horizon, 0.0f, 1.0f, 0.0f);
glRotatef(G_fAngle_vertical, 1.0f, 0.0f, 0.0f);

//绘制物体

//画一个正方形面
glColor3f(1.0f, 0.0f, 0.0f);
glBegin(GL_QUADS);
glVertex3f (-1.0, -1.0f, 0.0f);
glVertex3f (1.0, -1.0f, 0.0f);
glVertex3f (1.0, 1.0f, 0.0f);
glVertex3f (-1.0, 1.0f, 0.0f);
glEnd();

//画一个茶壶
glColor3f(0.0f, 1.0f, 0.0f);
glutWireTeapot(1.0);

//交换前后缓冲区
glutSwapBuffers();
}
1
2
3
4
5
6
7
8
9
10
11
//窗口大小变化时的回调函数
void myReshape(GLsizei w, GLsizei h)
{
//设定视区
glViewport(0, 0, w, h);

//设定透视方式
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glFrustum (-1.0, 1.0, -1.0, 1.0, 10.0, 30.0);
}

由于之前的学习,我知道一定要把摄像机往屏幕外移动,这样才能看到坐标中心,否则绘制出来时显示的是在茶壶内部。

因此要在display()添加代码,-G_fDistance是定义的一段距离。

1
2
//坐标中心向Z轴平移-G_fDistance (使坐标中心位于摄像机前方)
glTranslatef(0.0, 0.0, -G_fDistance);

此外,作为一个初学者,我先是不太清楚glColor3fglColor4f的区别。其次,对于myReshape()函数中的透视方式不太清楚。所以来看看是不是这其中有什么问题。


glColor

发送颜色数据

转到定义

1
glColor3f (GLfloat red, GLfloat green, GLfloat blue);
1
glColor4f (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha);

参数

red , green , blue 指定当前新的红、绿、蓝的颜色值

alpha 透明度,指明当前新的 alpha 颜色值 ,只有在 glColor4 函数中带4个变量时才会指定此参数。


投影变换函数

投影变换:给定视点和视线方向,计算当前顶点的投影点坐标。

调用投影变换函数的步骤

先要理解OpenGL中的变换都是对矩阵的操作。即是当前的矩阵乘上操作对应的一个矩阵。

乘以变换矩阵,设置模型的位置。

乘以投影矩阵,设置模型的投影方式。

乘以纹理矩阵,设置模型的纹理方式。

而用来指定乘以什么类型的矩阵, 就是glMatriMode,有3种模式:

  • GL_PROJECTION 投影

  • GL_MODELVIEW 模型视图

  • GL_TEXTURE 纹理

所以调用投影矩阵的一系列操作即为

1
2
3
4
5
glMatrixMode(GL_PROJECTION); //将当前矩阵指定为投影矩阵

glLoadIdentity(); //把矩阵设为单位矩阵:

调用glFrustum()或gluPerspective(),它们生成的矩阵会与当前的矩阵相乘,生成透视的效果;

三个常用投影函数

投影变换函数

  • glFrustum(),
1
void glFrustum(GLdouble left, GLdouble Right, GLdouble bottom, GLdouble top, GLdouble near, GLdouble far);
  • gluPerspective()
1
void gluPerspective(GLdouble fovy,GLdouble aspect,GLdouble zNear, GLdouble zFar);
  • glOrtho()
1
glOrtho(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble near, GLdouble far)

显示不出的原因

在使坐标中心位于摄像机前方之后发现还是显示不出来,我想肯定是投影出了问题,研究了glFrustum这个函数。

原代码:

1
glFrustum(-1.0, 1.0, -1.0, 1.0, 10.0, 30.0);

其left、right、bottom、top参数正常,会不会是near和far的参数出了问题?

我的理解是由于near和far值表示为离视点的远近,当near值太大时便显示不出。

这里我去调试,修改了near的值为2之后发现可成功运行,大小相对显示的窗口也合适。


最终代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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
#include "stdafx.h"

//需要包含的头文件
#include <windows.h>
#include <GL/freeglut.h>

//定义输出窗口的大小
#define WINDOW_HEIGHT 300
#define WINDOW_WIDTH 500

//摄像机离物体的距离
float G_fDistance = 3.6f;
//物体的旋转角度
float G_fAngle_horizon = 0.0;
float G_fAngle_vertical = 0.0f;

////////////////////////////////////////////////
void myinit(void);
void myReshape(GLsizei w, GLsizei h);
void display(void);

//响应键盘输入, 从而设定物体移近移远以及旋转的回调函数
void processSpecialKeys(int key, int x, int y);
void processNormalKeys(unsigned char key,int x,int y);


////////////////////////////////////////////////
//主函数
int main(int argc, char* argv[])
{
glutInit(&argc, argv);

//初始化OPENGL显示方式
glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGBA);

//设定OPENGL窗口位置和大小
glutInitWindowSize (500, 500);
glutInitWindowPosition (100, 100);

//打开窗口
glutCreateWindow ("作业3");

//调用初始化函数
myinit();

//设定窗口大小变化的回调函数
glutReshapeFunc(myReshape);

//设定键盘控制的回调函数
glutSpecialFunc(processSpecialKeys);
glutKeyboardFunc(processNormalKeys);

//开始OPENGL的循环
glutDisplayFunc(display);
glutIdleFunc(display);

glutMainLoop();

return 0;
}

////////////////////////////////////////////////
//用户初始化函数
void myinit(void)
{
//your initialization code
glEnable(GL_DEPTH_TEST);
}

//窗口大小变化时的回调函数
void myReshape(GLsizei w, GLsizei h)
{
//设定视区
glViewport(0, 0, w, h);

//设定透视方式
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glFrustum(-1.0, 1.0, -1.0, 1.0, 2.0, 30.0);
//gluPerspective(60.0, 1.0 * (GLfloat)w / (GLfloat)h, 1.0, 30.0); //保持投影窗口与视口比例一致
}

//每桢OpenGL都会调用这个函数,用户应该把显示代码放在这个函数中
void display(void)
{
//设置清除屏幕的颜色,并清除屏幕和深度缓冲
glClearColor(0.0f,0.0f,0.0f,0.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

//设置成模型矩阵模式
glMatrixMode(GL_MODELVIEW);

//载入单位化矩阵
glLoadIdentity();

glTranslatef(0.0, 0.0, -G_fDistance);
glRotatef(G_fAngle_horizon, 0.0f, 1.0f, 0.0f);
glRotatef(G_fAngle_vertical, 1.0f, 0.0f, 0.0f);

////////////////////////////////////////////////
//绘制物体

//画一个正方形面
glColor3f(1.0f, 0.0f, 0.0f);
//glColor4f(1.0f, 0.0f, 0.0f, 0.0f);
glBegin(GL_QUADS);
glVertex3f (-1.0, -1.0f, 0.0f);
glVertex3f (1.0, -1.0f, 0.0f);
glVertex3f (1.0, 1.0f, 0.0f);
glVertex3f (-1.0, 1.0f, 0.0f);
glEnd();

//画一个茶壶
glColor3f(0.0f, 1.0f, 0.0f);
//glColor4f(0.0f, 1.0f, 0.0f, 1.0f);
glutWireTeapot(1.0);
// glutSolidTeapot(1.0);


//交换前后缓冲区
glutSwapBuffers();
}


void processSpecialKeys(int key, int x, int y)
{
switch(key) {
case GLUT_KEY_LEFT:
G_fAngle_horizon -= 5.0f;
break;
case GLUT_KEY_RIGHT:
G_fAngle_horizon += 5.0f;
break;
case GLUT_KEY_UP:
G_fAngle_vertical -= 5.0f;
break;
case GLUT_KEY_DOWN:
G_fAngle_vertical += 5.0f;
break;
}
}

void processNormalKeys(unsigned char key,int x,int y)
{
switch(key) {
case 97: //"a"
G_fDistance -= 0.3f;
break;
case 65: //"A"
G_fDistance += 0.3f;
break;
case 27: //"esc"
exit(0);
}
}

参考

【转】OpenGL 入门纪录–2 .透视函数glFrustum(), gluPerspective