zoukankan      html  css  js  c++  java
  • OpenGL学习(4)——纹理

    拖了半个多月的博客,这次学习如何使用纹理(Texture)贴图来实现更多的细节。

    生成纹理对象

    和创建VAO、VBO方法类似,调用glGenTextures函数。

    glGenTextures(1, &texture);
    

    绑定并配置纹理对象

    glBindTexture(GL_TEXTURE_2D, texture);
    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);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    

    调用四次glTexParameter函数:

    1. 2D纹理坐标的横坐标和纵坐标范围都在[0, 1],当设置的横坐标超出这个范围,则以GL_REPEAT的形式显示纹理;
    2. 同理,当设置的纵坐标范围超出[0, 1]范围,同样以GL_REPEAT的形式显示纹理。不同选项下纹理的显示效果如下:
    3. 当纹理被缩小时,一个屏幕像素对应多个纹理像素,采用GL_LINEAR即线性过滤进行采样;
    4. 当纹理被放大时,一个纹理像素对应多个屏幕像素,同样采用线性过滤进行采样。可选择的过滤方式还有GL_NEAREST,GL_NEAREST_MIPMAP_NEAREST,GL_LINEAR_MIPMAP_NEAREST,GL_NEAREST_MIPMAP_LINEAR和GL_LINEAR_MIPMAP_LINEAR,需要注意的是,后四种过滤方式只能使用在纹理被缩小的情况下。

    加载纹理图像

    首先添加库stb_image.h

    #define STB_IMAGE_IMPLEMENTATION
    #include "stb_image.h"
    

    调用库函数stbi_load加载图片,五个参数分别设置图片的路径,输出图片的宽度,高度,默认颜色通道数和自定义颜色通道数。这里设置自定义颜色通道数为0,表示输出图片默认的颜色通道数。

    unsigned char *imageData = stbi_load("/path1/xxx.jpg", &width, &height, &nrChannel, 0);
    

    若图片加载成功,调用glTexImage2D函数使用被加载的图片参数生成2D纹理图像。注意第三和第七个参数虽然都设置成GL_RGB,但含义不同。第三个参数设置OpenGL存储纹理数据的格式,而第七个参数表示图片被加载时生成的数据格式。
    之后调用glGenerateMipmap函数根据纹理图像生成Mipmap。

    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, imageData);
    glGenerateMipmap(GL_TEXTURE_2D);
    

    最后释放内存并解绑纹理对象。

    stbi_image_free(imageData);
    glBindTexture(GL_TEXTURE_2D, 0);
    

    配置顶点属性指针

    在顶点数据中添加纹理坐标,四个顶点的纹理坐标分别为(2.0f, 2.0f),(0.0f, 2.0f),(2.0f, 0.0f)和(0.0f, 0.0f):

    float vertices[] = {0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 2.0f, 2.0f,
                        -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 2.0f,
                        0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 1.0f, 2.0f, 0.0f,
                        -0.5f, -0.5f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f};
    

    配置顶点属性指针的函数调用和参数说明参见《绘制三角形》

    glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8*sizeof(float), (void*)(6*sizeof(float))); //配置纹理坐标
    glEnableVertexAttribArray(2); //使能属性位置2
    

    编写Shader

    首先是Vertex Shader,Tex的属性位置为2,表示纹理坐标:

    #version 330 core
    layout (location = 0) in vec3 Pos;
    layout (location = 1) in vec3 Col;
    layout (location = 2) in vec2 Tex;
    out vec4 Color;
    out vec2 texCoord;
    void main()
    {
        gl_Position = vec4(Pos, 1.0f);
        Color = vec4(Col, 1.0f);
        texCoord = Tex;
    }
    

    添加sampler2D类型的变量,使Fragment Shader能够访问之前创建的纹理对象:

    #version 330 core
    out vec4 FragColor;
    in vec4 Color;
    in vec2 texCoord;
    uniform sampler2D texSampler;
    void main()
    {
        FragColor = texture(texSampler, texCoord) * Color;
    }
    

    渲染循环

    ourShader.use();
    glBindVertexArray(VAO);
    glBindTexture(GL_TEXTURE_2D, texture);
    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
    


    Texture Unit

    那么该如何同时生成并使用两个纹理呢?
    在编写Fragment Shader时,定义了uniform sampler2D类型的变量,可以调用glGetUniformLocation函数和glUniform函数对它进行赋值,来设置纹理的位置信息,即Texture Unit。通过glActiveTexture函数激活不同的Texture Unit,并绑定不同的纹理对象,在Fragment Shader中就可以同时对不同的纹理图像进行采样,混合并输出颜色。
    首先绑定并配置另一个纹理对象:

    glBindTexture(GL_TEXTURE_2D, texture[1]);
    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);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    

    调用库函数stbi_load加载另一张图片,根据图片参数生成纹理图像和Mipmap:

    imageData = stbi_load("/path2/xxx.png", &width, &height, &nrChannel, 0);
    
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, imageData); //因为我这里加载的图片有alpha通道,所以第七个参数设置成GL_RGBA
    glGenerateMipmap(GL_TEXTURE_2D);
    stbi_image_free(imageData);
    glBindTexture(GL_TEXTURE_2D, 0);
    

    修改Fragment Shader,定义两个sampler2D变量,并调用mix函数进行颜色混合,即第一个纹理采样颜色的80%和第二个纹理采样颜色的20%:

    #version 330 core
    out vec4 FragColor;
    in vec4 Color;
    in vec2 texCoord;
    uniform sampler2D texSampler1;
    uniform sampler2D texSampler2;
    void main()
    {
        FragColor = mix(texture(texSampler1, texCoord)*Color, texture(texSampler2, texCoord), 0.2);
    }
    

    在main函数中对Fragment Shader中的uniform进行赋值,分别设置位置信息即Texture Unit为0和1:

    ourShader.use();
    glUniform1i(glGetUniformLocation(ourShader.ID, "texSampler1"), 0);
    glUniform1i(glGetUniformLocation(ourShader.ID, "texSampler2"), 1);
    

    在渲染循环中激活Texture Unit,并绑定对应的纹理对象:

    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, texture[0]);
    glActiveTexture(GL_TEXTURE1);
    glBindTexture(GL_TEXTURE_2D, texture[1]);
    

    执行结果:

    完整代码如下:

    #include "shader.h"
    #include <GLFW/glfw3.h>
    #include <cmath>
    #define STB_IMAGE_IMPLEMENTATION
    #include "stb_image.h"
    
    using namespace std;
    
    /*
    void frambuffer_size_callback(GLFWwindow *window, int width, int height)
    {
        glViewport(0, 0, width, height);
    }
    */
    void processInput(GLFWwindow* window)
    {
        //check if ESCAPE is pressed
        if(glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
            glfwSetWindowShouldClose(window, true);
    }
    
    int main()
    {
        //initialize GLFW
        if(!glfwInit())
            return -1;
    
        //configure GLFW
        glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
        glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
        glfwWindowHint(GLFW_OPENGL_PROFILE,GLFW_OPENGL_CORE_PROFILE);
    
        //creat a window object
        const unsigned int window_width = 800;
        const unsigned int window_height = 600;
        GLFWwindow *window = glfwCreateWindow(window_width, window_height, "OpenGL_Demo", NULL, NULL);
        if (window == NULL){
            cout << "Failed to create GLFW window" << endl;
            glfwTerminate();
            return -1;
        }
        glfwMakeContextCurrent(window);
    
        //initialize GLAD to manage function pointers for OpenGL
        if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)){
            cout << "Failed to initialize GLAD" << endl;
            return -1;
        }
    
        //set width and height of Viewport
        glViewport(0, 0, window_width, window_height);
    
        Shader ourShader;
        ourShader.shader("/home/yi/Test/GL_test/vShader.vs", "/home/yi/Test/GL_test/fShader.fs");
        //glfwSetFramebufferSizeCallback(window, frambuffer_size_callback);
    
        //coordnate (x,y,z) of vertices
        float vertices[] = {0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 2.0f, 2.0f,
                            -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 2.0f,
                            0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 1.0f, 2.0f, 0.0f,
                            -0.5f, -0.5f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f};
        //indices of vertices
        unsigned int indices[] = {0, 1, 2, 1, 2, 3};
        //vertex buffer object(VBO)
        unsigned int VBO;
        //element buffuer object(EBO)
        unsigned int EBO;
        //vertex array object(VAO)
        unsigned int VAO;
        //texture object
        unsigned int texture[2];
    
        //generate VAO, VBO, EBO, texture
        glGenVertexArrays(1, &VAO);
        glGenBuffers(1, &VBO);
        glGenBuffers(1, &EBO);
        glGenTextures(2, texture);
    
        //bind and configure VAO, VBO and EBO
        glBindVertexArray(VAO);
        glBindBuffer(GL_ARRAY_BUFFER, VBO);
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
        glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
    
        //configure texture1
        glBindTexture(GL_TEXTURE_2D, texture[0]);
        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);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        stbi_set_flip_vertically_on_load(true);
        int width, height, nrChannel;
        unsigned char *imageData = stbi_load("/path1/xxx.jpg", &width, &height, &nrChannel, 0);
        if(imageData){
            glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, imageData);
            glGenerateMipmap(GL_TEXTURE_2D);
            cout << "Successful to load texture1" << endl;
        }
        else{
            cout << "Failed to load texture1" << endl;
            return -1;
        }
        stbi_image_free(imageData);
        glBindTexture(GL_TEXTURE_2D, 0);
    
        //configure texture2
        glBindTexture(GL_TEXTURE_2D, texture[1]);
        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);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        imageData = stbi_load("/path2/xxx.png", &width, &height, &nrChannel, 0);
        if(imageData){
            glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, imageData);
            glGenerateMipmap(GL_TEXTURE_2D);
            cout << "Successful to load texture2" << endl;
        }
        else{
            cout << "Failed to load texture2" << endl;
            return -1;
        }
        stbi_image_free(imageData);
        glBindTexture(GL_TEXTURE_2D, 0);
    
        //set which texture unit each sampler in fragment shader belongs to
        ourShader.use();
        glUniform1i(glGetUniformLocation(ourShader.ID, "texSampler1"), 0);
        glUniform1i(glGetUniformLocation(ourShader.ID, "texSampler2"), 1);
    
        //link vertex attributes
        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8*sizeof(float), (void*)0);
        glEnableVertexAttribArray(0);
        glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8*sizeof(float), (void*)(3*sizeof(float)));
        glEnableVertexAttribArray(1);
        glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8*sizeof(float), (void*)(6*sizeof(float)));
        glEnableVertexAttribArray(2);
    
        glBindBuffer(GL_ARRAY_BUFFER, 0);
        glBindVertexArray(0);
    
        glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
    
        //render loop
        while(!glfwWindowShouldClose(window)){
    
            processInput(window);
            glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
            glClear(GL_COLOR_BUFFER_BIT);
            ourShader.use();
            glBindVertexArray(VAO);
            glActiveTexture(GL_TEXTURE0);
            glBindTexture(GL_TEXTURE_2D, texture[0]);
            glActiveTexture(GL_TEXTURE1);
            glBindTexture(GL_TEXTURE_2D, texture[1]);
            glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
            glfwSwapBuffers(window);
            glfwPollEvents();
        }
    
        //clear resource
        glfwTerminate();
        return 0;
    }
    
  • 相关阅读:
    Linux忘了root的密码怎么办
    缩略图的实现
    ASP.NET程序编写注意 (转载)
    太极拳
    Linux系统管理技巧大荟萃
    茶经(转载)
    datagrid的显示控制
    太极功
    Linux下硬盘分区详解
    Tomcat4.0中文问题简单解决方法
  • 原文地址:https://www.cnblogs.com/yiqian/p/10867348.html
Copyright © 2011-2022 走看看