• opengl简单入门实例


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

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

    • 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轴垂直屏幕向外为正。符合右手定则。

    • 2020/5/15 13:06:37 对于图像的各种变换做一个小的补充
    • 视图变换即设置/改变观察点的位置,可以这么理解,相当于选择一个位置和方向设置一台照相机。针对glLookAt()函数而言,它一共有九个参数,3对坐标值。第一对三维坐标是观察点(照相机)在世界坐标中的位置,第二对三维坐标是被观察点(物体)的位置。从第一对坐标到第二对坐标的向量其实就指定了照相机的方向。比如说人站在台阶上,这是人在世界坐标的位置,然后人可以朝天空看,也可以朝地上看,可以朝北方看,也可以朝南方看。这个方向就是由两对坐标所造成的向量来决定的。第三对坐标是人头部的正向,可以指定人站着看,也可以倒立着看,类似于这样。
    • 模型变换则是改变物体本身的位置与方向。用到的函数有glTranslate,glRotate,glScale。这些都是对物体的坐标做变换的,相当于乘以一个变换矩阵。那么这里面有两个需要注意的点。
      •  1 变换的顺序是逆向的。也就是说,如果我们写的顺序是先平移再旋转,那么实际得到的结果应该是先旋转再平移。所以我们可以用堆栈来实现。对于堆栈的概念不多作解释了,如果不清楚可以去查数据结构。堆栈用到的函数是glPushMatrix()和glPopMatrix()。用法呢其实就是先声明push,然后按照想要的顺序写好矩阵函数,最后Pop一下,就能得到想要的结果。
      •    2 比如连续使用偏移函数,则第二个偏移的结果其实是在第一次作偏移的基础上再做偏移的。那么如果我不想这样算怎么办呢?清空矩阵。用到的函数就是glLoadIdentity()。它的作用是把当前矩阵设置为单位矩阵。一般在开始做变换前都是需要调用一次这个函数的。
    • 那么事实上,在OpenGL中,因为视图变换和模型变换的效果是类似的,所以这两个变换放在一个模式里面。在进行这两种变换前,需要声明glMatrixMode(GL_MODEVIEW)。看到这个'Matrix'是不是很眼熟呢?没错,上面讲堆栈函数的时候用到了。相信你们也会有个疑问,如果直接调用堆栈函数,这个堆栈是在哪里的呢?这个堆栈段不需要自己声明了,但它其实是属于这个模式的堆栈段。因为在接下来要将的投影变换的模式下也有自己的堆栈段。所以我也是从这个角度理解为什么要分为这两个模式的原因。
    • 投影变换事实上时指定了一个可视空间。相当于你在外部架好了照相机,但你仍然可以在照相机的镜头里设置要不要放大看到的景象。在教程【2】里有有关于这个的图。所以首先我们要声明模式glMatrixMode(GL_PROJECTION),并单位化矩阵glLoadIdentity()。
      • 在这个模式里有透视投影和正投影(我们做3D一般用的都是透视投影)。正投影的函数有glOrtho()和gluOrtho2D(),透视投影的函数有glFrustum()和gluPerspective()。我们最常用的当然就是gluPerspective()啦。
      • gluPerspective()中的第一个参数是角度,它相当于人的眼皮要睁开多大,也就是2*仰角(仰角=俯角=1/2这个角度)。第二个参数是比例,应该是跟显示的屏幕的宽高比例有关(但是这点我不是很确定,只是暂时这么理解,如果有更好的解释,欢迎在评论区提出)。第三、四个参数则是表示截取的范围,相当于两堵墙,两墙之间的东西能看,墙外的都忽略。这就是透视的意义。
    • 那么最后就是视口变换,用到的函数是glViewport()。这个就不多做介绍了。

     1 void display(void)
     2 {
     3     glEnable(GL_DEPTH_TEST);  //3、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,  //事实上6、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 }
  • 相关阅读:
    如何自建appender扩展Log4j框架
    在O(1)时间删除链表结点
    My First GitHub
    JAVA序列化和反序列化
    [转]Vim 复制粘帖格式错乱问题的解决办法
    Centos清理内存 内存回收释放及内存使用查看的相关命令
    Spark HA 的搭建
    Ambari安装
    Hadoop HA的搭建
    Hadoop32位和64位的查询
  • 原文地址:https://www.cnblogs.com/vinchy-z/p/12887877.html
Copyright © 2020-2023  润新知