C C++最全openGL入门之2、着色器&纹理_纹理与着色器(1),干货面试教程

img
img

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

#ifndef SHADER_H
#define SHADER_H

#include <glad/glad.h>

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

class Shader
{
public:
    unsigned int ID;
    // constructor generates the shader on the fly
    // ------------------------------------------------------------------------
    Shader(const char* vertexPath, const char* fragmentPath)
    {
        // 1. retrieve the vertex/fragment source code from filePath
        std::string vertexCode;
        std::string fragmentCode;
        std::ifstream vShaderFile;
        std::ifstream fShaderFile;
        // ensure ifstream objects can throw exceptions:
        vShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit);
        fShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit);
        try
        {
            // open files
            vShaderFile.open(vertexPath);
            fShaderFile.open(fragmentPath);
            std::stringstream vShaderStream, fShaderStream;
            // read file's buffer contents into streams
            vShaderStream << vShaderFile.rdbuf();
            fShaderStream << fShaderFile.rdbuf();
            // close file handlers
            vShaderFile.close();
            fShaderFile.close();
            // convert stream into 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. compile shaders
        unsigned int vertex, fragment;
        // vertex shader
        vertex = glCreateShader(GL_VERTEX_SHADER);
        glShaderSource(vertex, 1, &vShaderCode, NULL);
        glCompileShader(vertex);
        checkCompileErrors(vertex, "VERTEX");
        // fragment Shader
        fragment = glCreateShader(GL_FRAGMENT_SHADER);
        glShaderSource(fragment, 1, &fShaderCode, NULL);
        glCompileShader(fragment);
        checkCompileErrors(fragment, "FRAGMENT");
        // shader Program
        ID = glCreateProgram();
        glAttachShader(ID, vertex);
        glAttachShader(ID, fragment);
        glLinkProgram(ID);
        checkCompileErrors(ID, "PROGRAM");
        // delete the shaders as they're linked into our program now and no longer necessary
        glDeleteShader(vertex);
        glDeleteShader(fragment);
    }
    // activate the shader
    // ------------------------------------------------------------------------
    void use()
    {
        glUseProgram(ID);
    }
    void dele()
    {
        glDeleteProgram(ID);
    }
    // utility uniform functions
    // ------------------------------------------------------------------------
    void setBool(const std::string& name, bool value) const
    {
        glUniform1i(glGetUniformLocation(ID, name.c_str()), (int)value);
    }
    // ------------------------------------------------------------------------
    void setInt(const std::string& name, int value) const
    {
        glUniform1i(glGetUniformLocation(ID, name.c_str()), value);
    }
    // ------------------------------------------------------------------------
    void setFloat(const std::string& name, float value) const
    {
        glUniform1f(glGetUniformLocation(ID, name.c_str()), value);
    }

private:
    // utility function for checking shader compilation/linking errors.
    // ------------------------------------------------------------------------
    void 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;
            }
        }
    }
};
#endif

顶点着色器文件内容

#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aColor;
out vec4 vertexColor;
void main()
{
   vertexColor = vec4(aColor, 1.0);
   gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);
}

片段着色器文件内容

#version 330 core
out vec4 FragColor;
in vec4 vertexColor;
void main()
{
	FragColor = vertexColor;
}


使用着色器类

定义初始化
 Shader ourShader("shader.vs", "shader.fs");
使用
 ourShader.use();
删除
    ourShader.dele();

完整代码

#include <iostream>
#include <glad/glad.h> 
#include <GLFW/glfw3.h>
#include "ShaderClass.h"

using namespace std;
void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void processInput(GLFWwindow* window);
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;

