学习自:

https://learnopengl-cn.github.io/01%20Getting%20started/06%20Textures/

先上一波效果图:

实际上就是:画了一个矩形,然后贴了两张图,下面是一个木窗,上面一个笑脸。

 

首先放上这次教程所需要的贴图和库文件的百度云

链接:https://pan.baidu.com/s/1Ejn65QoYW11cDukiC6ZFjg
提取码:hl93

(1)添加需要的库文件

我们本次教程的流程,用到了本地资源中的图片,读取和使用本地图片,需要使用一个新的库:stb_image.h

这里我已经下载好了,你们可以直接下载我的百度云,找到需要的头文件,然后加到自己的项目目录中。

 

 

(2)编写需要的shader类:这里的shader类和我们上一节的教程中是一样的

#ifndef SHADER_H
#define SHADER_H

#include <glad/glad.h> // 包含glad来获取所有的必须OpenGL头文件

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


class Shader
{
public:
    // 程序ID
    unsigned int ID;

    // 构造器读取并构建着色器
    Shader(const GLchar* vertexPath, const GLchar* fragmentPath);
    // 使用/激活程序
    void use();
    // uniform工具函数
    void setBool(const std::string &name, bool value) const;
    void setInt(const std::string &name, int value) const;
    void setFloat(const std::string &name, float value) const;
private:
    void checkCompileErrors(unsigned int shader, std::string type);
};

#endif
#include "shader_s.h"

Shader::Shader(const GLchar * vertexPath, const GLchar * fragmentPath)
{
    // 1. 从文件路径中获取顶点/片段着色器
    std::string vertexCode;
    std::string fragmentCode;
    std::ifstream vShaderFile;
    std::ifstream fShaderFile;
    // 保证ifstream对象可以抛出异常:
    vShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit);
    fShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit);
    try
    {
        // 打开文件
        vShaderFile.open(vertexPath);
        fShaderFile.open(fragmentPath);
        std::stringstream vShaderStream, fShaderStream;
        // 读取文件的缓冲内容到数据流中
        vShaderStream << vShaderFile.rdbuf();
        fShaderStream << fShaderFile.rdbuf();
        // 关闭文件处理器
        vShaderFile.close();
        fShaderFile.close();
        // 转换数据流到string
        vertexCode = vShaderStream.str();
        fragmentCode = fShaderStream.str();
    }
    catch (std::ifstream::failure e)
    {
        std::cout << "ERROR::SHADER::FILE_NOT_SUCCESFULLY_READ" << std::endl;
    }
    const char* vShaderCode = vertexCode.c_str();
    const char * fShaderCode = fragmentCode.c_str();
    // 2. 编译着色器
    unsigned int vertex, fragment;
    // 顶点着色器    vs
    vertex = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vertex, 1, &vShaderCode, NULL);
    glCompileShader(vertex);
    checkCompileErrors(vertex, "VERTEX");
    // 片段着色器    fs
    fragment = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fragment, 1, &fShaderCode, NULL);
    glCompileShader(fragment);
    checkCompileErrors(fragment, "FRAGMENT");
    // 着色器程序
    ID = glCreateProgram();
    glAttachShader(ID, vertex);
    glAttachShader(ID, fragment);
    glLinkProgram(ID);
    checkCompileErrors(ID, "PROGRAM");
    // 删除着色器,它们已经链接到我们的程序中了,已经不再需要了    glDeleteShader(vertex);
    glDeleteShader(fragment);
}

void Shader::use()
{
    glUseProgram(ID);
}

void Shader::setBool(const std::string & name, bool value) const
{
    glUniform1i(glGetUniformLocation(ID, name.c_str()), (int)value);
}

void Shader::setInt(const std::string & name, int value) const
{
    glUniform1i(glGetUniformLocation(ID, name.c_str()), value);
}

void Shader::setFloat(const std::string & name, float value) const
{
    glUniform1f(glGetUniformLocation(ID, name.c_str()), value);
}

void Shader::checkCompileErrors(unsigned int shader, std::string type)
{
    int success;
    char infoLog[1024];
    if (type != "PROGRAM")
    {
        glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
        if (!success)
        {
            glGetShaderInfoLog(shader, 1024, NULL, infoLog);
            std::cout << "ERROR::SHADER_COMPILATION_ERROR of type: " << type << "\n" << infoLog << "\n -- --------------------------------------------------- -- " << std::endl;
        }
    }
    else
    {
        glGetProgramiv(shader, GL_LINK_STATUS, &success);
        if (!success)
        {
            glGetProgramInfoLog(shader, 1024, NULL, infoLog);
            std::cout << "ERROR::PROGRAM_LINKING_ERROR of type: " << type << "\n" << infoLog << "\n -- --------------------------------------------------- -- " << std::endl;
        }
    }
}

(3)编写shader脚本,这里有三个版本,大家可以直接放上(三)然后写完主程序后,再调整(一)和(二)

文件目录可以像我这样创建:

(一)仅放一张贴图

a)texture.vs顶点着色器:

#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aColor;
layout (location = 2) in vec2 aTexCoord;

out vec3 ourColor;
out vec2 TexCoord;

void main()
{
    gl_Position = vec4(aPos, 1.0);
    ourColor = aColor;
    TexCoord = aTexCoord;
}

 

b)texture.vs片段着色器:

 

#version 330 core
out vec4 FragColor;

in vec3 ourColor;
in vec2 TexCoord;

uniform sampler2D ourTexture;

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

此时我们的运行结果是这样:

 

(二)第一张贴图的基础上再加上渐变色(上一个教程的三色渐变)

修改的地方是我们的片段着色器

texture.fs

#version 330 core
out vec4 FragColor;

in vec3 ourColor;
in vec2 TexCoord;

uniform sampler2D ourTexture;

void main()
{
//我们只需把纹理颜色与顶点颜色在片段着色器中相乘来混合二者的颜色: FragColor
= texture(ourTexture, TexCoord) * vec4(ourColor, 1.0); }

此时效果图如下:

 

 (三)贴两张纹理

顶点着色器texture.vs

#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aColor;
layout (location = 2) in vec2 aTexCoord;

out vec3 ourColor;
out vec2 TexCoord;

void main()
{
    gl_Position = vec4(aPos, 1.0);
    ourColor = aColor;
    TexCoord = aTexCoord;
}

片段着色器texture.fs

#version 330 core
out vec4 FragColor;

in vec3 ourColor;
in vec2 TexCoord;
uniform sampler2D texture1;
uniform sampler2D texture2;

uniform sampler2D ourTexture;

void main()
{
 //GLSL内建的mix函数需要接受两个值作为参数,并对它们根据第三个参数进行线性插值。
//如果第三个值是0.0,它会返回第一个输入;如果是1.0,会返回第二个输入值。
//0.2会返回80%的第一个输入颜色和20%的第二个输入颜色,即返回两个纹理的混合色。 FragColor
= mix(texture(texture1, TexCoord), texture(texture2, TexCoord), 0.2); }

最终输出颜色现在是两个纹理的结合:

 

(4)编写主程序:

 

关键代码的注释我已经翻译并且加上去,一些常规代码可以忽略或者把英文注释翻一下吧

 

 

  1 #include <glad/glad.h>
  2 #include <GLFW/glfw3.h>
  3   
  4 #include "stb_image.h"
  5 #include "shader_s.h"
  6 #include <iostream>
  7 
  8 void framebuffer_size_callback(GLFWwindow* window, int width, int height);
  9 void processInput(GLFWwindow *window);
 10 
 11 // settings
 12 const unsigned int SCR_WIDTH = 800;
 13 const unsigned int SCR_HEIGHT = 600;
 14 
 15 int main()
 16 {
 17     // glfw: initialize and configure
 18     // ------------------------------
 19     glfwInit();
 20     glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
 21     glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
 22     glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
 23 
 24 #ifdef __APPLE__
 25     glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // uncomment this statement to fix compilation on OS X
 26 #endif
 27 
 28     // glfw window creation
 29     // --------------------
 30     GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);
 31     if (window == NULL)
 32     {
 33         std::cout << "Failed to create GLFW window" << std::endl;
 34         glfwTerminate();
 35         return -1;
 36     }
 37     glfwMakeContextCurrent(window);
 38     glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
 39 
 40     // glad: load all OpenGL function pointers
 41     // ---------------------------------------
 42     if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
 43     {
 44         std::cout << "Failed to initialize GLAD" << std::endl;
 45         return -1;
 46     }
 47 
 48     // build and compile our shader zprogram
 49     // ------------------------------------
 50     Shader ourShader("../res/textures/texture.vs", "../res/textures/texture.fs");
 51 
 52     // set up vertex data (and buffer(s)) and configure vertex attributes
 53     // ------------------------------------------------------------------
 54     float vertices[] = {
 55         // 位置信息          // 颜色信息           // 纹理 coords
 56          0.5f,  0.5f, 0.0f,   1.0f, 0.0f, 0.0f,   1.0f, 1.0f, // 右上
 57          0.5f, -0.5f, 0.0f,   0.0f, 1.0f, 0.0f,   1.0f, 0.0f, // 右下
 58         -0.5f, -0.5f, 0.0f,   0.0f, 0.0f, 1.0f,   0.0f, 0.0f, // 左下
 59         -0.5f,  0.5f, 0.0f,   1.0f, 1.0f, 0.0f,   0.0f, 1.0f  // 左上
 60     };
 61     unsigned int indices[] = {
 62         0, 1, 3, // 第一个三角形
 63         1, 2, 3  // 第二个三角形
 64     };
 65     unsigned int VBO, VAO, EBO;
 66     glGenVertexArrays(1, &VAO);
 67     glGenBuffers(1, &VBO);
 68     glGenBuffers(1, &EBO);
 69 
 70     glBindVertexArray(VAO);
 71 
 72     glBindBuffer(GL_ARRAY_BUFFER, VBO);
 73     glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
 74 
 75     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
 76     glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
 77 
 78     // position attribute
 79     glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0);
 80     glEnableVertexAttribArray(0);
 81     // color attribute
 82     glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float)));
 83     glEnableVertexAttribArray(1);
 84     // texture coord attribute
 85     glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float)));
 86     glEnableVertexAttribArray(2);
 87 
 88 
 89     //    加载并创建纹理
 90     // -------------------------
 91     unsigned int texture1, texture2;
 92     //    第一张纹理
 93 
 94     glGenTextures(1, &texture1);
 95     glBindTexture(GL_TEXTURE_2D, texture1);
 96     //    为当前绑定的纹理对象设置环绕、过滤方式
 97     //    将纹理包装设置为GL_REPEAT(默认包装方法)
 98     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);    
 99     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
