opengl简单入门实例

  • 实现任务目标:
    • 使用纹理贴图,加强可视效果
    • 应用坐标变换,实现场景中不一样物体重建
    • 采用双缓冲技术,实现场景实时绘制
    • 具备必定的鼠标、键盘交互功能
  • 先放效果

鼠标的交互功能有:右键暂停转动,左键继续转动,滚轮向前放大,向后缩小html

  • IDE:opengl实现须要库函数。用的编译环境是visual studio。附上一个很好的教程【1】:在vs2017下配置opengl。(vs2019也能够用)
  • 一个很好的入门教程【2】:OpenGL入门教程(精)。讲得很仔细,通俗易懂。前几课用到的库都没有超过glut的范围。
  • 事实上,对于opengl的实现主要是对于各类库函数的调用,因此对于各类库函数的认知很重要。这里也给出一个很好的教程【3】:OpenGL库函数汇总
  • ok,在看了上面的教程之后确定对于opengl有了必定认识,尤为是第二个教程中讲解得很是仔细。因此本文接下来的内容是创建在对那个教程的学习基础之上,对一些我在实践中遇到的问题做出补充。
  • 下面就进入正文。
  • 所包含的头文件目录
1 #include <GL/glut.h>  
2 #include <stdlib.h>
3 #include <stdio.h>
  • 最基本的功能是固然是建立本身的图形并显示出来,如上图我建立的是日地月系统。须要的函数为display()和main()。
  • 这其中很重要的一个知识点就是图像的视图变换/模型变换、投影变换和视口变换。有关这块的内容我的以为教程【2】中讲得不够清楚,能够参考一些别的教程。好比:OpenGL(六) gluLookAt和gluPerspective函数解析Opengl---gluLookAt函数详解
  • 这里要介绍一下opengl中的坐标轴。x轴水平向右为正,y轴竖直向上为正,z轴垂直屏幕向外为正。符合右手定则。
 1 void display(void)
 2 {
 3     glEnable(GL_DEPTH_TEST);  //三、5行代码中跟DEPTH有关的函数是为了在同一个窗口内建立多个图像而不会被后建立的图像覆盖。
 4     glClearColor(0, 0, 0, 1);  //设置“空”色。以前看到一个很好的解释,白纸是白色的,因此白纸上的“空”色为白色。那么信封上的“空”色就是信封的颜色。
 5     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //COLOR的那个参数是清除颜色缓存,设为“空”色
 6 
 7     glMatrixMode(GL_PROJECTION);  //投影变换
 8     glLoadIdentity();
 9     gluPerspective(60.0, 1, 1.0, 100.0);
10 
11     glMatrixMode(GL_MODELVIEW);   //视图变换/模型变换
12     glLoadIdentity();  //加载单位矩阵  
13     gluLookAt(0.0, 0.0, 60.0, 0, 0, 0, 0.0, 1.0, 0);
15    //太阳
16     glColor3f(1.0, 0, 0);
17     glutSolidSphere(6, 20, 20);
18   //地球
19     glColor3f(0.0, 0, 1.0);
20     glTranslatef(-20.0, 0, 0); //偏移矩阵
21     glutSolidSphere(3, 20, 20);
22   //月球
23     glColor3f(1.0, 1.0, 0);
24     glTranslatef(-6.0, 0, 0); //这里的偏移量是在上面已经偏移的基础上再进行偏移
25     glutSolidSphere(1, 20, 20);
26 
27     glutSwapBuffers(); //双缓冲函数用到,相关内容看上面的教程【2】里
28 }
  •  main()中的函数就不具体解释了,应该都懂
 1 int main(int argc, char** argv)
 2 {
 3     glutInit(&argc, argv);
 4     glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
 5     glutInitWindowSize(500, 500);
 6     glutInitWindowPosition(100, 100);
 7     glutCreateWindow("name");
 8     glutDisplayFunc(&display);
 9     glutMainLoop();
10     return 0;
11 }
  • 如今在现有程序的基础上加入动画须要4步
  • 1 加入全局变量
1 static GLfloat angle = 0.0f; 
  • 2 在display()里面加入旋转的函数。因为效果是让整个画面都转,这句话我选择加在gluLookAt()后面。须要加入的语句已标红。
 1 void display(void)
 2 {
 3     …… ……
 4     glMatrixMode(GL_MODELVIEW);
 5     glLoadIdentity();  //加载单位矩阵  
 6     gluLookAt(place_x, 0.0, place_z, 0, 0, 0, 0.0, 1.0, 0);
 7     glRotatef(angle, 0.0f, 1.0f, 0.0f); //旋转,改变的是x轴份量
 8 
 9     glColor3f(1.0, 0, 0);
10     …… ……
11 }
  • 3 编写myIdle()函数
1 void myIdle(void)
2 {
3     angle += 1.8f;
4     if (angle >= 360.0f)
5         angle = 0.0f;
6     display();
7 }
  • 4 在主函数加入glutIdleFunc(&myIdle);能够加在刚刚的display语句下面。
1 int main(int argc, char** argv)
2 {
3     …… ……
4     glutDisplayFunc(&display);
5     glutIdleFunc(&myIdle);
6         …… ……
7     glutMainLoop();
8     return 0;
9 }
  • ok。接下来就要为咱们的程序加上纹理了。首先在网上找了两张星空的网图。并且,为了方便起见,我把它们的格式改为了24位色的bmp图片,尺寸为258*258。
  • 至于怎么改格式:1 24位色能够对图片另存为时在下拉菜单里选择。2 修改尺寸能够用win自带的图片编辑器。
  • 个人两张照片分别命名为“wall.bmp”,"ground.bmp"。放在源程序的同一个子目录里面
  • 有关纹理贴图的详细内容继续参考教程【2】。这里附上我写的程序和说明。
  • 一共分为3步。
  • 1 搭建矩形框架【对个人程序来讲至关于有一个支架,而后把按照点对点的方式纹理图贴上去】
  • 在这一步中先只写上矩形各个点的坐标,为后面创建矩形作准备。

 1 //全局变量
 2 static const GLfloat vertex_list[][3] = {
 3     - 15.0f, -20.0f, -10.0f,  //事实上六、7两个点是用不到的,做为完整性就一块儿写了。贴图只在背面和底面贴了图,为了更好的演示效果。
 4     40.0f, -20.0f, -10.0f,
 5     40.0f,  20.0f, -10.0f,
 6     -15.0f,  20.0f, -10.0f,
 7     -15.0f, -20.0f,  10.0f,
 8     40.0f, -20.0f,  10.0f,
 9     -15.0f,  20.0f,  10.0f,
10     40.0f,  20.0f,  10.0f,
11  };
  1 //全局变量
  2 #define BMP_Header_Length 54 
  3 //函数
  4 // 函数power_of_two用于判断一个整数是否是2的整数次幂
  5 int power_of_two(int n)
  6 {
  7     if (n <= 0)
  8         return 0;
  9     return (n & (n - 1)) == 0;
 10 }
 11 /* 函数load_texture
 12 * 读取一个BMP文件做为纹理
 13 * 若是失败,返回0,若是成功,返回纹理编号
 14 */
 15 GLuint load_texture(const char* file_name)
 16 {
 17     GLint width, height, total_bytes;
 18     GLubyte* pixels = 0;
 19     GLuint last_texture_ID = 0, texture_ID = 0;
 20 
 21     // 打开文件,若是失败,返回
 22     FILE* pFile;
 23     errno_t err;
 24     err = fopen_s(&pFile, file_name, "rb");  //在vs中使用fopen_s()函数的示例。
 25     if (!pFile) exit(0);
 26 
 27     // 读取文件中图象的宽度和高度
 28     fseek(pFile, 0x0012, SEEK_SET);
 29     fread(&width, sizeof(width), 1, pFile);
 30     fread(&height, sizeof(height), 1, pFile);
 31     fseek(pFile, BMP_Header_Length, SEEK_SET);
 32 
 33     // 计算每行像素所占字节数,并根据此数据计算总像素字节数
 34     {
 35         GLint line_bytes = width * 3;
 36         while (line_bytes % 4 != 0)
 37             ++line_bytes;
 38         total_bytes = line_bytes * height;
 39     }
 40 
 41     // 根据总像素字节数分配内存
 42     pixels = (GLubyte*)malloc(total_bytes);
 43     if (pixels == 0)
 44     {
 45         fclose(pFile);
 46         return 0;
 47     }
 48 
 49     // 读取像素数据
 50     if (fread(pixels, total_bytes, 1, pFile) <= 0)
 51     {
 52         free(pixels);
 53         fclose(pFile);
 54         return 0;
 55     }
 56 
 57     // 对就旧版本的兼容,若是图象的宽度和高度不是的整数次方,则须要进行缩放
 58     // 若图像宽高超过了OpenGL规定的最大值,也缩放
 59     {
 60         GLint max;
 61         glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max);
 62         if (!power_of_two(width)
 63             || !power_of_two(height)
 64             || width > max
 65             || height > max)
 66         {
 67             const GLint new_width = 256;
 68             const GLint new_height = 256; // 规定缩放后新的大小为边长的正方形
 69             GLint new_line_bytes, new_total_bytes;
 70             GLubyte* new_pixels = 0;
 71 
 72             // 计算每行须要的字节数和总字节数
 73             new_line_bytes = new_width * 3;
 74             while (new_line_bytes % 4 != 0)
 75                 ++new_line_bytes;
 76             new_total_bytes = new_line_bytes * new_height;
 77 
 78             // 分配内存
 79             new_pixels = (GLubyte*)malloc(new_total_bytes);
 80             if (new_pixels == 0)
 81             {
 82                 free(pixels);
 83                 fclose(pFile);
 84                 return 0;
 85             }
 86 
 87             // 进行像素缩放
 88             gluScaleImage(GL_RGB,
 89                 width, height, GL_UNSIGNED_BYTE, pixels,
 90                 new_width, new_height, GL_UNSIGNED_BYTE, new_pixels);
 91 
 92             // 释放原来的像素数据,把pixels指向新的像素数据,并从新设置width和height
 93             free(pixels);
 94             pixels = new_pixels;
 95             width = new_width;
 96             height = new_height;
 97         }
 98     }
 99 
