本章参考官方教程:learnopengl-cn

本章主要讲述对纹理的使用,实现功能为将纹理贴到一个三角形内。

VertexShader.glsl

#version 330 core
layout(location = 0) in vec3 aPosition;
layout(location = 1) in vec2 aTexture;
uniform mat4 projection; // 投影矩阵
out vec2 ourTexture;

void main()
{
    gl_Position = projection * vec4(aPosition,1.0f);
    ourTexture  = aTexture;
}

FragmentShader.glsl

#version 330 core
in vec2 ourTexture;
out vec4 FragColor;
uniform sampler2D textureClass;

void main()
{
	FragColor = texture(textureClass,ourTexture);
}

对于第三章中使用的GlslDealConfig这里追加了三个功能接口

void CheckShaderCompileV(GLuint vertexShader);   //检查顶点着色器的编译检查
    void CheckShaderCompileF(GLuint vertexShader);	 //检查片元着色器的编译检查
    void CheckProgmaLinkStatus(GLuint Progma);		 //检查程序对象的链接

GlslDealConfig.h

#pragma once

#include <iostream>
#include <string>
#include <fstream>
#include <sstream>

#include "glew.h"

class GlslDealConfig
{
public:
    GlslDealConfig() {}
    ~GlslDealConfig() {}
    
public:
    std::string ReadGlslFile(const std::string& filename);

    void CheckShaderCompileV(GLuint vShader);
    void CheckShaderCompileF(GLuint fShader);
    void CheckProgmaLinkStatus(GLuint Progma);

};

GlslDealConfig.cpp

#include "GlslDealConfig.h"
#include "log.h"
std::string GlslDealConfig::ReadGlslFile(const std::string& filename)
{
    std::string data;
    std::ifstream ifs(filename,std::ios::in);
    if(!ifs.is_open())
    {
        printf("open %s failed",filename.c_str());
        return data;
    }
    std::ostringstream fs;
    fs << ifs.rdbuf();
    data = fs.str();
    return data;
}

void GlslDealConfig::CheckShaderCompileV(GLuint vShader)
{
    GLint status;
    GLchar infoLog[512];
    glGetShaderiv(vShader, GL_COMPILE_STATUS,&status);
    if(status != GL_TRUE)
    {
        glGetShaderInfoLog(vShader, 512, NULL, infoLog);
        LOGE("Vertex Shader Compilation Error: %s\n", infoLog);
    }
    LOGD("Vertex Shader Compile Success: %s\n", infoLog);
}

void GlslDealConfig::CheckShaderCompileF(GLuint fShader)
{
    GLint status;
    GLchar infoLog[512];
    glGetShaderiv(fShader, GL_COMPILE_STATUS,&status);
    if(status != GL_TRUE)
    {
        glGetShaderInfoLog(fShader, 512, NULL, infoLog);
        LOGE("Fragment Shader Compilation Error: %s\n", infoLog);
    }
    LOGD("Fragment Shader Compile Success: %s\n", infoLog);
}

void GlslDealConfig::CheckProgmaLinkStatus(GLuint Progma)
{
    GLint status;
    GLchar infoLog[512];
    glGetProgramiv(Progma, GL_LINK_STATUS,&status);
    if(status != GL_TRUE)
    {
        glGetProgramInfoLog(Progma, 512, NULL, infoLog);
        LOGE("Program Link Compilation Error: %s\n", infoLog);
    }
    LOGD("Program Link Compile Success: %s\n", infoLog);
}

main.cpp

#include <iostream>

#include "glew.h"
#include "glfw3.h"

#include "glm/glm.hpp"                  // GLM 的基本数学类型,例如 glm::mat4 和 glm::vec3
#include "glm/gtc/matrix_transform.hpp" // GLM 的矩阵变换函数,例如 glm::ortho
#include "glm/gtc/type_ptr.hpp"         // 用于 glm::value_ptr 函数
#include "stb_image.h"

#include "log.h"
#include "GlslDealConfig.h"

GLfloat vertices_1[] = 
{
	0.0f,   0.0f,     0.0f,
	400.0f, 800.0f,   0.0f,
	800.0f, 0.0f,     0.0f,
	
};

GLfloat vertices_2[] = 
{
	0.0f, 0.0f,
	0.5f, 1.0f,
	1.0f, 0.0f,
};

GLint vertices_3[] = 
{
	0,1,2,
};