int main()
{
    /******************************1、 初始化opengl窗口*********************************************/
    //glfw 初始化和配置 
    glfwInit();//初始化GLFW
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);//主版本号
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);//子版本号
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);//指定哪个配置文件配置上下文:GLFW我们使用的是核心模式
#ifdef __APPLE__//如果时mac os xp等系统则要进行配置一下才能生效
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif

    //glfw创建窗口
    GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);
    if (window == NULL)
    {
        std::cout << "Failed to create GLFW window" << std::endl;
        glfwTerminate();
        return -1;
    }
    glfwMakeContextCurrent(window);//GLFW将我们窗口的上下文设置为当前线程的主上下文
    glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
    
    //GLAD加载所有的opengl函数指针
    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
    {
        std::cout << "Failed to initialize GLAD" << std::endl;
        return -1;
    }

    /******************************2、 着色器编译编译链接 *********************************************/
    Shader ourShader("shader.vs", "shader.fs");

    /******************************3、 顶点属性缓存这些   *********************************************/

    //绘制两个相连的三角形
    float vertices[] = {
        //位置                //顶点
        -0.9f, -0.5f, 0.0f,  1.0f, 0.0f, 0.0f, // left 
       -0.0f, -0.5f, 0.0f,  0.0f, 1.0f, 0.0f,// right
       -0.45f, 0.5f, 0.0f,  0.0f, 0.0f, 1.0f,// top 
       0.9f, -0.5f, 0.0f,  1.0f, 0.0f, 0.0f,// right
       0.45f, 0.5f, 0.0f  ,0.0f, 0.0f, 1.0f // top 
    };
    //索引数组
    unsigned int indices[] = {  // note that we start from 0!
        0, 1, 2,  // first Triangle
        1, 3, 4   // second Triangle
    };
    unsigned int VBO, VAO, EBO;
    glGenVertexArrays(1, &VAO);//创建一个顶点数组 与顶点属性相绑定 
    glGenBuffers(1, &VBO);//创建顶点缓冲对象
    glGenBuffers(1, &EBO);

    glBindVertexArray(VAO);//绑定顶点数组 配置顶点属性

    glBindBuffer(GL_ARRAY_BUFFER, VBO);//将GL_ARRAY_BUFFER类型的缓存与VBO绑定
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);//之前定义的顶点数据复制到缓冲的内存
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);//将GL_ELEMENT_ARRAY_BUFFER类型的缓冲与EBO绑定
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);//把索引复制到缓冲里

    /*
        0;指定我们要配置的顶点位置属性 就是顶点着色器里面location那个   
    */
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6*sizeof(float), (void *)0);//步长为6了
    glEnableVertexAttribArray(0);//以顶点属性位置值0作为参数,启用顶点属性
     /*
        1;指定我们要配置的顶点颜色属性 就是顶点着色器里面location那个  glVertexAttribPointer函数更新顶点格式
    */
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float)));
    glEnableVertexAttribArray(1);

    glBindBuffer(GL_ARRAY_BUFFER, 0);//VBO 已经与顶点属性数组VAO进行绑定了 那么GL_ARRAY_BUFFER就可以解除绑定
    //glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); //记住:不要在VAO激活时解除绑定EBO,因为绑定的元素缓冲区对象存储在VAO中;保持EBO绑定。

    glBindVertexArray(0);//您可以在之后解除绑定VAO,这样其他VAO调用就不会意外地修改这个VAO

    //渲染循环
    //程序在我们主动关闭它之前不断绘制图像并能够接受用户输入 GLFW退出前一直保持运行
    while (!glfwWindowShouldClose(window))//检查一次GLFW是否被要求退出
    {
        //可接收键盘输入esc从而退出
        processInput(window);

        glClearColor(0.2f, 0.3f, 0.3f, 1.0f); //设置状态函数
        glClear(GL_COLOR_BUFFER_BIT);//使用状态函数

        
        ourShader.use();
       // glUseProgram(shaderProgram);//使用着色器程序

        glBindVertexArray(VAO);//绑定顶点数组  就是使用顶点属性
        //glDrawArrays(GL_TRIANGLES, 0, 3);
        //配置OpenGL如何绘制图元。第一个参数表示我们打算将其应用到所有的三角形的正面和背面,第二个参数告诉我们用线来绘制
        //注意如果进行设置glPolygonMode(GL_FRONT_AND_BACK, GL_FILL) 那么就会一直是线框模式
        //glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
        glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);//使用当前绑定的索引缓冲对象中的索引进行绘制:

        glfwSwapBuffers(window);//交换颜色缓冲(它是一个储存着GLFW窗口每一个像素颜色值的大缓冲),它在这一迭代中被用来绘制,并且将会作为输出显示在屏幕上。
        glfwPollEvents();//检查有没有触发什么事件(比如键盘输入、鼠标移动等)、更新窗口状态,并调用对应的回调函数
    }
    
    glDeleteVertexArrays(1, &VAO);
    glDeleteBuffers(1, &VBO);
    ourShader.dele();

    //释放/删除之前的分配的所有资源
    glfwTerminate();

	return 0;
}
void processInput(GLFWwindow* window)
{
    if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)//会返回这个按键是否正在被按下
        glfwSetWindowShouldClose(window, true);//把WindowShouldClose属性设置为 true的方法关闭GLFW
}
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
    //左上角坐标xy和宽高
    glViewport(0, 0, width, height);//OpenGL的显示试图
}