100     // 分配一个新的纹理编号
101     glGenTextures(1, &texture_ID);
102     if (texture_ID == 0)
103     {
104         free(pixels);
105         fclose(pFile);
106         return 0;
107     }
108 
109     // 绑定新的纹理,载入纹理并设置纹理参数
110     // 在绑定前,先得到原来绑定的纹理编号,以便在最后进行恢复
111     GLint lastTextureID = last_texture_ID;
112     glGetIntegerv(GL_TEXTURE_BINDING_2D, &lastTextureID);
113     glBindTexture(GL_TEXTURE_2D, texture_ID);
114     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
115     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
116     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
117     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
118     glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
119     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0,
120         GL_BGR_EXT, GL_UNSIGNED_BYTE, pixels);
121     glBindTexture(GL_TEXTURE_2D, lastTextureID);  //恢复以前的纹理绑定
122     free(pixels);
123     return texture_ID;
124 }
  • 3 在display()中打开状态机->读取纹理图片->搭起矩形框架->贴图->关闭状态机。
  • 这里踩过的坑就是关于状态机的开闭问题。若是没有关闭状态机,显示的图像中以前画的几个球都是全黑的。这是由于纹理贴图会干扰别的颜色
  • 其中用到的glTexCoord2f()函数能够参考百度的这个示例。
 1 //全局变量
 2 GLuint texGround;
 3 GLuint texWall;
 4 //函数补充
 5 void display(void)
 6 {
 7     …… ……//以前内容的后面加入一下内容
 8     glEnable(GL_TEXTURE_2D); //开启状态机
 9     texGround = load_texture("ground.bmp");
10     texWall = load_texture("wall.bmp");
11     
12     //绘制底面
13     glBindTexture(GL_TEXTURE_2D, texGround);
14     glBegin(GL_QUADS);
15     glTexCoord2f(0.0f, 0.0f); glVertex3fv(vertex_list[4]); //点对点
16     glTexCoord2f(1.0f, 0.0f); glVertex3fv(vertex_list[5]);
17     glTexCoord2f(1.0f, 1.0f); glVertex3fv(vertex_list[1]);
18     glTexCoord2f(0.0f, 1.0f); glVertex3fv(vertex_list[0]);
19     glEnd();
20     //绘制立面
21     glBindTexture(GL_TEXTURE_2D, texWall);
22     glBegin(GL_QUADS);
23     glTexCoord2f(0.0f, 0.0f); glVertex3fv(vertex_list[0]);
24     glTexCoord2f(1.0f, 0.0f); glVertex3fv(vertex_list[1]);
25     glTexCoord2f(1.0f, 1.0f); glVertex3fv(vertex_list[2]);
26     glTexCoord2f(0.0f, 1.0f); glVertex3fv(vertex_list[3]);
27     glEnd();
28     glDisable(GL_TEXTURE_2D);//关闭状态机
29     glutSwapBuffers();
30 }
  • ok。那么到这里咱们已经完成了纹理贴图、双缓冲绘制和场景重建的任务啦。接下来还有鼠标交互的任务。那么在这里先插入一个新的函数讲解:reshape()。
  • 关于reshape()的原理呢能够去查查资料。我说说个人理解吧。简单来讲呢就是在你显示窗口时,若是你拉动边框,窗口内的图像不会随着你拉动而改变。
  • 附上一个简单的图片示例。

  能够看到在右边的图中,我拉动了窗口的边框,则图像的形状也改变了。缓存

  • reshape()就能在窗体大小被改变时,窗口大小不变,图像比例也不变。
  • 那么一样的,完成这个功能须要2步。
  • 1 写一个reshape()函数
 1 void reshape(int w, int h)
 2 {
 3     glViewport(0, 0, 500, 500);
 4     glMatrixMode(GL_PROJECTION);
 5     glLoadIdentity();
 6     gluPerspective(60.0, 1, 1, 100.0);
 7     glMatrixMode(GL_MODELVIEW);
 8     glLoadIdentity();
 9     gluLookAt(0, 0.0, 60.0, 0, 0, 0, 0.0, 1.0, 0);
10 }
  • 2 在main函数中加入一句
