实验环境:win10、VS 2017、OPenGL库

实验一 基于OpenGL的二维图形绘制

要求

编程实现绘制一个五角星(基于OpenGL),效果如下图所示

问题分析

可以将一个五角星划分为10个三角形,假设五角星的各边长,分别计算出10个定点的坐标,然后逐个绘制三角形,将其拼接为五角星;

也可以利用参数方程,如下图所示,设大圆和小圆的半径分别为R、r,已知夹角为37度,在循环中计算绘制三角形。

代码

ProjectOne.cpp

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
#include <GL/glew.h>//OpenGL库
#include <GL/glut.h>//OpenGL辅助库
#include <math.h>
#pragma comment(linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"") //这句是不让控制台窗体出现,如果想要出现,去掉即可。

// 明确 计算三角函数 需要 theta * pi / 180
// 由于 两个三角形的theta角有36度的差值
// 点的坐标为 x = x0 + r(R) * sin(cos) theta
// 对于大圆 theta 为( 0 + 72 * i )* pi/180 == 0.4i * pi
// 小圆theta 为 (36 + 72 * i) * pi / 180 == 0.2*pi + 0.4i * pi
// 两者相差只有 0.2 * pi


void init(void)
{
glClearColor(1.0, 1.0, 1.0, 1.0);//设置背景色白色
}


void DrawStar1(float cx, float cy, float R, float r, float o)//五角星中心坐标x,y,大圆半径,小圆半径,初始角度
{
float x0, y0, x1, y1;//
float Theta = o;//大圆对应角度
float theta = o + 0.2 * 3.14;//小圆对应角度
for (int i = 0; i < 10; i++)
{
x0 = cx + R * cos(Theta);//大圆对应的x坐标
y0 = cy + R * sin(Theta);//大圆对应y坐标

x1 = cx + r * cos(theta);//小圆对应x坐标
y1 = cy + r * sin(theta);//小圆对应y坐标
glBegin(GL_LINES);//绘制
glColor3f(1, 0, 0);
glVertex2f(x0, y0);
glVertex2f(x1, y1);
glEnd();
if (i % 2 == 0)
{
Theta = Theta + 0.4 * 3.14;//大圆对应角度变换
}
else
{
theta = theta + 0.4 * 3.14;//小圆对应角度变换
}
}
}

void DrawStar2(float cx, float cy, float R, float r, float o)//五角星中心坐标x,y,大圆半径,小圆半径,初始角度
{
float x0, y0, x1, y1;//
float Theta = o;//大圆对应角度
float theta = o + 0.2 * 3.14;//小圆对应角度
for (int i = 0; i < 10; i++)
{
x0 = cx + R * cos(Theta);//大圆对应的x坐标
y0 = cy + R * sin(Theta);//大圆对应y坐标
x1 = cx + r * cos(theta);//小圆对应x坐标
y1 = cy + r * sin(theta);//小圆对应y坐标
glColor3f(1, 0, 0);
glBegin(GL_POLYGON);//绘制
glVertex2f(x0, y0);
glVertex2f(x1, y1);
glVertex2f(cx, cy);
glEnd();
if (i % 2 == 0)
{
Theta = Theta + 0.4 * 3.14;//大圆对应角度变换
}
else
{
theta = theta + 0.4 * 3.14;//小圆对应角度变换
}
}
}

