纹理
纹理是一个2D图片(甚至也有1D和3D的纹理),它可以用来添加物体的细节。
这是两张照片叠加的效果
由下面两张叠加而成
源代码
shaders类在自定义着色器 中有完整的源代码。
下边我们使用stb_image.h
来解析图片。
#define GLEW_STATIC // 这个一定要加不然报错 静态链接库
#include<GL/glew.h>
#include<GLFW/glfw3.h>
#include<iostream>
#include "Shaders.h"
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
using namespace std;
void processInput(GLFWwindow);
void processInput(GLFWwindow *window) {
//如果键盘输入esc 则触发 退出
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) {
// 设置 要求退出
glfwSetWindowShouldClose(window, true);
}
}
// 逆时针方向绘制 默认情况下,逆时针的顶点连接顺序被定义为三角形的正
// 逆时针或顺时针都是相对于观察者方向的
// uv(st)坐标 u(s)为x轴 v(t)为y轴 是给图片定义的 范围都是0 - 1
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, 2, //第一个三角形的索引
2, 3, 0 //第二个三角形的索引
};
int main() {
// 初始化GLFW
glfwInit();
// 提示 我们使用的版本是3.3
// 主版本
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
// 次版本
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
// 简介
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
// 创建一个窗口对象
GLFWwindow *window = glfwCreateWindow(800, 600, "Test window", NULL, NULL);
if (window == NULL) {
cout << "open window failed." << endl;
// 终止 glfw
glfwTerminate();
}
// 绑定window到上下文对象 创建完窗口我们就可以通知GLFW将我们窗口的上下文设置为当前线程的主上下文了
glfwMakeContextCurrent(window);
glewExperimental = true;
// GLEW_OK 0
//init GLEW
if (glewInit() != GLEW_OK) {
cout << "glew init failed." << endl;
// 终止 glfw
glfwTerminate();
return -1;
}
// OpenGL渲染窗口的尺寸大小
// glViewport函数前两个参数控制窗口左下角的位置。第三个和第四个参数控制渲染窗口的宽度和高度(像素)
glViewport(0, 0, 800, 600);
// 设置剔除 (opegl默认正面背面都显示(不剔除))
//glEnable(GL_CULL_FACE);
// 剔除背面 GL_BACK 剔除正面 GL_FRONT
//glCullFace(GL_BACK);
// 线框模式
//第一个参数表示我们打算将其应用到所有的三角形的正面和背面,第二个参数告诉我们用线来绘制
//glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
//VAO对象
unsigned int VAO;
// 生成一个VAO对象 这个方法可以生成多个 由第一个参数决定
glGenVertexArrays(1, &VAO);
// 绑定 VAO
glBindVertexArray(VAO);
unsigned int VBO; //如果多个可以用 VBO[]数组 这个方法可以生成多个 由第一个参数决定
glGenBuffers(1, &VBO);
//将新创建的缓冲绑定到 GL_ARRAY_BUFFER目标上
glBindBuffer(GL_ARRAY_BUFFER, VBO);
// glBufferData 是一个专门用来把用户定义的数据复制到当前绑定缓冲的函数
// GL_STATIC_DRAW 数据不会或几乎不会改变。
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
unsigned int EBO;
glGenBuffers(1, &EBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
Shaders *shaders = new Shaders("./shaders/vertexSource.txt", "./shaders/fragmentSource.txt");
// glVertexAttribPointer函数告诉OpenGL该如何解析顶点数据(应用到逐个顶点属性上)
// 从 0号栏位 开始 将数据每三个为一组 单位为float 每次跳6*float字节 偏移为0
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void *) 0);
// 以顶点属性位置值作为参数,启用顶点属性;顶点属性默认是禁用的
//读取到0号栏位上
glEnableVertexAttribArray(0);
// 读取颜色属性 从 1号栏位 开始 将数据每三个为一组 单位为float 每次跳6*float字节 偏移为3个float
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void *) (3 * sizeof(float)));
// 读取到1号栏位上
glEnableVertexAttribArray(1);
// 读取纹理坐标
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void *) (6 * sizeof(float)));
glEnableVertexAttribArray(2);
// 纹理缓冲对象
unsigned int TexBufferA;
unsigned int TexBufferB;
//创建纹理缓冲对象
glGenTextures(1, &TexBufferA);
// 绑定
//glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, TexBufferA);
//glActiveTexture(GL_TEXTURE3);
// stb_image.h 使用
int width, height, nrchannels;
// 在OpenGl中 y轴的读取是与正常图片相反了 所以需要 设置 翻转(flip)垂直(vertically)加载(load) 为true
stbi_set_flip_vertically_on_load(true);
//获取图片的rgb
unsigned char *data1 = stbi_load("container.jpg", &width, &height, &nrchannels, 0);
cout<<width<<" "<<height<<endl;
if (data1) {
// 纹理可以通过glTexImage2D来生成
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data1);
//直接在生成纹理之后调用glGenerateMipmap。这会为当前绑定的纹理自动生成所有需要的多级渐远纹理。 必须要有 glGenerateMipmap 否则无法显示纹理
glGenerateMipmap(GL_TEXTURE_2D);
} else {
cout << "load image failed." << endl;
}
//释放图像的内存
stbi_image_free(data1);
glBindTexture(GL_TEXTURE_2D,0);
glGenTextures(1, &TexBufferB);
glBindTexture(GL_TEXTURE_2D, TexBufferB);
unsigned char *data2 = stbi_load("ground-texture.jpg", &width, &height, &nrchannels, 0);
cout<<width<<" "<<height<<endl;
if (data2) {
// 纹理可以通过glTexImage2D来生成
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data2);
//直接在生成纹理之后调用glGenerateMipmap。这会为当前绑定的纹理自动生成所有需要的多级渐远纹理。
glGenerateMipmap(GL_TEXTURE_2D);
} else {
cout << "load image failed." << endl;
}
stbi_image_free(data2);
glBindTexture(GL_TEXTURE_2D,0);
//渲染循环 ,它能在我们让GLFW退出前一直保持运行。下面几行的代码就实现了一个简单的渲染循环:
//glfwWindowShouldClose 我们每次循环的开始前检查一次GLFW是否被要求退出
while (!glfwWindowShouldClose(window)) {
//自定义事件 当键盘触发esc 退出
processInput(window);
glClearColor(0.2, 0.3, 0.3, 1.0);
// GL_COLOR_BUFFER_BIT 颜色,GL_DEPTH_BUFFER_BIT 深度 和 GL_STENCIL_BUFFER_BIT 模板
// 清除前面的那一帧的颜色
glClear(GL_COLOR_BUFFER_BIT);
// 绑定 TextureBuffer
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, TexBufferA);
glActiveTexture(GL_TEXTURE3);
glBindTexture(GL_TEXTURE_2D, TexBufferB);
// 绑定 VAO
glBindVertexArray(VAO);
shaders->use();
// 如果只有单个纹理,则不需要以下操作,OpenGL会自动绑定
glUniform1i(glGetUniformLocation(shaders->ID, "ourTexture1"), 0);
glUniform1i(glGetUniformLocation(shaders->ID, "ourTexture3"), 3);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);//可以不需要这个 绑定VAO的同时也会自动绑定EBO
//glDrawElements函数从当前绑定到GL_ELEMENT_ARRAY_BUFFER目标的EBO中获取索引
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
//解绑VAO
glBindVertexArray(0);
glfwSwapBuffers(window);
glfwPollEvents();
}
// 最后终止 glfw
glfwTerminate();
return 0;
}
shaders
顶点着色器
#version 330 core
// 0号栏位读取顶点坐标
layout (location = 0) in vec3 aPos;
//1号栏位读取颜色颜色
layout (location = 1) in vec3 aColor;
// 2号栏位读取 纹理坐标
layout (location = 2) in vec2 aTexCoord;
out vec4 vertexColor;
out vec2 TexCoord;
void main()
{
// gl_Position 是固定的名称 用来保存 顶点坐标的
gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);
vertexColor = vec4(aColor.x,aColor.y,aColor.z,1.0);
TexCoord = aTexCoord;
}
片元着色器
#version 330 core
out vec4 FragColor;
//uniform vec4 outColor;
in vec4 vertexColor;
// 获取顶底着色器过来的纹理坐标
in vec2 TexCoord;
uniform sampler2D ourTexture1;
uniform sampler2D ourTexture3;
void main()
{
//FragColor = vertexColor;
// 采样纹理的颜色 第一个参数是纹理采样器 是cpu传过来的 第二个参数的对应的纹理坐标
FragColor = mix(texture(ourTexture1,TexCoord) , texture(ourTexture3,TexCoord),0.55);
}