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

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

本系列历史文
OpengGL教程(一)—OpenGL环境的配置(GLFW3,GLAD)
OpengGL教程(二)—渲染一个简单的窗体
OpengGL教程(三)—使用VAO和VBO方式绘制三角形
OpengGL教程(四)—使用EBO方式绘制矩形

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

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

运行截图:
在这里插入图片描述

这样我就将一个纹理贴在了指定的区域,如果我想要实现两个纹理叠加在一起的效果要如何做呢?

修改FragmentShader.glsl

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

void main()
{
	FragColor = mix(texture(textureClass,ourTexture),texture(textureClass2,ourTexture),0.2f);
}

线性插值: m a x ( x , y , n ) = = > x ∗ ( 1 − n ) + y ∗ a 线性插值:max(x,y,n) ==> x * (1 - n) + y * a 线性插值:max(x,y,n)==>x(1n)+ya

解释:0.2会返回80%的第一个输入颜色和20%的第二个输入颜色,即返回两个纹理的混合色。

为了简化代码我们可以将纹理对象封装为一个函数接口,我将这个函数放到了GlslDealConfig类中

// 纹理加载函数
GLuint GlslDealConfig::loadTexture(const std::string& path, GLenum format)
{
    GLuint texture;
    glGenTextures(1, &texture);
    glBindTexture(GL_TEXTURE_2D, texture);

    // 设置纹理参数
    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);

    // 加载纹理数据
    int width, height, nrChannels;
    stbi_set_flip_vertically_on_load(true);
    unsigned char* data = stbi_load(path.c_str(), &width, &height, &nrChannels, 0);

    if (data) {
        glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data);
        glGenerateMipmap(GL_TEXTURE_2D);
    } else {
        LOGE("Failed to load texture at %s", path.c_str());
    }

    stbi_image_free(data);
    glBindTexture(GL_TEXTURE_2D, 0);
    return texture;
}

修改后的main.cpp

#include <iostream>
#include <string>
#include "glew.h"
#include "glfw3.h"
#include "glm/glm.hpp"
#include "glm/gtc/matrix_transform.hpp"
#include "glm/gtc/type_ptr.hpp"


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

// 顶点数据
GLfloat vertices_1[] = {
    0.0f, 800.0f, 0.0f,
    0.0f, 0.0f, 0.0f,
    800.0f, 0.0f, 0.0f,
    800.0f, 800.0f, 0.0f
};

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

GLint indices[] = {
    0, 1, 2,
    0, 2, 3
};

// 主函数
int main() {
    GlslDealConfig glslConfig;

    // 初始化 GLFW
    if (!glfwInit()) {
        LOGE("Failed to initialize GLFW");
        return -1;
    }

    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", nullptr, nullptr);
    if (!window) {
        LOGE("Failed to create GLFW window");
        glfwTerminate();
        return -1;
    }
    glfwMakeContextCurrent(window);

    // 初始化 GLEW
    GLenum err = glewInit();
    if (err != GLEW_OK) {
        LOGE("Failed to initialize GLEW: %s", reinterpret_cast<const char*>(glewGetErrorString(err)));
        return -1;
    }

    glfwSwapInterval(1);

    // 加载着色器
    std::string vertexShaderCode = glslConfig.ReadGlslFile("/home/ryan/zxp/Rendering/demo/glsl/Texture/GLSL2/VertexShader.glsl");
    std::string fragmentShaderCode = glslConfig.ReadGlslFile("/home/ryan/zxp/Rendering/demo/glsl/Texture/GLSL2/FragmentShader.glsl");

    GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
    GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
    const GLchar* vShaderSource = vertexShaderCode.c_str();
    const GLchar* fShaderSource = fragmentShaderCode.c_str();

    glShaderSource(vertexShader, 1, &vShaderSource, nullptr);
    glShaderSource(fragmentShader, 1, &fShaderSource, nullptr);

    glCompileShader(vertexShader);
    glCompileShader(fragmentShader);
    glslConfig.CheckShaderCompileV(vertexShader);
    glslConfig.CheckShaderCompileF(fragmentShader);

    GLuint shaderProgram = glCreateProgram();
    glAttachShader(shaderProgram, vertexShader);
    glAttachShader(shaderProgram, fragmentShader);
    glLinkProgram(shaderProgram);
    glslConfig.CheckProgmaLinkStatus(shaderProgram);

    glDeleteShader(vertexShader);
    glDeleteShader(fragmentShader);

    // 配置 VAO, VBO, EBO
    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(indices), indices, GL_STATIC_DRAW);

    // 加载纹理
    GLuint texture[2];
    texture[0] = glslConfig.loadTexture("/home/ryan/zxp/Rendering/demo/resource/brickwall.jpg",   GL_RGB);
    texture[1] = glslConfig.loadTexture("/home/ryan/zxp/Rendering/demo/resource/awesomeface.png", GL_RGBA);

    // 设置投影矩阵
    glm::mat4 projection = glm::ortho(0.0f, 800.0f, 0.0f, 800.0f, -1.0f, 1.0f);
    glUseProgram(shaderProgram);
    GLuint projLoc = glGetUniformLocation(shaderProgram, "projection");
    glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(projection));

    // 激活纹理单元
    glActiveTexture(GL_TEXTURE3);
    GLuint textureLocation = glGetUniformLocation(shaderProgram, "textureClass");
    glUniform1i(textureLocation, 3);

    glActiveTexture(GL_TEXTURE6);
    GLuint textureLocation2 = glGetUniformLocation(shaderProgram, "textureClass2");
    glUniform1i(textureLocation2, 6);

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

        glUseProgram(shaderProgram);
        glBindVertexArray(VAO);
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);

        glActiveTexture(GL_TEXTURE3);
        glBindTexture(GL_TEXTURE_2D, texture[0]);

        glActiveTexture(GL_TEXTURE6);
        glBindTexture(GL_TEXTURE_2D, texture[1]);

        glDrawElements(GL_TRIANGLES, 6, 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(shaderProgram);

    glfwTerminate();
    return 0;
}

运行效果

在这里插入图片描述

别说,还蛮好看的伐!

感谢阅读^ _ ^
如有问题欢迎评论区交流指正,如需要对应源码资源可私聊我。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值