void DrawStar3(float cx, float cy, float R, float r, float o)//五角星中心坐标x,y,大圆半径,小圆半径,初始角度
{
float x0, y0, x1, y1;
float Theta = o; //大圆对应角度
float theta = o + 0.2 * 3.14; //小圆对应角度
for (int i = 0; i < 10; i++)
{
x0 = cx + R * cos(Theta);//大圆对应的x坐标
y0 = cy + R * sin(Theta);//大圆对应y坐标
x1 = cx + r * cos(theta);//小圆对应x坐标
y1 = cy + r * sin(theta);//小圆对应y坐标
if (i % 2 == 0)
{
glColor3f(1, 0, 0);
}
else
{
glColor3f(0, 1, 1);
}
glBegin(GL_POLYGON);//绘制
glVertex2f(x0, y0);
glVertex2f(x1, y1);
glVertex2f(cx, cy);
glEnd();
if (i % 2 == 0)
{
Theta = Theta + 0.4 * 3.14; //大圆对应角度变换
}
else
{
theta = theta + 0.4 * 3.14; //小圆对应角度变换
}
}
}
void display(void)
{
glClear(GL_COLOR_BUFFER_BIT); //清除帧缓存
DrawStar1(150, 300, 120, 50, 0); //glFlush();//单缓冲时必须要,说明绘图命令(函数)结束
DrawStar2(400, 300, 120, 50, 0);
DrawStar3(650, 300, 120, 50, 0);
glutSwapBuffers();//交换缓冲(双缓冲时使用)
}

void reshape(int width, int height)
{
glViewport(0, 0, width, height);//设置视区(窗口内绘图区域)
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0, width, 0, height);//设置图形数据范围
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
void keyboard(unsigned char key, int x, int y)
{
}
int main(int argc, char* argv[])
{
glutInitWindowPosition(200, 200);//应用程序窗口位置
glutInitWindowSize(800, 600);//窗口大小
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);//双缓冲,单缓冲为GLUT_SINGLE
glutCreateWindow("五角星绘制");//创建窗口,参数为窗口标题
init();
glutDisplayFunc(display);//图形绘制
glutReshapeFunc(reshape);//窗口大小变化
glutKeyboardFunc(keyboard);//键盘交互
glutMainLoop();//必须,进入GLUT事件处理循环
return 0;
}

效果展示

实验二 二维图形的几何变换

要求

  1. 实现一个五角星以任意角度在矩形四条边内部滚动,与边界碰撞时发生的反弹,参考台球在桌案上的滚动效果。
  2. 实现矩形框内一个五角星的连续放缩(大小变化)
  3. 注意:两个五角星在同一矩形内;放缩五角星的参照点为五角星的中心,五角星位置固定;滚动五角星的旋转角度和平移距离尽量一致。

问题分析

  1. 将滚动五角星看做半径为R的圆,滚动五角星的碰撞条件判断为:它的圆心到矩形边界的距离是否大于半径R。图示如下

    红色矩形所框选的范围即为滚动五角星圆心所运动的区域。
  2. 旋转五角星只需要不停地调用放缩函数即可。

代码

ProjectTwo.cpp

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
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
// OpenGLOld.cpp : 定义控制台应用程序的入口点。
//
#include <GL/glew.h>//OpenGL库
#include <GL/glut.h>//OpenGL辅助库
#include <math.h>
#pragma comment( linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"" ) //这句是不让控制台窗体出现,如果想要出现,去掉即可。

void init(void)
{
glClearColor(0.2, 0.3, 0.3, 1.0);//设置背景色
}

float alpha=0.0f;
float x = 0.0f, y = 0.0f;
double Scale = 0;

void DrawStar3(float cx, float cy, float R, float r, float o)//五角星中心坐标x,y,大圆半径,小圆半径,初始角度
{
float x0, y0, x1, y1;
float Theta = o; //大圆对应角度
float theta = o + 0.2 * 3.14; //小圆对应角度
for (int i = 0; i < 10; i++)
{
x0 = cx + R * cos(Theta);//大圆对应的x坐标
y0 = cy + R * sin(Theta);//大圆对应y坐标
x1 = cx + r * cos(theta);//小圆对应x坐标
y1 = cy + r * sin(theta);//小圆对应y坐标
if (i % 2 == 0)
{
glColor3f(1, 0, 0);
}
else
{
glColor3f(0, 1, 1);
}
glBegin(GL_POLYGON);//绘制
glVertex2f(x0, y0);
glVertex2f(x1, y1);
glVertex2f(cx, cy);
glEnd();
if (i % 2 == 0)
{
Theta = Theta + 0.4 * 3.14; //大圆对应角度变换
}
else
{
theta = theta + 0.4 * 3.14; //小圆对应角度变换
}
}
}