2、纹理

2.1、纹理坐标

在这里插入图片描述

2.2、纹理环绕方式

在这里插入图片描述

2.3、纹理过滤

在这里插入图片描述

2.4、多级渐远纹理

在这里插入图片描述

2.5、加载与创建纹理

在这里插入图片描述

2.5.1、首先要使用stb_image.h 加载图片;

在这里插入图片描述

2.5.2、生成纹理;纹理定义设置

定义纹理对象
unsigned int texture;
    glGenTextures(1, &texture);//生成纹理对象,同样也可以创建一个数组的纹理
    glBindTexture(GL_TEXTURE_2D, texture); //绑定它,让之后任何的纹理指令都可以配置当前绑定的纹理:
加载图片数据生成纹理
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
	glGenerateMipmap(GL_TEXTURE_2D);
 为当前绑定的纹理对象设置环绕、过滤方式
	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);

2.5.3、应用纹理;着色器设置

顶点数组的时候纹理坐标的传入
在这里插入图片描述

片段着色器的使用
在这里插入图片描述
主代码

#include <iostream>
#include <glad/glad.h> 
#include <GLFW/glfw3.h>
#include "ShaderClass.h"
#include "stb_image.h"
using namespace std;
void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void processInput(GLFWwindow* window);
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;

int main()
{
    /******************************1、 初始化opengl窗口*********************************************/
    //glfw 初始化和配置 
    glfwInit();//初始化GLFW
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);//主版本号
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);//子版本号
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);//指定哪个配置文件配置上下文:GLFW我们使用的是核心模式
#ifdef __APPLE__//如果时mac os xp等系统则要进行配置一下才能生效
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif

    //glfw创建窗口
    GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);
    if (window == NULL)
    {
        std::cout << "Failed to create GLFW window" << std::endl;
        glfwTerminate();
        return -1;
    }
    glfwMakeContextCurrent(window);//GLFW将我们窗口的上下文设置为当前线程的主上下文
    glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
    
    //GLAD加载所有的opengl函数指针
    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
    {
        std::cout << "Failed to initialize GLAD" << std::endl;
        return -1;
    }

    /******************************2、 着色器编译编译链接 *********************************************/
    Shader ourShader("shader.vs", "shader.fs");

    /******************************3、 顶点属性缓存这些   *********************************************/

    //绘制两个相连的三角形
    float vertices[] = {
         //     ---- 位置 ----       ---- 颜色 ----     - 纹理坐标 -
         0.5f,  0.5f, 0.0f,   1.0f, 0.0f, 0.0f,   1.0f, 1.0f,   // 右上
         0.5f, -0.5f, 0.0f,   0.0f, 1.0f, 0.0f,   1.0f, 0.0f,   // 右下
        -0.5f, -0.5f, 0.0f,   0.0f, 0.0f, 1.0f,   0.0f, 0.0f,   // 左下
        -0.5f,  0.5f, 0.0f,   1.0f, 1.0f, 0.0f,   0.0f, 1.0f    // 左上
    };
    //索引数组
    unsigned int indices[] = {
        0, 1, 3, // first triangle
        1, 2, 3  // second triangle
    };
    unsigned int VBO, VAO, EBO;
    glGenVertexArrays(1, &VAO);//创建一个顶点数组 与顶点属性相绑定 
    glGenBuffers(1, &VBO);//创建顶点缓冲对象
    glGenBuffers(1, &EBO);

    glBindVertexArray(VAO);//绑定顶点数组 配置顶点属性

    glBindBuffer(GL_ARRAY_BUFFER, VBO);//将GL_ARRAY_BUFFER类型的缓存与VBO绑定
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);//之前定义的顶点数据复制到缓冲的内存
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);//将GL_ELEMENT_ARRAY_BUFFER类型的缓冲与EBO绑定
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);//把索引复制到缓冲里

    /*
        0;指定我们要配置的顶点位置属性 就是顶点着色器里面location那个   
    */
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 *sizeof(float), (void *)0);//步长为8了
    glEnableVertexAttribArray(0);//以顶点属性位置值0作为参数,启用顶点属性
     /*
        1;指定我们要配置的顶点颜色属性 就是顶点着色器里面location那个  glVertexAttribPointer函数更新顶点格式
    */
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float)));
    glEnableVertexAttribArray(1);
    /*
        2;指定我们要配置的顶点纹理属性 就是顶点着色器里面location那个  glVertexAttribPointer函数更新顶点格式
    */
    glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float)));
    glEnableVertexAttribArray(2);
    glBindBuffer(GL_ARRAY_BUFFER, 0);//VBO 已经与顶点属性数组VAO进行绑定了 那么GL_ARRAY_BUFFER就可以解除绑定
    //glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); //记住:不要在VAO激活时解除绑定EBO,因为绑定的元素缓冲区对象存储在VAO中;保持EBO绑定。
    glBindVertexArray(0);//您可以在之后解除绑定VAO,这样其他VAO调用就不会意外地修改这个VAO

    // 加载创建纹理
    unsigned int texture;
    glGenTextures(1, &texture);//生成纹理对象,同样也可以创建一个数组的纹理
    glBindTexture(GL_TEXTURE_2D, texture); //绑定它,让之后任何的纹理指令都可以配置当前绑定的纹理:
    //为当前绑定的纹理对象设置环绕、过滤方式
    /*
    第一个参数指定了纹理目标;我们使用的是2D纹理,因此纹理目标是GL_TEXTURE_2D。
    第二个参数需要我们指定设置的选项与应用的纹理轴。我们打算配置的是WRAP选项,并且指定S和T轴。
    最后一个参数需要我们传递一个环绕方式(Wrapping),
    在这个例子中OpenGL会给当前激活的纹理设定纹理环绕方式为GL_REPEAT。
    */
    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_MIPMAP_LINEAR);//缩小时采用在两个邻近的多级渐远纹理之间使用线性插值,并使用线性插值进行采样
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);//线性
    // 加载图像
    int width, height, nrChannels;
    unsigned char* data = stbi_load("container.jpg", &width, &height, &nrChannels, 0);
    if (data)
    {
        /*
        当调用glTexImage2D时,当前绑定的纹理对象就会被附加上纹理图像
        第一个参数指定了纹理目标(Target)。设置为GL_TEXTURE_2D意味着会生成与当前绑定的纹理对象在同一个目标上的纹理(任何绑定到GL_TEXTURE_1D和GL_TEXTURE_3D的纹理不会受到影响)。
        第二个参数为纹理指定多级渐远纹理的级别,如果你希望单独手动设置每个多级渐远纹理的级别的话。这里我们填0,也就是基本级别。
        第三个参数告诉OpenGL我们希望把纹理储存为何种格式。我们的图像只有RGB值,因此我们也把纹理储存为RGB值。
        第四个和第五个参数设置最终的纹理的宽度和高度。我们之前加载图像的时候储存了它们,所以我们使用对应的变量。
        下个参数应该总是被设为0(历史遗留的问题)。
        第七第八个参数定义了源图的格式和数据类型。我们使用RGB值加载这个图像,并把它们储存为char(byte)数组,我们将会传入对应值。
        最后一个参数是真正的图像数据
        */
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
        glGenerateMipmap(GL_TEXTURE_2D);//为当前绑定的纹理自动生成所有需要的多级渐远纹理
    }
    else
    {
        std::cout << "Failed to load texture" << std::endl;
    }
    stbi_image_free(data);//释放图像的内存

    //渲染循环
    //程序在我们主动关闭它之前不断绘制图像并能够接受用户输入 GLFW退出前一直保持运行
    while (!glfwWindowShouldClose(window))//检查一次GLFW是否被要求退出
    {
        //可接收键盘输入esc从而退出
        processInput(window);

        glClearColor(0.2f, 0.3f, 0.3f, 1.0f); //设置状态函数
        glClear(GL_COLOR_BUFFER_BIT);//使用状态函数

        
        ourShader.use();
       // glUseProgram(shaderProgram);//使用着色器程序

        glBindVertexArray(VAO);//绑定顶点数组  就是使用顶点属性
        //glDrawArrays(GL_TRIANGLES, 0, 3);
        //配置OpenGL如何绘制图元。第一个参数表示我们打算将其应用到所有的三角形的正面和背面,第二个参数告诉我们用线来绘制
        //注意如果进行设置glPolygonMode(GL_FRONT_AND_BACK, GL_FILL) 那么就会一直是线框模式
        //glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
        //在调用glDrawElements之前绑定纹理了,它会自动把纹理赋值给片段着色器的采样器:
        glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);//使用当前绑定的索引缓冲对象中的索引进行绘制:

        glfwSwapBuffers(window);//交换颜色缓冲(它是一个储存着GLFW窗口每一个像素颜色值的大缓冲),它在这一迭代中被用来绘制,并且将会作为输出显示在屏幕上。
        glfwPollEvents();//检查有没有触发什么事件(比如键盘输入、鼠标移动等)、更新窗口状态,并调用对应的回调函数
    }
    
    glDeleteVertexArrays(1, &VAO);
    glDeleteBuffers(1, &VBO);
    ourShader.dele();

    //释放/删除之前的分配的所有资源
    glfwTerminate();

	return 0;
}
void processInput(GLFWwindow* window)
{
    if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)//会返回这个按键是否正在被按下
        glfwSetWindowShouldClose(window, true);//把WindowShouldClose属性设置为 true的方法关闭GLFW
}
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
    //左上角坐标xy和宽高
    glViewport(0, 0, width, height);//OpenGL的显示试图
}

