OpenGL之紋理貼圖(Texture)


學習自:

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 }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM