C C++最全openGL入门之2、着色器&纹理_纹理与着色器,2024年最新深度解析,2024年最新值得收藏

img
img

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

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

如果你需要这些资料,可以戳这里获取

“in vec4 vertexColor;\n”
“void main()\n”
“{\n”
" FragColor = vertexColor;\n"// 将ourColor设置为我们从顶点数据那里得到的输入颜色
“}\n\0”;

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、 着色器编译编译链接 *********************************************/
//编写 编译顶点着色器
unsigned int vertexShader = glCreateShader(GL_VERTEX_SHADER);//传入GL_VERTEX_SHADER表示创建顶点着色器
glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);//绑定源码 1表示只有一个
glCompileShader(vertexShader);//编译
int success;
char infoLog[512];
glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);//检查是否编译报错  也就是检查GLSL的语法
if (!success)
{
    glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);
    cout << "ERROR:Shader::Vertex::Compilation Fail\n" << infoLog << endl;
}

//编写 编译片段着色器
unsigned int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);//传入GL_FRAGMENT_SHADER 表示创建片段着色器
glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);//绑定源码 1表示只有一个
glCompileShader(fragmentShader);//编译
glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);//检查是否编译报错  也就是检查GLSL的语法
if (!success)
{
    glad_glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);
    cout << "ERROR:Shader::fragment::Compilation Fail:" << infoLog << endl;
}

//链接着色器程序
unsigned int shaderProgram = glCreateProgram(); //创建着色器程序
glAttachShader(shaderProgram, vertexShader);//之前编译的着色器附加到程序对象上
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);//链接它们
glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);//检查程序是否链接报错
if (!success)
{
    glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog);
    cout << "ERROR:Program::shaderProgram::Link Fail:" << infoLog << endl;
}
glDeleteShader(vertexShader);//着色器程序已链接完毕则不再需要可以进行delete了
glDeleteShader(fragmentShader);


/******************************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);//使用状态函数

    
    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);
glDeleteProgram(shaderProgram);

//释放/删除之前的分配的所有资源
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的显示试图
}


### 1.8、着色器封装成类


![在这里插入图片描述](https://img-blog.csdnimg.cn/1931aca0ce2b4a5a9526fbb572d35c7c.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAencxOTk2,size_20,color_FFFFFF,t_70,g_se,x_16)  
 ShaderClass.h文件  
 输入顶点着色器和片段着色器的文件路径  
 完成读取编译链接,并提供use,set,delte等方法



#ifndef SHADER_H
#define SHADER_H

#include <glad/glad.h>

#include
#include
#include
#include

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
#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、纹理坐标


![在这里插入图片描述](https://img-blog.csdnimg.cn/7e4598ca17e74ec3a4c634c0fa8664a2.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAencxOTk2,size_20,color_FFFFFF,t_70,g_se,x_16)


### 2.2、纹理环绕方式


![在这里插入图片描述](https://img-blog.csdnimg.cn/690ca79abdfc40bb8c355ef14abb61d2.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAencxOTk2,size_20,color_FFFFFF,t_70,g_se,x_16)


### 2.3、纹理过滤


![在这里插入图片描述](https://img-blog.csdnimg.cn/90adf879eac0458880e23c1ae124c1d0.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAencxOTk2,size_20,color_FFFFFF,t_70,g_se,x_16)


### 2.4、多级渐远纹理


![在这里插入图片描述](https://img-blog.csdnimg.cn/5641aa1a306a4dcab335d13a75cd9262.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAencxOTk2,size_20,color_FFFFFF,t_70,g_se,x_16)


### 2.5、加载与创建纹理


![在这里插入图片描述](https://img-blog.csdnimg.cn/ebf3eca4fa1a41ffad07f143516dc787.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAencxOTk2,size_20,color_FFFFFF,t_70,g_se,x_16#pic_center)


### 2.5.1、首先要使用stb\_image.h 加载图片;


![在这里插入图片描述](https://img-blog.csdnimg.cn/2f31c469e04849a7a731557f4a05148c.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAencxOTk2,size_20,color_FFFFFF,t_70,g_se,x_16)


### 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、应用纹理;着色器设置


**顶点数组的时候纹理坐标的传入**  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/0787c41d8506498f9e0c7bbb7bff7362.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAencxOTk2,size_20,color_FFFFFF,t_70,g_se,x_16)


**片段着色器的使用**  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/db23fbbd4eb34422b0c92bf055cc1a78.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAencxOTk2,size_20,color_FFFFFF,t_70,g_se,x_16#pic_center)  
 主代码



#include
#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个纹理




![img](https://img-blog.csdnimg.cn/img_convert/93576fa36d523b74ad03c91f8638ab3e.png)
![img](https://img-blog.csdnimg.cn/img_convert/3569f40b67a2ea07b9f8ff3b3b0ca36b.png)

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

**[需要这份系统化的资料的朋友,可以添加戳这里获取](https://bbs.csdn.net/topics/618668825)**


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

_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个纹理


[外链图片转存中…(img-4Zf6UsxA-1715696139439)]
[外链图片转存中…(img-VWj2nSLN-1715696139439)]

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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值