1 int main(int argc, char** argv)
2 {
3     …… ……
4     glutDisplayFunc(&display);
5     glutReshapeFunc(&reshape);
6     glutIdleFunc(&myIdle);
7     …… ……
8 }
  • ok。最后的最后,要完成鼠标的交互了。
  • 我所设置的鼠标的功能包括:右键暂停、左键继续;滚轮向上放大,滚轮向下缩小。
  • 前两个改变的是转过的角度angle,后两个则跟咱们所创建的视图模型,也就是以前用过glLookAt()函数的参数有关。
  • 对于鼠标交互用到的函数及参量是void myMouse(int button, int state, int x, int y);关于这个更多的信息能够自行查找。
  • 那么一样的,完成这个须要2 / 3步。可是我分为两个部分来说。首先是对于右键暂停和左键继续的部分。
  • 1 以前的显示函数里已经有了一个angle变量用来控制角度,因此咱们要作的就是停掉这个angle变量的自增,因此咱们要停用myIdle函数。
 1 void myMouse(int button, int state, int x, int y)
 2 {
 3     if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN)
 4     {
 5         glutIdleFunc(&myIdle);
 6     }
 7     if (button == GLUT_RIGHT_BUTTON && state == GLUT_DOWN)
 8     {
 9         glutIdleFunc(NULL);
10     }
11 }
  • 2 在主函数中加入语句。