int main()
{
    GlslDealConfig mdeal;
    int major = 0, minor = 0, rev = 0;
    glfwGetVersion(&major, &minor, &rev);
    LOGI("glfw version %d - %d - %d", major,minor,rev);

    if (glfwInit() == GLFW_FALSE)
    {
        LOGE("glfwInit failed");
        return 0;
    }

    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

    GLFWwindow* window = glfwCreateWindow(800, 800, "texture", NULL, NULL);
    if (window == NULL)
    {
        LOGE("glfwCreateWindow failed");
        return 0;
    }

    glfwMakeContextCurrent(window);

    GLenum err = glewInit();
    if (err != GLEW_OK)
    {
        LOGE("glew init failed : %s", reinterpret_cast<const char*>(glewGetErrorString(err)));
        return 0;
    }

    glfwSwapInterval(1);

    std::string vertexShader   = mdeal.ReadGlslFile("/home/ryan/zxp/Rendering/demo/glsl/Texture/VertexShader.glsl");
    std::string fragmentShader = mdeal.ReadGlslFile("/home/ryan/zxp/Rendering/demo/glsl/Texture/FragmentShader.glsl");

    const GLchar *vData = vertexShader.c_str();
    const GLchar *fData = fragmentShader.c_str();

    GLuint vShader = glCreateShader(GL_VERTEX_SHADER);
    GLuint fShader = glCreateShader(GL_FRAGMENT_SHADER);

    glShaderSource(vShader,1,&vData,NULL);
    glShaderSource(fShader,1,&fData,NULL);

    glCompileShader(vShader);
    glCompileShader(fShader);

    mdeal.CheckShaderCompileV(vShader);
    mdeal.CheckShaderCompileF(fShader);

    GLuint Progma = glCreateProgram();
    glAttachShader(Progma,vShader);
    glAttachShader(Progma,fShader);
    glLinkProgram(Progma);

    mdeal.CheckProgmaLinkStatus(Progma);

    glDeleteShader(vShader);
    glDeleteShader(fShader);

    GLuint VAO,VBO[2],EBO;

    glGenVertexArrays(1, &VAO);
    glGenBuffers(2, VBO);
    glGenBuffers(1, &EBO);

    glBindVertexArray(VAO);

    glBindBuffer(GL_ARRAY_BUFFER, VBO[0]);
    glBufferData(GL_ARRAY_BUFFER,sizeof(vertices_1),vertices_1, GL_STATIC_DRAW);
    glVertexAttribPointer(0,3, GL_FLOAT,GL_FALSE,3*sizeof(GLfloat),(void*)0);
    glEnableVertexAttribArray(0);

    glBindBuffer(GL_ARRAY_BUFFER, VBO[1]);
    glBufferData(GL_ARRAY_BUFFER,sizeof(vertices_2),vertices_2, GL_STATIC_DRAW);
    glVertexAttribPointer(1,2, GL_FLOAT,GL_FALSE,2*sizeof(GLfloat),(void*)0);
    glEnableVertexAttribArray(1);

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER,sizeof(vertices_3),vertices_3, GL_STATIC_DRAW);


    unsigned int texture;
    glGenTextures(1, &texture);             //生成纹理对象,并将其标识符存储在 texture 变量中
    glBindTexture(GL_TEXTURE_2D, texture);  //将之前生成的纹理对象绑定到当前的纹理目标 GL_TEXTURE_2D
                                            //之后的纹理操作(如设置纹理参数、加载纹理数据等)都将作用于这个绑定的纹理对象。

    // 设置纹理参数
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);


    stbi_set_flip_vertically_on_load(true);
    int width, height, nrChannels;
    unsigned char *data = stbi_load("/home/ryan/zxp/Rendering/demo/resource/awesomeface.png", &width, &height, &nrChannels, 0);
    LOGD("width = %d, height = %d, nrChannels = %d", width, height,nrChannels);
    if (data)
    {
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
        glGenerateMipmap(GL_TEXTURE_2D);
    }
    else
    {
        LOGE("Failed to load texture");
    }
    stbi_image_free(data);


    glBindVertexArray(0);
    glBindTexture(GL_TEXTURE_2D, 0); //纹理属性配置完成取消绑定


    glm::mat4 projection = glm::ortho(0.0f, 800.0f, 0.0f, 800.0f, -1.0f, 1.0f);
    glUseProgram(Progma);
    GLuint projLoc = glGetUniformLocation(Progma, "projection");
    glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(projection));

    // 激活纹理单元 3
    glActiveTexture(GL_TEXTURE3);       //激活一个纹理单元

    int texturelocation = glGetUniformLocation(Progma,"textureClass");
    glUniform1i(texturelocation,3);     //告诉片元着色器使用那个纹理单元

    // 绘制循环
    while (!glfwWindowShouldClose(window))
    {
        glViewport(0, 0, 800, 800);
        glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);

        glUseProgram(Progma);
        glBindVertexArray(VAO);
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
        glBindTexture(GL_TEXTURE_2D, texture);

        glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_INT, 0);
        glBindVertexArray(0);
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
        glBindTexture(GL_TEXTURE_2D, 0);
        glfwSwapBuffers(window);
        glfwPollEvents();
    }

    glDeleteVertexArrays(1, &VAO);
    glDeleteBuffers(2, VBO);
    glDeleteBuffers(1, &EBO);
    glDeleteProgram(Progma);

    glfwTerminate();
    return 0;
}

注意:
1、因为OpenGL要求y轴(0.0)坐标是在图片的底部的,但是图片的y轴(0.0)坐标通常在顶部。很幸运,stb_image.h能够在图像加载时帮助我们翻转y轴,只需要在加载任何图像前加入以下语句即可.

stbi_set_flip_vertically_on_load(true);

2、设置纹理属性前榜绑定纹理对象,设置完成后解除绑定。绘制时候同理,每次绘制前绑定,绘制结束接触绑定。

3、注意纹理的通道个数

glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
        glGenerateMipmap(GL_TEXTURE_2D);

四通道为GL_RGBA三通道为GL_RGB。

运行截图:

OpengGL教程(五)---纹理的应用_OpenGL


感谢阅读^ _ ^

如有问题欢迎评论区交流指正。