100     //    设置纹理过滤参数
101     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
102     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
103     // 加载并生成纹理
104     int width, height, nrChannels;
105     stbi_set_flip_vertically_on_load(true); //告诉stb_image.h在y轴上翻转加载的纹理。
106 
107     unsigned char *data = stbi_load("../res/textures/container.jpg", &width, &height, &nrChannels, 0);
108     if (data)
109     {
110         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
111         glGenerateMipmap(GL_TEXTURE_2D);
112     }
113     else
114     {
115         std::cout << "Failed to load texture" << std::endl;
116     }
117     stbi_image_free(data);
118 
119 
120     // texture 2
121     glGenTextures(1, &texture2);
122     glBindTexture(GL_TEXTURE_2D, texture2);
123     // set the texture wrapping parameters
124     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);    // set texture wrapping to GL_REPEAT (default wrapping method)
125     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
126     // set texture filtering parameters
127     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
128     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
129     // load image, create texture and generate mipmaps
130     data = stbi_load("../res/textures/awesomeface.png", &width, &height, &nrChannels, 0);
131     if (data)
132     {
133         //请注意,awesomeface.png具有透明度,因此具有alpha通道,
134         //因此请务必告诉OpenGL数据类型为GL_RGBA        
135         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
136         glGenerateMipmap(GL_TEXTURE_2D);
137     }
138     else
139     {
140         std::cout << "Failed to load texture" << std::endl;
141     }
142     stbi_image_free(data);
143 
144     //告诉每个采样器的opengl它属于哪个纹理单元(只需要做一次)    
145     ourShader.use(); //激活着色器
146     // either set it manually like so:
147     glUniform1i(glGetUniformLocation(ourShader.ID, "texture1"), 0);
148     // or set it via the texture class
149     ourShader.setInt("texture2", 1);
150 
151 
152 
153     // render loop
154     // -----------
155     while (!glfwWindowShouldClose(window))
156     {
157         // input
158         // -----
159         processInput(window);
160 
161         // render
162         // ------
163         glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
164         glClear(GL_COLOR_BUFFER_BIT);
165 
166         // bind textures on corresponding texture units
167         glActiveTexture(GL_TEXTURE0);
168         glBindTexture(GL_TEXTURE_2D, texture1);
169         glActiveTexture(GL_TEXTURE1);
170         glBindTexture(GL_TEXTURE_2D, texture2);
171 
172         // render container
173         ourShader.use();
174         glBindVertexArray(VAO);
175         glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
176 
177         // glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.)
178         // -------------------------------------------------------------------------------
179         glfwSwapBuffers(window);
180         glfwPollEvents();
181     }
182 
183     // optional: de-allocate all resources once they've outlived their purpose:
184     // ------------------------------------------------------------------------
185     glDeleteVertexArrays(1, &VAO);
186     glDeleteBuffers(1, &VBO);
187     glDeleteBuffers(1, &EBO);
188 
189     // glfw: terminate, clearing all previously allocated GLFW resources.
190     // ------------------------------------------------------------------
191     glfwTerminate();
192     return 0;
193 }
194 
195 // process all input: query GLFW whether relevant keys are pressed/released this frame and react accordingly
196 // ---------------------------------------------------------------------------------------------------------
197 void processInput(GLFWwindow *window)
198 {
199     if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
200         glfwSetWindowShouldClose(window, true);
201 }
202 
203 // glfw: whenever the window size changed (by OS or user resize) this callback function executes
204 // ---------------------------------------------------------------------------------------------
205 void framebuffer_size_callback(GLFWwindow* window, int width, int height)
206 {
207     // make sure the viewport matches the new window dimensions; note that width and 
208     // height will be significantly larger than specified on retina displays.
209     glViewport(0, 0, width, height);
210 }