美文网首页
008-OpenGL纹理

008-OpenGL纹理

作者: 沉默Coder | 来源:发表于2020-07-20 14:43 被阅读0次

    什么是纹理?

    纹理可以理解为图片. 在渲染图形时需要在其编码填充图片,为了使得场景更加逼真.⽽这里使⽤的图片,就是常说的纹理.但是在OpenGL,我们更加习惯叫纹理,而不是图⽚.

    纹理相关的函数

    载入纹理

    void glTexImage2D(GLenum target,GLint level,GLint internalformat,GLsizei width,GLsizei height,GLint border,GLenum format,GLenum type,void * data);
    参数说明:

    • 参数1:纹理维度
    • 参数2:mip贴图层次
    • 参数3:纹理单元存储的颜色成分(从读取像素图是获得)
    • 参数4:加载纹理宽
    • 参数5:加载纹理高
    • 参数6:加载纹理的深度
    • 参数7:像素数据的数据类型(GL_UNSIGNED_BYTE,每个颜色分量都是一个8位无符号整数)
    • 参数8:指向纹理图像数据的指针
    纹理对象

    分配纹理对象
    void glGenTextures(GLsizei n,GLuint * textTures);
    参数说明:

    • 参数1:纹理的个数
    • 参数2:纹理对象指针

    绑定纹理状态
    void glBindTexture(GLenum target,GLunit texture);
    参数说明:

    • 参数1:纹理状态(2D)
    • 参数2:纹理对象

    删除绑定纹理对象
    void glDeleteTextures(GLsizei n,GLuint *textures);
    参数说明:

    • 参数1:纹理个数
    • 参数2:纹理对象指针

    测试纹理对象是否有效
    GLboolean glIsTexture(GLuint texture);
    参数说明:

    • 参数1:纹理对象

    返回值:
    如果texture是⼀个已经分配空间的纹理对象,那么这个函数会返回GL_TRUE,否则会返回GL_FALSE。

    设置纹理参数
    glTexParameterf(GLenum target,GLenum pname,GLFloat param);
    glTexParameteri(GLenum target,GLenum pname,GLint param);
    glTexParameterfv(GLenum target,GLenum pname,GLFloat *param);
    glTexParameteriv(GLenum target,GLenum pname,GLint *param);
    

    参数说明:

    • 参数1:target,指定这些参数将要应用在那个纹理模式上,比如GL_TEXTURE_1D、GL_TEXTURE_2D、GL_TEXTURE_3D。
    • 参数2:pname,指定需要设置那个纹理参数
    • 参数3:param,设定特定的纹理参数的值

    纹理过滤方式

    过滤方式
    • 邻近过滤(GL_NEAREST):也就是说取当前像素点最接近的纹理坐标的值
    • 线性过滤(GL_LINEAR):取当前像素点相邻的纹理值然后通过计算得到一个平均值

    两种纹理过滤方式的比较

    过滤方式比较
    纹理参数的设置函数
    //纹理放大时,使用线性过滤
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR) ;
    //纹理缩小时,使用邻近过滤
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
    

    纹理的环绕方式

    常用的环绕方式有:

    环绕方式 描述
    GL_REPEAT 对纹理的默认行为,重复纹理图像
    GL_MIRRORED_REPEAT 和GL_REPEAT一样,但是每次重复都是镜像放置的
    GL_CLAMP_TO_EDGE 纹理坐标会被约束在0-1之间,超出的部分会重复纹理坐标的边缘,产生一种边缘被拉伸的效果
    GL_CLAMP_TO_BORDER 超出坐标为用户指定的边缘颜色

    不同环绕方式的效果:


    环绕方式
    设置环绕方式
    glTextParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAR_S,GL_CLAMP_TO_EDGE);
    glTextParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAR_T,GL_CLAMP_TO_EDGE);
    

    参数说明:

    • 参数1:GL_TEXTURE_1D、GL_TEXTURE_2D、GL_TEXTURE_3D
    • 参数2:GL_TEXTURE_WRAP_S、GL_TEXTURE_T、GL_TEXTURE_R,针对s,t,r坐标
    • 参数3:GL_REPEAT、GL_CLAMP、GL_CLAMP_TO_EDGE、GL_CLAMP_TO_BORDER
    OpenGL像素格式
    像素格式

    纹理坐标

    纹理坐标的取值在 0-1 之间,如下图:


    纹理坐标

    上图中从(0,0)(1,0)(0.5,1)三个坐标可以取出该纹理的一个三角形

    例子

    小面我们来使用纹理完成一个小例子,最终结果如图:


    Demo

    完整代码如下

    #include "GLTools.h"
    #include "GLShaderManager.h"
    #include "GLFrustum.h"
    #include "GLBatch.h"
    #include "GLFrame.h"
    #include "GLMatrixStack.h"
    #include "GLGeometryTransform.h"
    
    #ifdef __APPLE__
    #include <glut/glut.h>
    #else
    #define FREEGLUT_STATIC
    #include <GL/glut.h>
    #endif
    
    GLShaderManager     shaderManager;
    GLMatrixStack       modelViewMatrix;
    GLMatrixStack       projectionMatrix;
    GLFrame             cameraFrame;
    GLFrame             objectFrame;
    GLFrustum           viewFrustum;
    
    GLBatch             pyramidBatch;
    
    //纹理变量,一般使用无符号整型
    GLuint              textureID;
    
    GLGeometryTransform transformPipeline;
    M3DMatrix44f        shadowMatrix;
    
    //绘制金字塔
    void MakePyramid(GLBatch& pyramidBatch)
    {
        /*1、通过pyramidBatch组建三角形批次
          参数1:类型
          参数2:顶点数
          参数3:这个批次中将会应用1个纹理
          注意:如果不写这个参数,默认为0。
         */
        pyramidBatch.Begin(GL_TRIANGLES, 18, 1);
        
        /***前情导入
         
         2)设置纹理坐标
         void MultiTexCoord2f(GLuint texture, GLclampf s, GLclampf t);
         参数1:texture,纹理层次,对于使用存储着色器来进行渲染,设置为0
         参数2:s:对应顶点坐标中的x坐标
         参数3:t:对应顶点坐标中的y
         (s,t,r,q对应顶点坐标的x,y,z,w)
         
         pyramidBatch.MultiTexCoord2f(0,s,t);
         
         3)void Vertex3f(GLfloat x, GLfloat y, GLfloat z);
          void Vertex3fv(M3DVector3f vVertex);
         向三角形批次类添加顶点数据(x,y,z);
          pyramidBatch.Vertex3f(-1.0f, -1.0f, -1.0f);
        
         */
        
        //塔顶
        M3DVector3f vApex = { 0.0f, 1.0f, 0.0f };
        M3DVector3f vFrontLeft = { -1.0f, -1.0f, 1.0f };
        M3DVector3f vFrontRight = { 1.0f, -1.0f, 1.0f };
        M3DVector3f vBackLeft = { -1.0f,  -1.0f, -1.0f };
        M3DVector3f vBackRight = { 1.0f,  -1.0f, -1.0f };
        
        //金字塔底部
        //底部的四边形 = 三角形X + 三角形Y
        //三角形X = (vBackLeft,vBackRight,vFrontRight)
        //vBackLeft
        pyramidBatch.MultiTexCoord2f(0, 0.0f, 0.0f);
        pyramidBatch.Vertex3fv(vBackLeft);
        
        //vBackRight
        pyramidBatch.MultiTexCoord2f(0, 1.0f, 0.0f);
        pyramidBatch.Vertex3fv(vBackRight);
        
        //vFrontRight
        pyramidBatch.MultiTexCoord2f(0, 1.0f, 1.0f);
        pyramidBatch.Vertex3fv(vFrontRight);
        
        
        //三角形Y =(vFrontLeft,vBackLeft,vFrontRight)
        //vFrontLeft
        pyramidBatch.MultiTexCoord2f(0, 0.0f, 1.0f);
        pyramidBatch.Vertex3fv(vFrontLeft);
        
        //vBackLeft
        pyramidBatch.MultiTexCoord2f(0, 0.0f, 0.0f);
        pyramidBatch.Vertex3fv(vBackLeft);
        
        //vFrontRight
        pyramidBatch.MultiTexCoord2f(0, 1.0f, 1.0f);
        pyramidBatch.Vertex3fv(vFrontRight);
    
        
        // 金字塔前面
        //三角形:(Apex,vFrontLeft,vFrontRight)
        pyramidBatch.MultiTexCoord2f(0, 0.5f, 1.0f);
        pyramidBatch.Vertex3fv(vApex);
    
        pyramidBatch.MultiTexCoord2f(0, 0.0f, 0.0f);
        pyramidBatch.Vertex3fv(vFrontLeft);
    
        pyramidBatch.MultiTexCoord2f(0, 1.0f, 0.0f);
        pyramidBatch.Vertex3fv(vFrontRight);
        
        //金字塔左边
        //三角形:(vApex, vBackLeft, vFrontLeft)
        pyramidBatch.MultiTexCoord2f(0, 0.5f, 1.0f);
        pyramidBatch.Vertex3fv(vApex);
        
        pyramidBatch.MultiTexCoord2f(0, 1.0f, 0.0f);
        pyramidBatch.Vertex3fv(vBackLeft);
        
        pyramidBatch.MultiTexCoord2f(0, 0.0f, 0.0f);
        pyramidBatch.Vertex3fv(vFrontLeft);
        
        //金字塔右边
        //三角形:(vApex, vFrontRight, vBackRight)
        pyramidBatch.MultiTexCoord2f(0, 0.5f, 1.0f);
        pyramidBatch.Vertex3fv(vApex);
        
        pyramidBatch.MultiTexCoord2f(0, 1.0f, 0.0f);
        pyramidBatch.Vertex3fv(vFrontRight);
    
        pyramidBatch.MultiTexCoord2f(0, 0.0f, 0.0f);
        pyramidBatch.Vertex3fv(vBackRight);
        
        //金字塔后边
        //三角形:(vApex, vBackRight, vBackLeft)
        pyramidBatch.MultiTexCoord2f(0, 0.5f, 1.0f);
        pyramidBatch.Vertex3fv(vApex);
        
        pyramidBatch.MultiTexCoord2f(0, 0.0f, 0.0f);
        pyramidBatch.Vertex3fv(vBackRight);
        
        pyramidBatch.MultiTexCoord2f(0, 1.0f, 0.0f);
        pyramidBatch.Vertex3fv(vBackLeft);
        
        //结束批次设置
        pyramidBatch.End();
    }
    
    // 将TGA文件加载为2D纹理。
    bool LoadTGATexture(const char *szFileName, GLenum minFilter, GLenum magFilter, GLenum wrapMode)
    {
        GLbyte *pBits;
        int nWidth, nHeight, nComponents;
        GLenum eFormat;
        
        //1、读纹理位,读取像素
        //参数1:纹理文件名称
        //参数2:文件宽度地址
        //参数3:文件高度地址
        //参数4:文件组件地址
        //参数5:文件格式地址
        //返回值:pBits,指向图像数据的指针
        
        pBits = gltReadTGABits(szFileName, &nWidth, &nHeight, &nComponents, &eFormat);
        if(pBits == NULL)
            return false;
        
        //2、设置纹理参数
        //参数1:纹理维度
        //参数2:为S/T坐标设置模式
        //参数3:wrapMode,环绕模式
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrapMode);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrapMode);
        
        //参数1:纹理维度
        //参数2:线性过滤
        //参数3: 缩小/放大过滤方式.
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, minFilter);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, magFilter);
        
    
        //3.载入纹理
        //参数1:纹理维度
        //参数2:mip贴图层次
        //参数3:纹理单元存储的颜色成分(从读取像素图是获得)
        //参数4:加载纹理宽
        //参数5:加载纹理高
        //参数6:加载纹理的深度
        //参数7:像素数据的数据类型(GL_UNSIGNED_BYTE,每个颜色分量都是一个8位无符号整数)
        //参数8:指向纹理图像数据的指针
        
        glTexImage2D(GL_TEXTURE_2D, 0, nComponents, nWidth, nHeight, 0,
                     eFormat, GL_UNSIGNED_BYTE, pBits);
        
        //使用完毕释放pBits
        free(pBits);
        
        //只有minFilter 等于以下四种模式,才可以生成Mip贴图
        //GL_NEAREST_MIPMAP_NEAREST具有非常好的性能,并且闪烁现象非常弱
        //GL_LINEAR_MIPMAP_NEAREST常常用于对游戏进行加速,它使用了高质量的线性过滤器
        //GL_LINEAR_MIPMAP_LINEAR 和GL_NEAREST_MIPMAP_LINEAR 过滤器在Mip层之间执行了一些额外的插值,以消除他们之间的过滤痕迹。
        //GL_LINEAR_MIPMAP_LINEAR 三线性Mip贴图。纹理过滤的黄金准则,具有最高的精度。
        if(minFilter == GL_LINEAR_MIPMAP_LINEAR ||
           minFilter == GL_LINEAR_MIPMAP_NEAREST ||
           minFilter == GL_NEAREST_MIPMAP_LINEAR ||
           minFilter == GL_NEAREST_MIPMAP_NEAREST)
        //4.纹理生成所有的Mip层
        //参数:GL_TEXTURE_1D、GL_TEXTURE_2D、GL_TEXTURE_3D
        glGenerateMipmap(GL_TEXTURE_2D);
     
        return true;
    }
    
    void SetupRC()
    {
        //1.
        glClearColor(0.7f, 0.7f, 0.7f, 1.0f );
        shaderManager.InitializeStockShaders();
        
        //2.
        glEnable(GL_DEPTH_TEST);
        
        //3.
        //分配纹理对象 参数1:纹理对象个数,参数2:纹理对象指针
        glGenTextures(1, &textureID);
        
        //绑定纹理状态 参数1:纹理状态2D 参数2:纹理对象
        glBindTexture(GL_TEXTURE_2D, textureID);
        
        //将TGA文件加载为2D纹理。
        //参数1:纹理文件名称
        //参数2&参数3:需要缩小&放大的过滤器
        //参数4:纹理坐标环绕模式
        LoadTGATexture("brick.tga", GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR, GL_REPEAT);
        
        //4.创造金字塔pyramidBatch
        MakePyramid(pyramidBatch);
        
        //5.
        /**相机frame MoveForward(平移)
        参数1:Z,深度(屏幕到图形的Z轴距离)
         */
        cameraFrame.MoveForward(-10);
    }
    
    // 清理…例如删除纹理对象
    void ShutdownRC(void)
    {
        glDeleteTextures(1, &textureID);
    }
    
    void RenderScene(void)
    {
        //1.颜色值&光源位置
        static GLfloat vLightPos [] = { 1.0f, 1.0f, 0.0f };
        static GLfloat vWhite [] = { 1.0f, 1.0f, 1.0f, 1.0f };
        
        //2.清理缓存区
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
        
        //3.当前模型视频压栈
        modelViewMatrix.PushMatrix();
        
        //添加照相机矩阵
        M3DMatrix44f mCamera;
        //从camraFrame中获取一个4*4的矩阵
        cameraFrame.GetCameraMatrix(mCamera);
        //矩阵乘以矩阵堆栈顶部矩阵,相乘结果存储到堆栈的顶部 将照相机矩阵 与 当前模型矩阵相乘 压入栈顶
        modelViewMatrix.MultMatrix(mCamera);
        
        //创建mObjectFrame矩阵
        M3DMatrix44f mObjectFrame;
        //从objectFrame中获取矩阵,objectFrame保存的是特殊键位的变换矩阵
        objectFrame.GetMatrix(mObjectFrame);
        //矩阵乘以矩阵堆栈顶部矩阵,相乘结果存储到堆栈的顶部 将世界变换矩阵 与 当前模型矩阵相乘 压入栈顶
        modelViewMatrix.MultMatrix(mObjectFrame);
        
        //4.绑定纹理,因为我们的项目中只有一个纹理。如果有多个纹理。绑定纹理很重要
        glBindTexture(GL_TEXTURE_2D, textureID);
        
        //5.纹理替换矩阵着色器
         /*
         参数1:GLT_SHADER_TEXTURE_REPLACE(着色器标签)
         参数2:模型视图投影矩阵
         参数3:纹理层
         */
        
        shaderManager.UseStockShader(GLT_SHADER_TEXTURE_REPLACE, transformPipeline.GetModelViewProjectionMatrix(), 0);
        
        //pyramidBatch 绘制
        pyramidBatch.Draw();
        
        //模型视图出栈,恢复矩阵(push一次就要pop一次)
        modelViewMatrix.PopMatrix();
        
        //6.交换缓存区
        glutSwapBuffers();
    }
    
    void SpecialKeys(int key, int x, int y)
    {
        if(key == GLUT_KEY_UP)
            objectFrame.RotateWorld(m3dDegToRad(-5.0f), 1.0f, 0.0f, 0.0f);
        
        if(key == GLUT_KEY_DOWN)
            objectFrame.RotateWorld(m3dDegToRad(5.0f), 1.0f, 0.0f, 0.0f);
        
        if(key == GLUT_KEY_LEFT)
            objectFrame.RotateWorld(m3dDegToRad(-5.0f), 0.0f, 1.0f, 0.0f);
        
        if(key == GLUT_KEY_RIGHT)
            objectFrame.RotateWorld(m3dDegToRad(5.0f), 0.0f, 1.0f, 0.0f);
        
        glutPostRedisplay();
    }
    
    void ChangeSize(int w, int h)
    {
        //1.设置视口
        glViewport(0, 0, w, h);
        
        //2.创建投影矩阵
        viewFrustum.SetPerspective(35.0f, float(w) / float(h), 1.0f, 500.0f);
      
        //viewFrustum.GetProjectionMatrix()  获取viewFrustum投影矩阵
        //并将其加载到投影矩阵堆栈上
        projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
        
        //3.设置变换管道以使用两个矩阵堆栈(变换矩阵modelViewMatrix ,投影矩阵projectionMatrix)
        //初始化GLGeometryTransform 的实例transformPipeline.通过将它的内部指针设置为模型视图矩阵堆栈 和 投影矩阵堆栈实例,来完成初始化
        //当然这个操作也可以在SetupRC 函数中完成,但是在窗口大小改变时或者窗口创建时设置它们并没有坏处。而且这样可以一次性完成矩阵和管线的设置。
        transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);
    }
    
    
    int main(int argc, char* argv[])
    {
        gltSetWorkingDirectory(argv[0]);
        
        glutInit(&argc, argv);
        glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_STENCIL);
        glutInitWindowSize(800, 600);
        glutCreateWindow("Pyramid");
        glutReshapeFunc(ChangeSize);
        glutSpecialFunc(SpecialKeys);
        glutDisplayFunc(RenderScene);
        
        GLenum err = glewInit();
        if (GLEW_OK != err) {
            fprintf(stderr, "GLEW Error: %s\n", glewGetErrorString(err));
            return 1;
        }
        
        SetupRC();
        glutMainLoop();
        ShutdownRC();
        return 0;
    }
    

    相关文章

      网友评论

          本文标题:008-OpenGL纹理

          本文链接:https://www.haomeiwen.com/subject/qytykktx.html