顶点着色器

#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;
}

片段着色器

#version 330 core
out vec4 FragColor;

in vec3 ourColor;
in vec2 TexCoord;

//在调用glDrawElements之前绑定纹理了,它会自动把纹理赋值给片段着色器的采样器:
uniform sampler2D ourTexture;

void main()
{
//ourTexture  采样器
//TexCoord  由顶点坐标那边传来的纹理坐标
//最后输出还可以把颜色也叠加上去
    FragColor = texture(ourTexture, TexCoord) * vec4(ourColor, 1.0);;
}

2.6、纹理单元 实现多幅纹理图片叠加

纹理单元的主要目的是让我们在着色器中可以使用多于一个的纹理。通过把纹理单元赋值给采样器,我们可以一次绑定多个纹理,只要我们首先激活对应的纹理单元。
OpenGL至少保证有16个纹理单元供你使用,也就是说你可以激活从GL_TEXTURE0到GL_TEXTRUE15。它们都是按顺序定义的,所以我们也可以通过GL_TEXTURE0 + 8的方式获得GL_TEXTURE8,这在当我们需要循环一些纹理单元的时候会很有用。

2.6.1、首先我们要编写片段着色器

#version 330 core
out vec4 FragColor;

in vec3 ourColor;
in vec2 TexCoord;