void MyDrawRect()
{
glBegin(GL_LINE_LOOP);
glVertex2f(0, 0);
glVertex2f(0, 700);
glVertex2f(1000, 700);
glVertex2f(1000, 0);
glEnd();

}

void display(void)
{
glClear(GL_COLOR_BUFFER_BIT);//清除帧缓存

//模型观察矩阵初始化
glMatrixMode(GL_MODELVIEW);

// 旋转与碰撞
glPushMatrix();
glTranslatef(150.0f+x,300.0f+y,0.0f);
glRotatef(alpha, 0, 0, 1);
glTranslatef(-150.0f-x, -300.0f-y, 0.0f);

DrawStar3(150+x, 300+y, 100, 40, 0);
//glLoadIdentity();
glPopMatrix();

//缩放
glPushMatrix();
glTranslatef(300.0, 200.0, 0.0);
glScalef(Scale, Scale, 1);
glTranslatef(-300.0, -200.0, 0.0);

DrawStar3(300, 200, 100, 40, 0.1*3.14);
//glLoadIdentity();
glPopMatrix();

MyDrawRect();

//glFlush();//单缓冲时必须要,说明绘图命令(函数)结束
glutSwapBuffers();//交换缓冲(双缓冲时使用)
}

void reshape(int width, int height)
{
glViewport(0, 0, width, height);//设置视区(窗口内绘图区域)

glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0, width, 0, height);//设置图形数据范围
// glMatrixMode(GL_MODELVIEW);
// glLoadIdentity();
}

int flag = 0, flagX = 0, flagY = 0;


void change()
{
alpha += 0.2f;
if (alpha > 360) alpha -= 360;

// 放缩判断
if (flag == 0) {
Scale += 0.002;
if (Scale > 2.5) {
flag = 1;
}
} else if (flag == 1) {
Scale -= 0.002;
if (Scale < 0.2) {
flag = 0;
}
}

// X取值范围判断 (x1, x2)
// x1 = x - R
// x2 = x + R
if (flagX == 0) {
x += 0.2f;
if (x + 150 >= 900) {
flagX = 1;
}
}
else if (flagX == 1) {
x -= 0.2f;
if (x + 150 <= 100) {
flagX = 0;
}
}

// Y值取值范围判断 (y1, y2)
// y1 = y - R
// y2 = y + R

if (flagY == 0) {
y += 0.2f;
if (y + 300 >= 600) {
flagY = 1;
}
}else if (flagY == 1) {
y -= 0.2f;
if (y + 300 <= 100) {
flagY = 0;
}
}

glutPostRedisplay();
}
void keyboard(unsigned char key, int x, int y)
{

}

int main(int argc, char* argv[])
{
glutInitWindowPosition(200, 200);//应用程序窗口位置
glutInitWindowSize(800, 600);//窗口大小

glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);//双缓冲,单缓冲为GLUT_SINGLE
glutCreateWindow("Project Two");//创建窗口,参数为窗口标题
init();

glutDisplayFunc(display);//图形绘制
glutReshapeFunc(reshape);//窗口大小变化
glutKeyboardFunc(keyboard);//键盘交互
glutIdleFunc(change);//键盘交互

glutMainLoop();//必须,进入GLUT事件处理循环

return 0;
}

效果展示

实验三 & 实验四

要求

  1. 构建一个丰富的三维场景,包括立方体、球体、圆柱体;
  2. 为它们添加合适的纹理;
  3. 在场景中添加光源;
  4. 可以使用按键控制你视角的移动及光源的移动。

代码

ProjectFour.cpp

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
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
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
// OpenGLOld.cpp : 定义控制台应用程序的入口点。
//
#include <Windows.h>
#include <GL/glew.h>//OpenGL库
#include <GL/glut.h>//OpenGL辅助库

#include <math.h>
#include "SOIL/SOIL.h"

#include "cgSphere.h"
#include "cgCylinder.h"
#include "cgCube.h"
#include "cgQuad.h"

#include "Bitmap.h"
#pragma comment( linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"" ) //这句是不让控制台窗体出现,如果想要出现,去掉即可。

float alpha=0.0f;
float pos[]={0.0,1.5,0.0};
float headdir[]={0.0f,0.0f,-1.0f};
float rightdir[]={1.0f,0.0f,0.0f};

float step = 0.10f;
float beta = 180.0f;//与z轴正向夹角
float betastep = 1.0f;

cgSphere sphere, sun;
cgCylinder cylinder;
cgCube cube;

//光照变量
GLfloat light_position[] = { 0.0, 2.0, 1.0, 0.1 };
GLfloat light_color[] = { 1.0, 0.0, 0.0, 0.0 };
// 添加的光照
GLfloat sun_position[] = { -10.0, 5.0, 0.0, 0.0 };
GLfloat sun_color[] = { 1.0, 1.0, 1.0, 1.0 };

GLfloat aMaterial[] = { .20, 0.20, 0.20, 1.0 }; //环境光反射系数
GLfloat dMaterial[] = { .60, 0.60, 0.60, 1.0 }; //漫反射光反射系数
GLfloat sMaterial[] = { 0.4, 0.4,0.4, 1 }; //镜面反射光反射系数
GLfloat shiniess = 20; //高光指数

int rendermode = 2;//0:填充; 1:线框;2:纹理

GLuint textureID;
int frames=0;

GLuint LoadTexture(char* fname)
{
GLuint tID;

int width, height, nrComponents;
unsigned char *data;

data = SOIL_load_image(fname, &width, &height, &nrComponents,SOIL_LOAD_RGB);//

if (data)
{
GLenum internalFormat;
GLenum dataFormat;
bool gammaCorrection = false;
if (nrComponents == 1)
{
internalFormat = dataFormat = GL_RED;
}
else if (nrComponents == 3)
{
internalFormat = gammaCorrection ? GL_SRGB : GL_RGB;
dataFormat = GL_RGB;
}
else if (nrComponents == 4)
{
internalFormat = gammaCorrection ? GL_SRGB_ALPHA : GL_RGBA;
dataFormat = GL_RGBA;
}

//生成纹理
glGenTextures(1, &tID);
glBindTexture(GL_TEXTURE_2D, tID);

//glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);//这个函数居然不能用
gluBuild2DMipmaps(GL_TEXTURE_2D, internalFormat, width, height, dataFormat, GL_UNSIGNED_BYTE, data);

//glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);

glBindTexture(GL_TEXTURE_2D, 0);

SOIL_free_image_data(data);

return tID;
}

return -1;
}

void init(void)
{
glClearColor(0.5, 1.0, 1.0, 1.0);//设置背景色白色
glEnable(GL_DEPTH_TEST);

/*GL_AMBIENT、GL_DIFFUSE、GL_SPECULAR属性。这三个属性与光源的三个对应属性类似,每一属性都由四个值组成。
GL_AMBIENT表示各种光线照射到该材质上,经过很多次反射后最终遗留在环境中的光线强度(颜色)。
GL_DIFFUSE表示光线照射到该材质上,经过漫反射后形成的光线强度(颜色)。
GL_SPECULAR表示光线照射到该材质上,经过镜面反射后形成的光线强度(颜色)。
通常,GL_AMBIENT和GL_DIFFUSE都取相同的值,可以达到比较真实的效果。
使用GL_AMBIENT_AND_DIFFUSE可以同时设置GL_AMBIENT和GL_DIFFUSE属性。*/

//光照
glLightfv(GL_LIGHT0, GL_POSITION, light_position);

glLightfv(GL_LIGHT0, GL_AMBIENT, light_color);
glLightfv(GL_LIGHT0, GL_DIFFUSE, light_color);
glLightfv(GL_LIGHT0, GL_SPECULAR, light_color);

glLightfv(GL_LIGHT1, GL_POSITION, sun_position);

glLightfv(GL_LIGHT1, GL_AMBIENT, sun_color);
glLightfv(GL_LIGHT1, GL_DIFFUSE, sun_color);
glLightfv(GL_LIGHT1, GL_SPECULAR, sun_color);

// void glMaterial{if}(GLenum face, GLenum pname, TYPE param);
// 参数pname的可能取值。
// GL_AMBIENT_AND_DIFFUSE让您能够同时设置材质的环境颜色和散射颜色,并将它们设置为相同的RGBA值。
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, aMaterial);
glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, dMaterial);
glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, sMaterial);
glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, &shiniess);

glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glEnable(GL_LIGHT1);
///////////////////////////

//地球仪
sphere.InitData(1.0f);
sphere.SetPos(cgPoint3D(0.0f, 2.5f,-8.0f));
sphere.SetTexture(LoadTexture("Textures/earth2.jpg"));

// 太阳光照
sun.InitData(3.0f);
sun.SetPos(cgPoint3D(sun_position[0], sun_position[1], sun_position[2]));
sun.SetTexture(LoadTexture("Textures/sun.jpeg"));

// 正方体
cube.InitData(1.0f);
cube.SetPos(cgPoint3D(1.0f, 1.0f, -5.0f));
cube.SetTexture(LoadTexture("Textures/timg.bmp"));

// 圆柱体
cylinder.InitData(1.0f, 5.0f);
cylinder.SetPos(cgPoint3D(3.0f, 4.0f, -3.0f));
cylinder.SetTextureID(LoadTexture("Textures/desert.bmp"));

//地面纹理
textureID = LoadTexture("Textures/timg.jpg");
}

void display(void)
{
glClear(GL_COLOR_BUFFER_BIT| GL_DEPTH_BUFFER_BIT );//清除帧缓存和深度缓存

//模型观察矩阵初始化
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();

float at[3];
for (int i=0; i<3; i++)
at[i] = pos[i] + headdir[i];

gluLookAt(pos[0], pos[1], pos[2], at[0], at[1], at[2], 0.0, 1.0, 0.0);

if (rendermode==1)
glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);
else
glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);

//地面
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, textureID);
glColor3f(1.0f,1.0f,1.0f);
glBegin(GL_QUADS);
glTexCoord2f(0.0f,0.0f);//指定顶点纹理坐标
glNormal3f(0,1,0);//指定顶点法向量
glVertex3f(-50.0f,0.0f,50.0f);//指定顶点坐标

glTexCoord2f(10.0f,0.0f);
glNormal3f(0,1,0);
glVertex3f(50.0f,0.0f,50.0f);

glTexCoord2f(10.0f,10.0f);
glNormal3f(0,1,0);
glVertex3f(50.0f,0.0f,-50.0f);

glTexCoord2f(0.0f,10.0f);
glNormal3f(0,1,0);
glVertex3f(-50.0f,0.0f,-50.0f);
glEnd();
glDisable(GL_TEXTURE_2D);

glColor3f(0.25,0.5,0.5);
sphere.Render();

glColor3f(1.0,0.0,0.0);
cube.Render();
sun.Render();
cylinder.Render();

glutSwapBuffers();//交换缓冲(双缓冲时使用)
}

void reshape(int width, int height)
{
glViewport(0, 0, width, height);//设置视区(窗口内绘图区域)

glMatrixMode(GL_PROJECTION);
glLoadIdentity();

gluPerspective(60,(GLfloat) width/(GLfloat)height, 1.0, 200.0);

glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}

void change()
{
glutPostRedisplay();
}

