在开始之前需要说明一下使用texture这一章加载纹理需要用到一个git上的头文件stb_image.h
这个库点击这里下载
或者使用网盘链接百度网盘
提取码:984h
shader.cpp文件:
#include "Shader.h"
#include
#include
#include
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
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 // 左上
};
//这个纹理坐标和点的位置是一对一的关系 比如这个
// 纹理坐标1.0f, 1.0f, 右上 对应位置 0.5f, 0.5f, 0.0f 其他的同理
//纹理坐标1.0f, 0.0f, 右下 对应位置 0.5f, -0.5f, 0.0f,
//相当于把纹理的坐标设置到哪个顶点
unsigned int indices[] = { // 注意索引从0开始!
0, 1, 3, // 第一个三角形
1, 2, 3 // 第二个三角形
};
void processInput(GLFWwindow* window)
{
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
glfwSetWindowShouldClose(window, true);
//这里我们检查用户是否按下了返回键(Esc)
}
int main(void)
{
//必须先初始化该库,然后才能使用大多数GLFW函数。成功初始化后,GLFW_TRUE将返回。如果发生错误,GLFW_FALSE则返回。
if (!glfwInit())
return -1;
//创建窗口(OpenGL上下文似乎也一并创建了)
GLFWwindow* window = glfwCreateWindow(640, 480, "Hello World", NULL, NULL);
//glfwCreateWindow函数需要窗口的宽和高作为它的前两个参数。第三个参数表示这个窗口的名称(标题
if (!window)
{
glfwTerminate();
return -1;
}
/* Make the window's context current */
glfwMakeContextCurrent(window);
//使用GLAD来加载OpenGL的函数地址 GLAD是用来管理OpenGL的函数指针的,所以在调用任何OpenGL的函数之前我们需要初始化GLAD。
//我们给GLAD传入了用来加载系统相关的OpenGL函数指针地址的函数。GLFW给我们的是glfwGetProcAddress,它根据我们编译的系统定义了正确的函数。
gladLoadGL();
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout << "Failed to initialize GLAD" << std::endl;
return -1;
}
Shader ourShader("shader.vs", "shader.fs");
//如果要绘制两个三角形就需要使用索引缓存对象来告诉GPU使用哪些顶点
unsigned int VAO, VBO;
unsigned int EBO;
glGenBuffers(1, &EBO);//glGenBuffers创建索引对象
glGenVertexArrays(1, &VAO);//glGenVertexArrays来创建顶点数组
glGenBuffers(1, &VBO);//glGenBuffers来创建缓存
glBindVertexArray(VAO);//为下面的属性绑定VAO
glBindBuffer(GL_ARRAY_BUFFER, VBO); //OpenGL有很多缓冲对象类型,顶点缓冲对象的缓冲类型是GL_ARRAY_BUFFER。
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);//绑定顶点并把顶点发送给GPU 第三个参数就是我们希望发送的实际数据。
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);//绑定EBO
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);//如何使用索引
//glVertexAttribPointer 是为了指定对应的属性 不然一堆数字GPU怎么知道哪几个是哪个属性
//下面依次是第一个属性第二个属性第三个属性
//其中第五个参数(步长)8 * sizeof(float) 这个就是某个定点三个属性的大小之和 比如第一个顶点:3+3+2=8;如果是六个float就是6* sizeof(float)
//第一个顶点的位置0.5f, 0.5f, 0.0f,
//第一个顶点的颜色是1.0f, 0.0f, 0.0f,
//第一个顶点对应的纹理坐标1.0f, 1.0f,
//第六个参数 (void*)0就是说你这个属性是从哪个位置开始;
//比如一个float是4个字节所有的属性大小就是 8 * sizeof(float)=4*8=32
//比如第三个属性是第6个元素开始的(第0个元素开始) 前面有 6 * sizeof(float)大小的空间
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(1);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float)));
glEnableVertexAttribArray(2);
unsigned int texture;
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);
/*
* 环绕方式:纹理坐标的范围通常是从(0, 0)到(1, 1),那如果我们把纹理坐标设置在范围之外会发生什么?
glTexParameteri();函数的作用就是设置纹理的环绕、过滤方式
GL_REPEAT 对纹理的默认行为。重复纹理图像。
GL_MIRRORED_REPEAT 和GL_REPEAT一样,但每次重复图片是镜像放置的。
GL_CLAMP_TO_EDGE 纹理坐标会被约束在0到1之间,超出的部分会重复纹理坐标的边缘,产生一种边缘被拉伸的效果。
GL_CLAMP_TO_BORDER 超出的坐标为用户指定的边缘颜色。
GL_TEXTURE_WRAP_S 设置纹理S轴的环绕方式
GL_TEXTURE_WRAP_T 设置纹理T轴的环绕方式
*/
//为当前绑定的纹理对象设置过滤方式
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
/*
设置纹理过滤
GL_NEAREST OpenGL会选择中心点最接近纹理坐标的那个像素(或者说颜色)是什么颜色就会选择那个颜色作为采样颜色
GL_LINEAR 这个过滤方式就是这个会基于纹理坐标附近的纹理像素进行一个线性差值
GL_TEXTURE_MAG_FILTER 表示进行放大(Magnify)时候使用GL_NEAREST 最近坐标过滤
GL_TEXTURE_MIN_FILTER 表示进行缩小(Minify)时候使用GL_LINEAR 线性差值过滤
*/
/*
多级渐远纹理 "只对缩小的纹理有效"
比如一个房间有很多个物体 每个物体大小都不一样
如果不使用多级渐远纹理 一个小的物体对纹理进行采样的时候,
取一个很大的纹理 色块的跨度就很大不好取值
使用一种叫做多级渐远纹理(Mipmap)的概念来解决这个问题,它简单来说就是一系列的纹理图像,后一个纹理图像是前一个的二分之一。
有了这么一系列纹理opengl就会在正确的距离选择合适纹理
*/
//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(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);
/*
第一个参数指定了纹理目标(Target)。设置为GL_TEXTURE_2D意味着会生成与当前绑定的纹理对象在同一个目标上的纹理(任何绑定到GL_TEXTURE_1D和GL_TEXTURE_3D的纹理不会受到影响)。
第二个参数为纹理指定多级渐远纹理的级别,如果你希望单独手动设置每个多级渐远纹理的级别的话。这里我们填0,也就是基本级别。
第三个参数告诉OpenGL我们希望把纹理储存为何种格式。我们的图像只有RGB值,因此我们也把纹理储存为RGB值。
第四个和第五个参数设置最终的纹理的宽度和高度。我们之前加载图像的时候储存了它们,所以我们使用对应的变量。
第六个参数应该总是被设为0(历史遗留的问题)。
第七第八个参数定义了源图的格式和数据类型。我们使用RGB值加载这个图像,并把它们储存为char(byte)数组,我们将会传入对应值。
第九个参数是真正的图像数据。
*/
glBindBuffer(GL_ARRAY_BUFFER, 0);//解绑V
glBindVertexArray(0);//使用完成之后解绑就行了为下面的属性绑定VAO
//循环直到用户关闭窗口 渲染循环(Render Loop),它能在我们让GLFW退出前一直保持运行
while (!glfwWindowShouldClose(window))
{
processInput(window);//使用按键
//清理屏幕所用的颜色:
glClearColor(0.4f, 0.5f, 0.6f, 1.0f);
//清理屏幕 它接受一个缓冲位(Buffer Bit)来指定要清空的缓冲,
//可能的缓冲位有GL_COLOR_BUFFER_BIT,GL_DEPTH_BUFFER_BIT和GL_STENCIL_BUFFER_BIT。由于现在我们只关心颜色值,所以我们只清空颜色缓冲。
glClear(GL_COLOR_BUFFER_BIT);
// glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);//使用glPolygonMode设置绘制模式这里是绘制线框
glBindTexture(GL_TEXTURE_2D, texture);
ourShader.use();//使用这个程序
glBindVertexArray(VAO);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
//交换前后缓冲
glfwSwapBuffers(window);
//轮询并处理事件
glfwPollEvents();
}
glDeleteVertexArrays(1, &VAO);
glDeleteBuffers(1, &VBO);
//使用GLFW完成操作后,通常是在应用程序退出之前,需要终止GLFW 释放/删除之前的分配的所有资源
glfwTerminate();
return 0;
}
shader.h头文件:
#include
#include
#include
#include
#include
//#include "stb_image.h"
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
{
// 打开文件
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: " << e.what() << 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);
}
// 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;
}
}
}
};
顶点点着色器
#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;
uniform sampler2D ourTexture;
void main()
{
FragColor = texture(ourTexture, TexCoord);
}
使用纹理单元
一个纹理单元相当于可以多使用一张纹理一般可以使用0-15 一共16个纹理单元
//1和创建第一个纹理一样创建第二个纹理
//开启使用纹理单元 glUniform1i
//还记得之前说的吗uniform 差不多是一个全局变量
//除了sampler2D 之外还有sampler3D
ourShader.use(); // 不要忘记在设置uniform变量之前激活着色器程序!
glUniform1i(glGetUniformLocation(ourShader.ID, "texture1"), 0); // 手动设置
ourShader.setInt("texture2", 1); // 或者使用着色器类设置
//2在while渲染里面激活 纹理单元
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture1);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, texture2);
注意如果你的贴图有透明通道需要在glTexImage2D 里面设置为RGBA模式否则会出现错误的图案
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
//使用纹理单元
#include "Shader.h"
#include
#include
#include
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
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 // 左上
};
//这个纹理坐标和点的位置是一对一的关系 比如这个
// 纹理坐标1.0f, 1.0f, 右上 对应位置 0.5f, 0.5f, 0.0f 其他的同理
//纹理坐标1.0f, 0.0f, 右下 对应位置 0.5f, -0.5f, 0.0f,
//相当于把纹理的坐标设置到哪个顶点
unsigned int indices[] = { // 注意索引从0开始!
0, 1, 3, // 第一个三角形
1, 2, 3 // 第二个三角形
};
void processInput(GLFWwindow* window)
{
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
glfwSetWindowShouldClose(window, true);
//这里我们检查用户是否按下了返回键(Esc)
}
int main(void)
{
//必须先初始化该库,然后才能使用大多数GLFW函数。成功初始化后,GLFW_TRUE将返回。如果发生错误,GLFW_FALSE则返回。
if (!glfwInit())
return -1;
//创建窗口(OpenGL上下文似乎也一并创建了)
GLFWwindow* window = glfwCreateWindow(640, 480, "Hello World", NULL, NULL);
//glfwCreateWindow函数需要窗口的宽和高作为它的前两个参数。第三个参数表示这个窗口的名称(标题
if (!window)
{
glfwTerminate();
return -1;
}
/* Make the window's context current */
glfwMakeContextCurrent(window);
//使用GLAD来加载OpenGL的函数地址 GLAD是用来管理OpenGL的函数指针的,所以在调用任何OpenGL的函数之前我们需要初始化GLAD。
//我们给GLAD传入了用来加载系统相关的OpenGL函数指针地址的函数。GLFW给我们的是glfwGetProcAddress,它根据我们编译的系统定义了正确的函数。
gladLoadGL();
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout << "Failed to initialize GLAD" << std::endl;
return -1;
}
//Shader ourShader("shader.vs", "shader.fs");
Shader ourShader("shaderSampler.vs", "shaderSampler.fs");
//如果要绘制两个三角形就需要使用索引缓存对象来告诉GPU使用哪些顶点
unsigned int VAO, VBO;
unsigned int EBO;
glGenBuffers(1, &EBO);//glGenBuffers创建索引对象
glGenVertexArrays(1, &VAO);//glGenVertexArrays来创建顶点数组
glGenBuffers(1, &VBO);//glGenBuffers来创建缓存
glBindVertexArray(VAO);//为下面的属性绑定VAO
glBindBuffer(GL_ARRAY_BUFFER, VBO); //OpenGL有很多缓冲对象类型,顶点缓冲对象的缓冲类型是GL_ARRAY_BUFFER。
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);//绑定顶点并把顶点发送给GPU 第三个参数就是我们希望发送的实际数据。
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);//绑定EBO
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);//如何使用索引
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(1);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float)));
glEnableVertexAttribArray(2);
// 加载并生成纹理
unsigned int texture1, texture2;
glGenTextures(1, &texture1);
glBindTexture(GL_TEXTURE_2D, texture1);
// set the texture wrapping parameters
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); // set texture wrapping to GL_REPEAT (default wrapping method)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
// set texture filtering parameters
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
int width, height, nrChannels;
//stbi_set_flip_vertically_on_load(true);//告诉stb_image.h翻转Y轴上加载的纹理。
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);
glGenTextures(1, &texture2);
glBindTexture(GL_TEXTURE_2D, texture2);
// set the texture wrapping parameters
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); // set texture wrapping to GL_REPEAT (default wrapping method)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
// set texture filtering parameters
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);
ourShader.use(); // 不要忘记在设置uniform变量之前激活着色器程序!
glUniform1i(glGetUniformLocation(ourShader.ID, "texture1"), 0); // 手动设置
ourShader.setInt("texture2", 1); // 或者使用着色器类设置
//循环直到用户关闭窗口 渲染循环(Render Loop),它能在我们让GLFW退出前一直保持运行
while (!glfwWindowShouldClose(window))
{
processInput(window);//使用按键
//清理屏幕所用的颜色:
glClearColor(0.4f, 0.5f, 0.6f, 1.0f);
//清理屏幕 它接受一个缓冲位(Buffer Bit)来指定要清空的缓冲,
//可能的缓冲位有GL_COLOR_BUFFER_BIT,GL_DEPTH_BUFFER_BIT和GL_STENCIL_BUFFER_BIT。由于现在我们只关心颜色值,所以我们只清空颜色缓冲。
glClear(GL_COLOR_BUFFER_BIT);
// bind textures on corresponding texture units
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture1);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, texture2);
// render container
ourShader.use();
glBindVertexArray(VAO);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
//交换前后缓冲
glfwSwapBuffers(window);
//轮询并处理事件
glfwPollEvents();
}
glDeleteVertexArrays(1, &VAO);
glDeleteBuffers(1, &VBO);
glDeleteBuffers(1, &EBO);
//使用GLFW完成操作后,通常是在应用程序退出之前,需要终止GLFW 释放/删除之前的分配的所有资源
glfwTerminate();
return 0;
}
片段着色器
#version 330 core
out vec4 FragColor;
in vec3 ourColor;
in vec2 TexCoord;
uniform sampler2D texture1;
uniform sampler2D texture2;
void main()
{
FragColor = mix(texture(texture1, TexCoord), texture(texture2, TexCoord), 0.2);
//最终输出颜色现在是两个纹理混合。GLSL内建的mix函数需要接受两个值作为参数,并对它们根据第三个参数进行线性插值
// 如果第三个值是0.0,它会返回第一个输入
// 如果是1.0,会返回第二个输入值
// 0.2会返回80%的第一个输入颜色和20%的第二个输入颜色,即返回两个纹理的混合色
}
顶点着色器
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aColor;
layout (location = 2) in vec2 aTexCoord;
//输入的属性1属性2属性3
out vec3 ourColor;
out vec2 TexCoord;
void main()
{
gl_Position = vec4(aPos, 1.0);
ourColor = aColor;
TexCoord = aTexCoord;
}
正常效果:
图片有透明通道如果不设置RGBA会得到以下图案
通过使用stbi_set_flip_vertically_on_load(true);翻转Y轴
因为图片的(0,0)点一般在顶部所以为成倒置的图片翻转一下就好