//sampler2D就是表示纹理单元类型的 是uniform卡类型
uniform sampler2D texture1;
uniform sampler2D texture2;

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

2.6.2、同样将纹理2获取设置

完全与纹理1设置一样

//纹理2
    glGenTextures(1, &texture2);
    glBindTexture(GL_TEXTURE_2D, texture2);
    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);
    data = stbi_load("awesomeface.png", &width, &height, &nrChannels, 0);
    if (data)
    {
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
        glGenerateMipmap(GL_TEXTURE_2D);
    }
    else
    {
        std::cout << "Failed to load texture" << std::endl;
    }
    stbi_image_free(data);


2.6.3、纹理绑定着色器

 /*
使用glUniform1i设置每个采样器的方式告诉OpenGL每个着色器采样器属于哪个纹理单元。
我们只需要设置一次即可,所以这个会放在渲染循环的前面
设置uniforms变量的时候需激活着色器
*/
    ourShader.use();
    glUniform1i(glGetUniformLocation(ourShader.ID, "texture1"), 0);//绑定这个着色器的第0个纹理
    ourShader.setInt("texture2", 1);//绑定这个着色器的第0个纹理


2.6.4、在渲染中激活绑定

//激活纹理进行绑定
	glActiveTexture(GL_TEXTURE0);
	glBindTexture(GL_TEXTURE_2D, texture);
	glActiveTexture(GL_TEXTURE1);
	glBindTexture(GL_TEXTURE_2D, texture2);

2.6.5、实践在这里插入图片描述

主代码

#include <iostream>
#include <glad/glad.h> 
#include <GLFW/glfw3.h>
#include "ShaderClass.h"
#include "stb_image.h"
using namespace std;
void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void processInput(GLFWwindow* window);
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;

int main()
{
    /******************************1、 初始化opengl窗口*********************************************/
    //glfw 初始化和配置 
    glfwInit();//初始化GLFW
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);//主版本号
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);//子版本号
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);//指定哪个配置文件配置上下文:GLFW我们使用的是核心模式
#ifdef __APPLE__//如果时mac os xp等系统则要进行配置一下才能生效
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif

    //glfw创建窗口
    GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);
    if (window == NULL)
    {
        std::cout << "Failed to create GLFW window" << std::endl;
        glfwTerminate();
        return -1;
    }
    glfwMakeContextCurrent(window);//GLFW将我们窗口的上下文设置为当前线程的主上下文
    glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
    
    //GLAD加载所有的opengl函数指针
    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
    {
        std::cout << "Failed to initialize GLAD" << std::endl;
        return -1;
    }

    /******************************2、 着色器编译编译链接 *********************************************/
    Shader ourShader("shader.vs", "shader.fs");

    /******************************3、 顶点属性缓存这些   *********************************************/

    //绘制两个相连的三角形
    float vertices[] = {
         //     ---- 位置 ----       ---- 颜色 ----     - 纹理坐标 -
         0.5f,  0.5f, 0.0f,   1.0f, 0.0f, 0.0f,   1.0f, 1.0f,   // 右上
         0.5f, -0.5f, 0.0f,   0.0f, 1.0f, 0.0f,   1.0f, 0.0f,   // 右下
        -0.5f, -0.5f, 0.0f,   0.0f, 0.0f, 1.0f,   0.0f, 0.0f,   // 左下
        -0.5f,  0.5f, 0.0f,   1.0f, 1.0f, 0.0f,   0.0f, 1.0f    // 左上
    };
    //索引数组
    unsigned int indices[] = {
        0, 1, 3, // first triangle
        1, 2, 3  // second triangle
    };
    unsigned int VBO, VAO, EBO;
    glGenVertexArrays(1, &VAO);//创建一个顶点数组 与顶点属性相绑定 
    glGenBuffers(1, &VBO);//创建顶点缓冲对象
    glGenBuffers(1, &EBO);

    glBindVertexArray(VAO);//绑定顶点数组 配置顶点属性

    glBindBuffer(GL_ARRAY_BUFFER, VBO);//将GL_ARRAY_BUFFER类型的缓存与VBO绑定
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);//之前定义的顶点数据复制到缓冲的内存
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);//将GL_ELEMENT_ARRAY_BUFFER类型的缓冲与EBO绑定
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);//把索引复制到缓冲里

    /*
        0;指定我们要配置的顶点位置属性 就是顶点着色器里面location那个   
    */
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 *sizeof(float), (void *)0);//步长为8了
    glEnableVertexAttribArray(0);//以顶点属性位置值0作为参数,启用顶点属性
     /*
        1;指定我们要配置的顶点颜色属性 就是顶点着色器里面location那个  glVertexAttribPointer函数更新顶点格式
    */
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float)));
    glEnableVertexAttribArray(1);
    /*
        2;指定我们要配置的顶点纹理属性 就是顶点着色器里面location那个  glVertexAttribPointer函数更新顶点格式
    */
    glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float)));
    glEnableVertexAttribArray(2);
    glBindBuffer(GL_ARRAY_BUFFER, 0);//VBO 已经与顶点属性数组VAO进行绑定了 那么GL_ARRAY_BUFFER就可以解除绑定
    //glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); //记住:不要在VAO激活时解除绑定EBO,因为绑定的元素缓冲区对象存储在VAO中;保持EBO绑定。
    glBindVertexArray(0);//您可以在之后解除绑定VAO,这样其他VAO调用就不会意外地修改这个VAO

    // 加载创建纹理
    unsigned int texture, texture2;
    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_MIPMAP_LINEAR);//缩小时采用在两个邻近的多级渐远纹理之间使用线性插值,并使用线性插值进行采样
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);//线性
    // 加载图像
    int width, height, nrChannels;
    stbi_set_flip_vertically_on_load(true);//在加载图像前反转一下;是因为OpenGL要求y轴0.0坐标是在图片的底部的,但是图片的y轴0.0坐标通常在顶部。
    unsigned char* data = stbi_load("container.jpg", &width, &height, &nrChannels, 0);
    if (data)
    {
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
        glGenerateMipmap(GL_TEXTURE_2D);//为当前绑定的纹理自动生成所有需要的多级渐远纹理
    }
    else
    {
        std::cout << "Failed to load texture" << std::endl;
    }
    stbi_image_free(data);//释放图像的内存
   
    //纹理2
    glGenTextures(1, &texture2);
    glBindTexture(GL_TEXTURE_2D, texture2);
    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);
    data = stbi_load("awesomeface.png", &width, &height, &nrChannels, 0);
    if (data)
    {
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
        glGenerateMipmap(GL_TEXTURE_2D);
    }
    else
    {
        std::cout << "Failed to load texture" << std::endl;
    }
    stbi_image_free(data);

    /*
    使用glUniform1i设置每个采样器的方式告诉OpenGL每个着色器采样器属于哪个纹理单元。
    我们只需要设置一次即可,所以这个会放在渲染循环的前面
    设置uniforms变量的时候需激活着色器
    */
    ourShader.use();
    glUniform1i(glGetUniformLocation(ourShader.ID, "texture1"), 0);//绑定这个着色器的第0个纹理
    ourShader.setInt("texture2", 1);//绑定这个着色器的第0个纹理


    //渲染循环
    //程序在我们主动关闭它之前不断绘制图像并能够接受用户输入 GLFW退出前一直保持运行
    while (!glfwWindowShouldClose(window))//检查一次GLFW是否被要求退出
    {
        //可接收键盘输入esc从而退出
        processInput(window);

        glClearColor(0.2f, 0.3f, 0.3f, 1.0f); //设置状态函数


![img](https://img-blog.csdnimg.cn/img_convert/6f066becf1522aac876eb4f0005ed6ba.png)
![img](https://img-blog.csdnimg.cn/img_convert/5918c23be53fb5ba8ceb4df2c3b97a70.png)

**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!**

**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新**

**[如果你需要这些资料,可以戳这里获取](https://bbs.csdn.net/topics/618668825)**

e" << std::endl;
    }
    stbi_image_free(data);

    /*
    使用glUniform1i设置每个采样器的方式告诉OpenGL每个着色器采样器属于哪个纹理单元。
    我们只需要设置一次即可,所以这个会放在渲染循环的前面
    设置uniforms变量的时候需激活着色器
    */
    ourShader.use();
    glUniform1i(glGetUniformLocation(ourShader.ID, "texture1"), 0);//绑定这个着色器的第0个纹理
    ourShader.setInt("texture2", 1);//绑定这个着色器的第0个纹理


    //渲染循环
    //程序在我们主动关闭它之前不断绘制图像并能够接受用户输入 GLFW退出前一直保持运行
    while (!glfwWindowShouldClose(window))//检查一次GLFW是否被要求退出
    {
        //可接收键盘输入esc从而退出
        processInput(window);

        glClearColor(0.2f, 0.3f, 0.3f, 1.0f); //设置状态函数


[外链图片转存中...(img-zj0Fg7ED-1715696103096)]
[外链图片转存中...(img-ctebCKfd-1715696103096)]

**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!**

**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新**

**[如果你需要这些资料,可以戳这里获取](https://bbs.csdn.net/topics/618668825)**

  • 5
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值