void keyboard(unsigned char key, int x, int y)
{
switch (key)
{
case 'W': //上移
case 'w':
for (int i=0; i<3; i++)
pos[i] += step*headdir[i];
break;
case 'S': //下移
case 's':
for (int i=0; i<3; i++)
pos[i] -= step*headdir[i];
break;
case 'A': //左移
case 'a':
for (int i=0; i<3; i++)
pos[i] -= step*rightdir[i];
break;
case 'D': //右移
case 'd':
for (int i=0; i<3; i++)
pos[i] += step*rightdir[i];
break;

case 'Z':
case 'z':
//抬高相机
pos[1] += .30f;
break;

case 'X':
case 'x':
//降低相机
pos[1] -= .30f;
break;

case 'T': //修改绘制模式
case 't':
rendermode = (++rendermode)%2;
break;

// 光源移动
case '1':
sun_position[0] += 1.0f;
glLightfv(GL_LIGHT1, GL_POSITION, sun_position);
sun.SetPos(cgPoint3D(sun_position[0], sun_position[1], sun_position[2]));
break;
case '2':
sun_position[0] -= 1.0f;
glLightfv(GL_LIGHT1, GL_POSITION, sun_position);
sun.SetPos(cgPoint3D(sun_position[0], sun_position[1], sun_position[2]));
break;
case '3':
sun_position[1] += 1.0f;
glLightfv(GL_LIGHT1, GL_POSITION, sun_position);
sun.SetPos(cgPoint3D(sun_position[0], sun_position[1], sun_position[2]));
break;
case '4':
sun_position[1] -= 1.0f;
glLightfv(GL_LIGHT1, GL_POSITION, sun_position);
sun.SetPos(cgPoint3D(sun_position[0], sun_position[1], sun_position[2]));
break;
case '5':
glEnable(GL_LIGHT1);
break;
case '6':
glDisable(GL_LIGHT1);
break;
case '7':
glEnable(GL_LIGHT0);
break;
case '8':
glDisable(GL_LIGHT0);
break;

case 'n':
case 'N':
cube.UpdateTexCord();
break;
}

glutPostRedisplay();
}

void SpecialKey(GLint key,GLint x,GLint y)
{
switch (key)
{
case GLUT_KEY_UP:
headdir[1] += 0.05f;
break;

case GLUT_KEY_DOWN:
headdir[1] -= 0.05f;
break;

case GLUT_KEY_LEFT:
//修改前进方向
beta += betastep;
headdir[0] = sin(beta / 180 * 3.14);
headdir[2] = cos(beta / 180 * 3.14);
headdir[1] = headdir[1];

//修改右向方向
rightdir[0] = -cos(beta / 180 * 3.14);
rightdir[2] = sin(beta / 180 * 3.14);
rightdir[1] = rightdir[1];
break;

case GLUT_KEY_RIGHT:
//修改前进方向
beta -= betastep;
headdir[0] = sin(beta / 180 * 3.14);
headdir[2] = cos(beta / 180 * 3.14);
headdir[1] = headdir[1];

//修改右向方向
rightdir[0] = -cos(beta / 180 * 3.14);;
rightdir[2] = sin(beta / 180 * 3.14);;
rightdir[1] = rightdir[1];
break;

}

glutPostRedisplay();
}

int main(int argc, char* argv[])
{
glutInitWindowPosition(200, 200);//应用程序窗口位置
glutInitWindowSize(800, 600);//窗口大小

glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE| GLUT_DEPTH );//双缓冲,单缓冲为GLUT_SINGLE,深度缓存
glutCreateWindow("Project Four");//创建窗口,参数为窗口标题
init();

glutDisplayFunc(display);//图形绘制
glutReshapeFunc(reshape);//窗口大小变化
glutKeyboardFunc(keyboard);//键盘交互
glutSpecialFunc(&SpecialKey);//方向键
glutIdleFunc(change);//空闲时间执行

glutMainLoop();//必须,进入GLUT事件处理循环

return 0;
}

效果展示

原场景关闭光照后移动视角后