1 int main(int argc, char** argv)
2 {
3     …… ……
4     glutCreateWindow("name");
5     glutMouseFunc(&myMouse);
6     glutDisplayFunc(&display);
7     …… ……
8 }
  • 对于缩放.
  • 1 由于要涉及到以前显示函数display()中的glLookAt()的改变,因此咱们将其中的值设为全局变量。
 1 //全局变量
 2 static float place_z = 60.0f;
 3 static float place_x = 0.0f;
 4 //修改函数参数
 5 void display(void)
 6 {
 7     …… …… 
 8     gluLookAt(place_x, 0.0, place_z, 0, 0, 0, 0.0, 1.0, 0);
 9     glRotatef(angle, 0.0f, 1.0f, 0.0f); //旋转
10         …… ……
11 }
  • 2 在以前的鼠标的函数中加入对滚轮的控制语句
 1 //全局变量
 2 #define  GLUT_WHEEL_UP 3 
 3 #define  GLUT_WHEEL_DOWN 4
 4 //函数中
 5 void myMouse(int button, int state, int x, int y)
 6 {
 7     …… ……
 8     if (state == GLUT_UP && button == GLUT_WHEEL_UP)
 9     {
10         glutReshapeFunc(NULL);
11         place_z -= 5.0;
12         display();        
13     }
14     if (state == GLUT_UP && button == GLUT_WHEEL_DOWN)
15     {
16         glutReshapeFunc(NULL);
17         place_z += 5.0;
18         display();
19     }
20 }
  • 这样就ok啦。到这里就完成了一开始提出四个目标以及一个reshape()函数。效果就如最开始的gif动画同样。
  • 这里还须要提到的一点是,动画播放的速度在不一样的cpu里是不同的,若是太快或太慢能够经过myIdle函数的angle自增的大小来控制。
  • 为了不混乱,最后附上完整的源代码。
  1 #include <GL/glut.h>  
  2 #include <stdlib.h>
  3 #include <stdio.h>
  4 
  5 static const GLfloat vertex_list[][3] = {
  6     - 15.0f, -20.0f, -10.0f,
  7     40.0f, -20.0f, -10.0f,
  8     40.0f,  20.0f, -10.0f,
  9     -15.0f,  20.0f, -10.0f,
 10     -15.0f, -20.0f,  10.0f,
 11     40.0f, -20.0f,  10.0f,
 12     -15.0f,  20.0f,  10.0f,
 13     40.0f,  20.0f,  10.0f,
 14  };
 15 GLuint texGround;
 16 GLuint texWall;
 17 
 18 #define BMP_Header_Length 54  
 19 static GLfloat angle = 0.0f;   
 20 static float place_z = 60.0f;
 21 static float place_x = 0.0f;
 22 #define  GLUT_WHEEL_UP 3 
 23 #define  GLUT_WHEEL_DOWN 4
 24 
 25 // 函数power_of_two用于判断一个整数是否是2的整数次幂
 26 int power_of_two(int n)
 27 {
 28     if (n <= 0)
 29         return 0;
 30     return (n & (n - 1)) == 0;
 31 }
 32 
 33 /* 函数load_texture
 34 * 读取一个BMP文件做为纹理
 35 * 若是失败,返回0,若是成功,返回纹理编号
 36 */
 37 GLuint load_texture(const char* file_name)
 38 {
 39     GLint width, height, total_bytes;
 40     GLubyte* pixels = 0;
 41     GLuint last_texture_ID = 0, texture_ID = 0;
 42 
 43     // 打开文件,若是失败,返回
 44     FILE* pFile;
 45     errno_t err;
 46     err = fopen_s(&pFile, file_name, "rb");
 47     if (!pFile) exit(0);
 48 
 49     // 读取文件中图象的宽度和高度
 50     fseek(pFile, 0x0012, SEEK_SET);
 51     fread(&width, sizeof(width), 1, pFile);
 52     fread(&height, sizeof(height), 1, pFile);
 53     fseek(pFile, BMP_Header_Length, SEEK_SET);
 54 
 55     // 计算每行像素所占字节数,并根据此数据计算总像素字节数
 56     {
 57         GLint line_bytes = width * 3;
 58         while (line_bytes % 4 != 0)
 59             ++line_bytes;
 60         total_bytes = line_bytes * height;
 61     }
 62 
 63     // 根据总像素字节数分配内存
 64     pixels = (GLubyte*)malloc(total_bytes);
 65     if (pixels == 0)
 66     {
 67         fclose(pFile);
 68         return 0;
 69     }
 70 
 71     // 读取像素数据
 72     if (fread(pixels, total_bytes, 1, pFile) <= 0)
 73     {
 74         free(pixels);
 75         fclose(pFile);
 76         return 0;
 77     }
 78 
 79     // 对就旧版本的兼容,若是图象的宽度和高度不是的整数次方,则须要进行缩放
 80     // 若图像宽高超过了OpenGL规定的最大值,也缩放
 81     {
 82         GLint max;
 83         glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max);
 84         if (!power_of_two(width)
 85             || !power_of_two(height)
 86             || width > max
 87             || height > max)
 88         {
 89             const GLint new_width = 256;
 90             const GLint new_height = 256; // 规定缩放后新的大小为边长的正方形
 91             GLint new_line_bytes, new_total_bytes;
 92             GLubyte* new_pixels = 0;
 93 
 94             // 计算每行须要的字节数和总字节数
 95             new_line_bytes = new_width * 3;
 96             while (new_line_bytes % 4 != 0)
 97                 ++new_line_bytes;
 98             new_total_bytes = new_line_bytes * new_height;
 99 
100             // 分配内存
101             new_pixels = (GLubyte*)malloc(new_total_bytes);
102             if (new_pixels == 0)
103             {
104                 free(pixels);
105                 fclose(pFile);
106                 return 0;
107             }
108 
109             // 进行像素缩放
110             gluScaleImage(GL_RGB,
111                 width, height, GL_UNSIGNED_BYTE, pixels,
112                 new_width, new_height, GL_UNSIGNED_BYTE, new_pixels);
113 
114             // 释放原来的像素数据,把pixels指向新的像素数据,并从新设置width和height
115             free(pixels);
116             pixels = new_pixels;
117             width = new_width;
118             height = new_height;
119         }
120     }
121 
122     // 分配一个新的纹理编号
123     glGenTextures(1, &texture_ID);
124     if (texture_ID == 0)
125     {
126         free(pixels);
127         fclose(pFile);
128         return 0;
129     }
130 
131     // 绑定新的纹理,载入纹理并设置纹理参数
132     // 在绑定前,先得到原来绑定的纹理编号,以便在最后进行恢复
133     GLint lastTextureID = last_texture_ID;
134     glGetIntegerv(GL_TEXTURE_BINDING_2D, &lastTextureID);
135     glBindTexture(GL_TEXTURE_2D, texture_ID);
136     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
137     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
138     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
139     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
140     glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
141     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0,
142         GL_BGR_EXT, GL_UNSIGNED_BYTE, pixels);
143     glBindTexture(GL_TEXTURE_2D, lastTextureID);  //恢复以前的纹理绑定
144     free(pixels);
145     return texture_ID;
146 }
147 void display(void)
148 {
149     glEnable(GL_DEPTH_TEST);
150     glClearColor(0, 0, 0, 1);
151     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
152 
153     glMatrixMode(GL_PROJECTION);
154     glLoadIdentity();
155     gluPerspective(60.0, 1, 1.0, 100.0);
156 
157     glMatrixMode(GL_MODELVIEW);
158     glLoadIdentity();  //加载单位矩阵  
159     gluLookAt(place_x, 0.0, place_z, 0, 0, 0, 0.0, 1.0, 0);
160     glRotatef(angle, 0.0f, 1.0f, 0.0f); //旋转
161 
162     glColor3f(1.0, 0, 0);
163     glutSolidSphere(6, 20, 20);
164 
165     glColor3f(0.0, 0, 1.0);
166     glTranslatef(-20.0, 0, 0);
167     glutSolidSphere(3, 20, 20);
168 
169     glColor3f(1.0, 1.0, 0);
170     glTranslatef(-6.0, 0, 0);
171     glutSolidSphere(1, 20, 20);
172 
173     glEnable(GL_TEXTURE_2D);
174     texGround = load_texture("ground.bmp");
175     texWall = load_texture("wall.bmp");
176     
177     //绘制底面
178     glBindTexture(GL_TEXTURE_2D, texGround);
179     glBegin(GL_QUADS);
180     glTexCoord2f(0.0f, 0.0f); glVertex3fv(vertex_list[4]);
181     glTexCoord2f(1.0f, 0.0f); glVertex3fv(vertex_list[5]);
182     glTexCoord2f(1.0f, 1.0f); glVertex3fv(vertex_list[1]);
183     glTexCoord2f(0.0f, 1.0f); glVertex3fv(vertex_list[0]);
184     glEnd();
185     //绘制立面
186     glBindTexture(GL_TEXTURE_2D, texWall);
187     glBegin(GL_QUADS);
188     glTexCoord2f(0.0f, 0.0f); glVertex3fv(vertex_list[0]);
189     glTexCoord2f(1.0f, 0.0f); glVertex3fv(vertex_list[1]);
190     glTexCoord2f(1.0f, 1.0f); glVertex3fv(vertex_list[2]);
191     glTexCoord2f(0.0f, 1.0f); glVertex3fv(vertex_list[3]);
192     glEnd();
193     glDisable(GL_TEXTURE_2D);
194     glutSwapBuffers();
195 }
196 void myIdle(void)
197 {
198     angle += 1.8f;
199     if (angle >= 360.0f)
200         angle = 0.0f;
201     display();
202 }
203 void myMouse(int button, int state, int x, int y)
204 {
205     if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN)
206     {
207         glutIdleFunc(&myIdle);
208     }
209     if (button == GLUT_RIGHT_BUTTON && state == GLUT_DOWN)
210     {
211         glutIdleFunc(NULL);
212     }
213     if (state == GLUT_UP && button == GLUT_WHEEL_UP)
214     {
215         glutReshapeFunc(NULL);
216         place_z -= 5.0;
217         display();        
218     }
219     if (state == GLUT_UP && button == GLUT_WHEEL_DOWN)
220     {
221         glutReshapeFunc(NULL);
222         place_z += 5.0;
223         display();
224     }
225 }
226 
227 void reshape(int w, int h)
228 {
229     glViewport(0, 0, 500, 500);
230     glMatrixMode(GL_PROJECTION);
231     glLoadIdentity();
232     gluPerspective(60.0, 1, 1, 100.0);
233     glMatrixMode(GL_MODELVIEW);
234     glLoadIdentity();
235     gluLookAt(0, 0.0, 60.0, 0, 0, 0, 0.0, 1.0, 0);
236 }
237 int main(int argc, char** argv)
238 {
239     glutInit(&argc, argv);
240     glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
241     glutInitWindowSize(500, 500);
242     glutInitWindowPosition(100, 100);
243     glutCreateWindow("name");
244     glutMouseFunc(&myMouse);
245     glutDisplayFunc(&display);
246     glutReshapeFunc(&reshape);
247     glutIdleFunc(&myIdle);
248     glutMainLoop();
249     return 0;
250 }