zoukankan      html  css  js  c++  java
  • 图形学_opengl纹理映射

    学了半学期的图形学,除了几个用python或是matlab比较方便的实验外,用的大多数是opengl,在这总结一下纹理贴图实验中opengl的用法。

    1、编译器连接静态库

    有用到glaux.h的程序,在加入相应的.h、.lib文件后,需要加入两行代码强行连接静态库:

    #pragma comment(lib, "glaux")
    #pragma comment(lib, "legacy_stdio_definitions")

    另外关于glaux.h,我想吐槽的是在csdn卖下载的人是有多想赚钱?……这里我把找到的glaux.h的下载链接贴出来,需要自取:

    链接:https://pan.baidu.com/s/1-P44eWXlehmd9jPYuXNiHw    提取码:hbi6

    2、画一个简单立方体

    最终目的是要把贴图映射到一个立方体上,首先我们需要构建一个不存在任何纹理的立方体。首先构建绘制函数:

    void Draw(void){
        glClear(GL_COLOR_BUFFER_BIT); 
        glLoadIdentity();        
        glTranslatef(0.0f, 0.0f, -5.0f);         
        glBegin(GL_QUADS);
    
        glVertex3f(-1.0f, -1.0f, 1.0f); 
        glVertex3f(1.0f, -1.0f, 1.0f);
        glVertex3f(1.0f, 1.0f, 1.0f); 
        glVertex3f(-1.0f, 1.0f, 1.0f); 
    
        glVertex3f(-1.0f, -1.0f, -1.0f); 
        glVertex3f(-1.0f, 1.0f, -1.0f); 
        glVertex3f(1.0f, 1.0f, -1.0f); 
        glVertex3f(1.0f, -1.0f, -1.0f);
    
        glVertex3f(-1.0f, 1.0f, -1.0f); 
        glVertex3f(-1.0f, 1.0f, 1.0f); 
        glVertex3f(1.0f, 1.0f, 1.0f);
        glVertex3f(1.0f, 1.0f, -1.0f); 
    
        glVertex3f(-1.0f, -1.0f, -1.0f); 
        glVertex3f(1.0f, -1.0f, -1.0f); 
        glVertex3f(1.0f, -1.0f, 1.0f); 
        glVertex3f(-1.0f, -1.0f, 1.0f); 
    
        glVertex3f(1.0f, -1.0f, -1.0f); 
        glVertex3f(1.0f, 1.0f, -1.0f);
        glVertex3f(1.0f, 1.0f, 1.0f); 
        glVertex3f(1.0f, -1.0f, 1.0f); 
    
        glVertex3f(-1.0f, -1.0f, -1.0f); 
        glVertex3f(-1.0f, -1.0f, 1.0f); 
        glVertex3f(-1.0f, 1.0f, 1.0f); 
        glVertex3f(-1.0f, 1.0f, -1.0f); 
        glEnd();
    
        glFlush(); 
    
    }

    首先使用大家都喜欢的(必须用的)清屏函数glClear将窗口清除成当前帧的颜色,不用的话满屏漆黑。glLoadIdentity()被用于重置观察模型矩阵(感觉这几个函数挺固定搭配的?);glTranslatef()用于移动模型,三个参数分别对应x,y,z轴线模型的移动,这里向负方向移动了5f。不挪 或者 向正方向挪,只会造成我们的视点根本无法观察到模型,负方向5f的位置相对大小比较合适。

    模板一样的东西套完以后,可以注意到glBegin()和glEnd()之间有一万个(不是)glVertex3f的堆叠,用于确定立方体的点。glBegin的参数QUADS表示接下来要绘制以四个点为一组的四边形,其他参数的含义如下:

    GL_POINTS:把每个顶点作为一个点进行处理,顶点n定义了点n,绘制N个点。

    GL_LINES:   把每个顶点作为一个独立的线段,顶点2n-1和2n之间定义了n条线段,绘制N/2条线段

    GL_LINE_STRIP:绘制从第一个顶点到最后一个顶点依次相连的一组线段,第n和n+1个顶点定义了线段n,绘制n-1条线段。

    GL_LINE_LOOP: 绘制从第一个顶点到最后一个顶点依次相连的一组线段,然后最后一个顶点和第一个顶点相连,第n和n+1个顶点定义了线段n,绘制n条线段。

    GL_TRIANGLES: 把每个顶点作为一个独立的三角形,顶点3n-2,3n-1和3n定义了第n个三角形,绘制了N/3个三角形。

    GL_TRIANGLE_STPIP:绘制一组相连的三角形,对于奇数n,顶点n,n+1,和n+2定义了第n个三角形;对于偶数n,顶点n+1,n和n+2定义了第n个三角形,绘制N-2个三角 形。

    GL_QUAD_STRIP:绘制一组相连的四边形。每个四边形是由一对顶点及其后给定的一对顶点共同确定的。顶点2n-1,2n,2n+2和2n+1定义了第n个四边形,绘制了N/2-1个   四边形。

    GL_POLYGON:绘制了一个凸多边形。顶点1到n定义了这个多边形。

    而glVertex3f()的三个函数对应x,y,z轴的坐标,共4*6个glVertex3f()函数调用,对应立方体6个面的4个点。最后使用glFlush()清除缓存。

    再构建一个display函数用于展示,将绘制函数置于display函数中:

    void display(void){
        glClear(GL_COLOR_BUFFER_BIT);    
        Draw();
        glutSwapBuffers();            
    }

    清屏函数还是熟悉的味道,只是多了glutSwapBuffers()用于交换缓冲区和显示图形。

    接下来是一个实现绘图之后转换矩阵模式的函数,用于主函数中的回调函数glutReshapeFunc()中:

    void reshape(GLsizei w, GLsizei h){
        glViewport(0, 0, w, h);        
        glMatrixMode(GL_PROJECTION);           
        gluPerspective(60, (GLfloat)w / h, 0, 100);  
        glMatrixMode(GL_MODELVIEW);      
    }

    绘图以后先将矩阵模式调为投影模式,在投影模式下使用gluPerspective调整投影模型(不同模式下能执行的操作不一样),四个参数分别代表视野范围(值越大,视野范围越宽阔),裁剪面的宽w高h比(影响到视野的截面有多大),近裁剪面到眼睛的距离,远裁剪面到眼睛的距离(都不能设置设置为负值)。设置完之后把矩阵设置为视图模式。

    最后是main()函数:

    int main(int argc, char* argv[]){
        glutInit(&argc, argv);  
        glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE);   
        glutInitWindowSize(600, 600);   
        glutInitWindowPosition(100, 100); 
        glutCreateWindow("大立方体?");
        glutDisplayFunc(display);
        glutReshapeFunc(reshape);                
        glutMainLoop();
        return 0;
    }

    main函数的固定搭配glutInit(),之后设置显示模式为RGB单缓存区,分别设置窗口大小和位置之后,使用glutDisplayFunc()调用display(),glutReshapeFunc调用reshape,所得图形是这样:

     

    呃…这不是个正方形嘛,其实只是我们的视点正对着立方体 而已。

    接下来给它贴上纹理,首先加入三轴的旋转变量与一个纹理数组:

    GLfloat  xrot = 0; 
    GLfloat  yrot = 0;  
    GLfloat  zrot = 0;   
    GLuint  texture[1];  

    读入贴图文件:

    AUX_RGBImageRec* LoadBMP(const char Filename[7]){
        FILE* File = NULL;         
        if (!Filename){
            return NULL;        
        }
        File = fopen(Filename, "r");       
        if (File) {
            fclose(File);        
            return auxDIBImageLoadA(Filename);    
        }
        return NULL;       
    }

    听说是固定搭配?

    有了读取位图的函数,我们需要将它转换成纹理:

    int LoadGLTextures(GLuint* texture, const char bmp_file_name[7], int texture_id){
        int re=1;         
        AUX_RGBImageRec* TextureImage[1];
        memset(TextureImage, 0, sizeof(void*) * 1);  
        if (TextureImage[0] = LoadBMP(bmp_file_name)){
            re = 0;         
            glGenTextures(texture_id, texture); 
            glBindTexture(GL_TEXTURE_2D, *texture); 
            glTexImage2D(GL_TEXTURE_2D, 0, 3, TextureImage[0]->sizeX, TextureImage[0]->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, TextureImage[0]->data);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 
        }
        if (TextureImage[0]){
            if (TextureImage[0]->data){
                free(TextureImage[0]->data); 
            }
            free(TextureImage[0]); 
        }
        else
            printf("纹理不存在");
        return re;      
    }

    首先声明一个AUX_RGBImageRec类型的指针,memset初始化之后调用之前的函数载入位图,glGenTextures生成纹理,参数分别为生成纹理的数量与指针,使用glBindTexture 将一个命名的纹理绑定到一个纹理目标上,glTexImage2D功能是根据指定的参数,生成一个2D纹理,这里的长、宽都必须是2的整数次方,glTexParameteri……是神秘的纹理过滤函数,对我们的纹理分别进行了一次缩小与放大的线性过滤。纹理生成之后,释放原图像的空间。

    接下来修改绘制函数,将纹理映射到六个面上:

    void Draw(void){
        glClear(GL_COLOR_BUFFER_BIT ); 
        glLoadIdentity();        
    glTranslatef(
    0.0f, 0.0f, -5.0f); glRotatef(xrot, 1.0f, 0.0f, 0.0f); glRotatef(yrot, 0.0f, 1.0f, 0.0f); glRotatef(zrot, 0.0f, 0.0f, 1.0f); glBindTexture(GL_TEXTURE_2D, texture[0]); glBegin(GL_QUADS); glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f); glTexCoord2f(1.0f, 0.0f); glVertex3f(1.0f, -1.0f, 1.0f); glTexCoord2f(1.0f, 1.0f); glVertex3f(1.0f, 1.0f, 1.0f); glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 1.0f); glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f); glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, 1.0f, -1.0f); glTexCoord2f(0.0f, 1.0f); glVertex3f(1.0f, 1.0f, -1.0f); glTexCoord2f(0.0f, 0.0f); glVertex3f(1.0f, -1.0f, -1.0f); glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, -1.0f); glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, 1.0f, 1.0f); glTexCoord2f(1.0f, 0.0f); glVertex3f(1.0f, 1.0f, 1.0f); glTexCoord2f(1.0f, 1.0f); glVertex3f(1.0f, 1.0f, -1.0f); glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, -1.0f, -1.0f); glTexCoord2f(0.0f, 1.0f); glVertex3f(1.0f, -1.0f, -1.0f); glTexCoord2f(0.0f, 0.0f); glVertex3f(1.0f, -1.0f, 1.0f); glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f); glTexCoord2f(1.0f, 0.0f); glVertex3f(1.0f, -1.0f, -1.0f); glTexCoord2f(1.0f, 1.0f); glVertex3f(1.0f, 1.0f, -1.0f); glTexCoord2f(0.0f, 1.0f); glVertex3f(1.0f, 1.0f, 1.0f); glTexCoord2f(0.0f, 0.0f); glVertex3f(1.0f, -1.0f, 1.0f); glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f); glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f); glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 1.0f); glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, -1.0f);
    glEnd(); glFlush(); }

    glRotatef用来设置opengl中绘制实体的自转方式,为我们的模型动起来做准备。比起之前的draw函数,这里多了glTexCoord2f,两参数分别为X轴坐标,Y轴坐标,用于绘制图形时指定纹理的坐标,其中x、y的坐标:0.0是纹理的左侧,1.0是纹理的右侧;0.0是纹理的底部,1.0是纹理的顶部。

    这里继续新增一个纹理初始化函数:
    void init(void){
        glClearColor(1.0, 1.0, 1.0, 0.0);            
        glCullFace(GL_BACK);                       
        glEnable(GL_CULL_FACE);                    
        glEnable(GL_TEXTURE_2D);
        LoadGLTextures(&texture[0], "111_.bmp", 1);            
    }

    使用清屏函数之后,使用glCullFace禁用多边形背面上的光照、阴影和颜色计算及操作,消除不必要的渲染计算,使用glEnable开启之前的消除操作。最后载入之前生成的纹理。

    给出我们修改以后的主函数:

    int main(int argc, char* argv[]){
        glutInit(&argc, argv);  
        glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE);   
        glutInitWindowSize(600, 600);   
        glutInitWindowPosition(100, 100); 
        glutCreateWindow("贴图立方体?");
        init();  
        LoadGLTextures(&texture[0], "111_.bmp", 1);
        glutDisplayFunc(display);
        glutReshapeFunc(reshape);             
        glutMainLoop();
        return 0;
    }

    可以注意到新增了初始化函数与纹理载入函数,结果为:

     

    看起来还只是一个平面图?为了让它转起来,写入鼠标响应函数:

    void RotateRect() {
        xrot += 1.0f;   
        glutPostRedisplay();   
        yrot += 1.0f;
        glutPostRedisplay();
        zrot += 1.0f;
        glutPostRedisplay();
        Sleep(10);
    }
    
    
    void OnMouse(int button, int state, int x, int y){
        if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN){
            glutIdleFunc(RotateRect);
        }
        if (button == GLUT_RIGHT_BUTTON && state == GLUT_DOWN){
            glutIdleFunc(NULL);
        }
    }

    在RotateRect中修改各个维度的旋转量,使用glutPostRedisplay进行重绘,为了不使它暴走旋转,调用winbase.h中的Sleep函数,变量改变一次暂停程序10毫秒。定义鼠标响应函数,左键启动,右键暂停(返回NULL),在回调函数glutIdleFunc中调用RotateRect功能。

    修改主函数:

    int main(int argc, char* argv[]){
        glutInit(&argc, argv);  //固定格式
        glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE);  
        glutInitWindowSize(600, 600);    
        glutInitWindowPosition(100, 100); 
        glutCreateWindow("贴图立方体?");
        init();  
        LoadGLTextures(&texture[0], "111_.bmp", 1);
        glutDisplayFunc(display);
        glutReshapeFunc(reshape);                
        glutMouseFunc(&OnMouse);  
        glutMainLoop();
        return 0;
    }

    到这里 纹理映射实验就基本结束了。

    参考博客:

    https://blog.csdn.net/dcrmg/article/details/53223680?utm_source=blogxgwz2

    https://www.cnblogs.com/OctoptusLian/p/7366844.html#commentform

    https://blog.csdn.net/tyxkzzf/article/details/40921713

    https://blog.csdn.net/yangmeng900816/article/details/46816007

  • 相关阅读:
    家庭养花秘笈1000问
    生活中来3000例·健康篇
    生命的奥秘百科(套装共10册)
    海军陆战队6:太空战舰
    历史文明探秘百科(套装共10册)
    中医养生知识读本
    上工养生话刮痧
    古法艾灸
    钻井液处理剂及其作用原理
    重金属污泥处理技术与管理
  • 原文地址:https://www.cnblogs.com/missouter/p/12878363.html
Copyright © 2011-2022 走看看