本章参考官方教程:learnopengl-cn
本系列历史文
OpengGL教程(一)—OpenGL环境的配置(GLFW3,GLAD)
OpengGL教程(二)—渲染一个简单的窗体
OpengGL教程(三)—使用VAO和VBO方式绘制三角形
OpengGL教程(四)—使用EBO方式绘制矩形
本章主要讲述对纹理的使用,实现功能为将纹理贴到一个三角形内。
VertexShader.glsl
#version 330 core
layout(location = 0) in vec3 aPosition;
layout(location = 1) in vec2 aTexture;
uniform mat4 projection; // 投影矩阵
out vec2 ourTexture;
void main()
{
gl_Position = projection * vec4(aPosition,1.0f);
ourTexture = aTexture;
}
FragmentShader.glsl
#version 330 core
in vec2 ourTexture;
out vec4 FragColor;
uniform sampler2D textureClass;
void main()
{
FragColor = texture(textureClass,ourTexture);
}
对于第三章中使用的GlslDealConfig这里追加了三个功能接口
void CheckShaderCompileV(GLuint vertexShader); //检查顶点着色器的编译检查
void CheckShaderCompileF(GLuint vertexShader); //检查片元着色器的编译检查
void CheckProgmaLinkStatus(GLuint Progma); //检查程序对象的链接
GlslDealConfig.h
#pragma once
#include <iostream>
#include <string>
#include <fstream>
#include <sstream>
#include "glew.h"
class GlslDealConfig
{
public:
GlslDealConfig() {}
~GlslDealConfig() {}
public:
std::string ReadGlslFile(const std::string& filename);
void CheckShaderCompileV(GLuint vShader);
void CheckShaderCompileF(GLuint fShader);
void CheckProgmaLinkStatus(GLuint Progma);
};
GlslDealConfig.cpp
#include "GlslDealConfig.h"
#include "log.h"
std::string GlslDealConfig::ReadGlslFile(const std::string& filename)
{
std::string data;
std::ifstream ifs(filename,std::ios::in);
if(!ifs.is_open())
{
printf("open %s failed",filename.c_str());
return data;
}
std::ostringstream fs;
fs << ifs.rdbuf();
data = fs.str();
return data;
}
void GlslDealConfig::CheckShaderCompileV(GLuint vShader)
{
GLint status;
GLchar infoLog[512];
glGetShaderiv(vShader, GL_COMPILE_STATUS,&status);
if(status != GL_TRUE)
{
glGetShaderInfoLog(vShader, 512, NULL, infoLog);
LOGE("Vertex Shader Compilation Error: %s\n", infoLog);
}
LOGD("Vertex Shader Compile Success: %s\n", infoLog);
}
void GlslDealConfig::CheckShaderCompileF(GLuint fShader)
{
GLint status;
GLchar infoLog[512];
glGetShaderiv(fShader, GL_COMPILE_STATUS,&status);
if(status != GL_TRUE)
{
glGetShaderInfoLog(fShader, 512, NULL, infoLog);
LOGE("Fragment Shader Compilation Error: %s\n", infoLog);
}
LOGD("Fragment Shader Compile Success: %s\n", infoLog);
}
void GlslDealConfig::CheckProgmaLinkStatus(GLuint Progma)
{
GLint status;
GLchar infoLog[512];
glGetProgramiv(Progma, GL_LINK_STATUS,&status);
if(status != GL_TRUE)
{
glGetProgramInfoLog(Progma, 512, NULL, infoLog);
LOGE("Program Link Compilation Error: %s\n", infoLog);
}
LOGD("Program Link Compile Success: %s\n", infoLog);
}
main.cpp
#include <iostream>
#include "glew.h"
#include "glfw3.h"
#include "glm/glm.hpp" // GLM 的基本数学类型,例如 glm::mat4 和 glm::vec3
#include "glm/gtc/matrix_transform.hpp" // GLM 的矩阵变换函数,例如 glm::ortho
#include "glm/gtc/type_ptr.hpp" // 用于 glm::value_ptr 函数
#include "stb_image.h"
#include "log.h"
#include "GlslDealConfig.h"
GLfloat vertices_1[] =
{
0.0f, 0.0f, 0.0f,
400.0f, 800.0f, 0.0f,
800.0f, 0.0f, 0.0f,
};
GLfloat vertices_2[] =
{
0.0f, 0.0f,
0.5f, 1.0f,
1.0f, 0.0f,
};
GLint vertices_3[] =
{
0,1,2,
};
int main()
{
GlslDealConfig mdeal;
int major = 0, minor = 0, rev = 0;
glfwGetVersion(&major, &minor, &rev);
LOGI("glfw version %d - %d - %d", major,minor,rev);
if (glfwInit() == GLFW_FALSE)
{
LOGE("glfwInit failed");
return 0;
}
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
GLFWwindow* window = glfwCreateWindow(800, 800, "texture", NULL, NULL);
if (window == NULL)
{
LOGE("glfwCreateWindow failed");
return 0;
}
glfwMakeContextCurrent(window);
GLenum err = glewInit();
if (err != GLEW_OK)
{
LOGE("glew init failed : %s", reinterpret_cast<const char*>(glewGetErrorString(err)));
return 0;
}
glfwSwapInterval(1);
std::string vertexShader = mdeal.ReadGlslFile("/home/ryan/zxp/Rendering/demo/glsl/Texture/VertexShader.glsl");
std::string fragmentShader = mdeal.ReadGlslFile("/home/ryan/zxp/Rendering/demo/glsl/Texture/FragmentShader.glsl");
const GLchar *vData = vertexShader.c_str();
const GLchar *fData = fragmentShader.c_str();
GLuint vShader = glCreateShader(GL_VERTEX_SHADER);
GLuint fShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(vShader,1,&vData,NULL);
glShaderSource(fShader,1,&fData,NULL);
glCompileShader(vShader);
glCompileShader(fShader);
mdeal.CheckShaderCompileV(vShader);
mdeal.CheckShaderCompileF(fShader);
GLuint Progma = glCreateProgram();
glAttachShader(Progma,vShader);
glAttachShader(Progma,fShader);
glLinkProgram(Progma);
mdeal.CheckProgmaLinkStatus(Progma);
glDeleteShader(vShader);
glDeleteShader(fShader);
GLuint VAO,VBO[2],EBO;
glGenVertexArrays(1, &VAO);
glGenBuffers(2, VBO);
glGenBuffers(1, &EBO);
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO[0]);
glBufferData(GL_ARRAY_BUFFER,sizeof(vertices_1),vertices_1, GL_STATIC_DRAW);
glVertexAttribPointer(0,3, GL_FLOAT,GL_FALSE,3*sizeof(GLfloat),(void*)0);
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, VBO[1]);
glBufferData(GL_ARRAY_BUFFER,sizeof(vertices_2),vertices_2, GL_STATIC_DRAW);
glVertexAttribPointer(1,2, GL_FLOAT,GL_FALSE,2*sizeof(GLfloat),(void*)0);
glEnableVertexAttribArray(1);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER,sizeof(vertices_3),vertices_3, GL_STATIC_DRAW);
unsigned int texture;
glGenTextures(1, &texture); //生成纹理对象,并将其标识符存储在 texture 变量中
glBindTexture(GL_TEXTURE_2D, texture); //将之前生成的纹理对象绑定到当前的纹理目标 GL_TEXTURE_2D
//之后的纹理操作(如设置纹理参数、加载纹理数据等)都将作用于这个绑定的纹理对象。
// 设置纹理参数
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
stbi_set_flip_vertically_on_load(true);
int width, height, nrChannels;
unsigned char *data = stbi_load("/home/ryan/zxp/Rendering/demo/resource/awesomeface.png", &width, &height, &nrChannels, 0);
LOGD("width = %d, height = %d, nrChannels = %d", width, height,nrChannels);
if (data)
{
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
}
else
{
LOGE("Failed to load texture");
}
stbi_image_free(data);
glBindVertexArray(0);
glBindTexture(GL_TEXTURE_2D, 0); //纹理属性配置完成取消绑定
glm::mat4 projection = glm::ortho(0.0f, 800.0f, 0.0f, 800.0f, -1.0f, 1.0f);
glUseProgram(Progma);
GLuint projLoc = glGetUniformLocation(Progma, "projection");
glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(projection));
// 激活纹理单元 3
glActiveTexture(GL_TEXTURE3); //激活一个纹理单元
int texturelocation = glGetUniformLocation(Progma,"textureClass");
glUniform1i(texturelocation,3); //告诉片元着色器使用那个纹理单元
// 绘制循环
while (!glfwWindowShouldClose(window))
{
glViewport(0, 0, 800, 800);
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glUseProgram(Progma);
glBindVertexArray(VAO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBindTexture(GL_TEXTURE_2D, texture);
glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_INT, 0);
glBindVertexArray(0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glBindTexture(GL_TEXTURE_2D, 0);
glfwSwapBuffers(window);
glfwPollEvents();
}
glDeleteVertexArrays(1, &VAO);
glDeleteBuffers(2, VBO);
glDeleteBuffers(1, &EBO);
glDeleteProgram(Progma);
glfwTerminate();
return 0;
}
注意:
1、因为OpenGL要求y轴(0.0)坐标是在图片的底部的,但是图片的y轴(0.0)坐标通常在顶部。很幸运,stb_image.h能够在图像加载时帮助我们翻转y轴,只需要在加载任何图像前加入以下语句即可.
stbi_set_flip_vertically_on_load(true);
2、设置纹理属性前榜绑定纹理对象,设置完成后解除绑定。绘制时候同理,每次绘制前绑定,绘制结束接触绑定。
3、注意纹理的通道个数
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
四通道为GL_RGBA三通道为GL_RGB。
运行截图:
这样我就将一个纹理贴在了指定的区域,如果我想要实现两个纹理叠加在一起的效果要如何做呢?
修改FragmentShader.glsl。
#version 330 core
in vec2 ourTexture;
out vec4 FragColor;
uniform sampler2D textureClass;
uniform sampler2D textureClass2;
void main()
{
FragColor = mix(texture(textureClass,ourTexture),texture(textureClass2,ourTexture),0.2f);
}
线性插值: m a x ( x , y , n ) = = > x ∗ ( 1 − n ) + y ∗ a 线性插值:max(x,y,n) ==> x * (1 - n) + y * a 线性插值:max(x,y,n)==>x∗(1−n)+y∗a
解释:0.2会返回80%的第一个输入颜色和20%的第二个输入颜色,即返回两个纹理的混合色。
为了简化代码我们可以将纹理对象封装为一个函数接口,我将这个函数放到了GlslDealConfig类中
// 纹理加载函数
GLuint GlslDealConfig::loadTexture(const std::string& path, GLenum format)
{
GLuint texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
// 设置纹理参数
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
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);
unsigned char* data = stbi_load(path.c_str(), &width, &height, &nrChannels, 0);
if (data) {
glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
} else {
LOGE("Failed to load texture at %s", path.c_str());
}
stbi_image_free(data);
glBindTexture(GL_TEXTURE_2D, 0);
return texture;
}
修改后的main.cpp
#include <iostream>
#include <string>
#include "glew.h"
#include "glfw3.h"
#include "glm/glm.hpp"
#include "glm/gtc/matrix_transform.hpp"
#include "glm/gtc/type_ptr.hpp"
#include "log.h"
#include "GlslDealConfig.h"
// 顶点数据
GLfloat vertices_1[] = {
0.0f, 800.0f, 0.0f,
0.0f, 0.0f, 0.0f,
800.0f, 0.0f, 0.0f,
800.0f, 800.0f, 0.0f
};
GLfloat vertices_2[] = {
0.0f, 1.0f,
0.0f, 0.0f,
1.0f, 0.0f,
1.0f, 1.0f
};
GLint indices[] = {
0, 1, 2,
0, 2, 3
};
// 主函数
int main() {
GlslDealConfig glslConfig;
// 初始化 GLFW
if (!glfwInit()) {
LOGE("Failed to initialize GLFW");
return -1;
}
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
// 创建窗口
GLFWwindow* window = glfwCreateWindow(800, 800, "Texture", nullptr, nullptr);
if (!window) {
LOGE("Failed to create GLFW window");
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
// 初始化 GLEW
GLenum err = glewInit();
if (err != GLEW_OK) {
LOGE("Failed to initialize GLEW: %s", reinterpret_cast<const char*>(glewGetErrorString(err)));
return -1;
}
glfwSwapInterval(1);
// 加载着色器
std::string vertexShaderCode = glslConfig.ReadGlslFile("/home/ryan/zxp/Rendering/demo/glsl/Texture/GLSL2/VertexShader.glsl");
std::string fragmentShaderCode = glslConfig.ReadGlslFile("/home/ryan/zxp/Rendering/demo/glsl/Texture/GLSL2/FragmentShader.glsl");
GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
const GLchar* vShaderSource = vertexShaderCode.c_str();
const GLchar* fShaderSource = fragmentShaderCode.c_str();
glShaderSource(vertexShader, 1, &vShaderSource, nullptr);
glShaderSource(fragmentShader, 1, &fShaderSource, nullptr);
glCompileShader(vertexShader);
glCompileShader(fragmentShader);
glslConfig.CheckShaderCompileV(vertexShader);
glslConfig.CheckShaderCompileF(fragmentShader);
GLuint shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);
glslConfig.CheckProgmaLinkStatus(shaderProgram);
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
// 配置 VAO, VBO, EBO
GLuint VAO, VBO[2], EBO;
glGenVertexArrays(1, &VAO);
glGenBuffers(2, VBO);
glGenBuffers(1, &EBO);
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO[0]);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices_1), vertices_1, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (void*)0);
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, VBO[1]);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices_2), vertices_2, GL_STATIC_DRAW);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(GLfloat), (void*)0);
glEnableVertexAttribArray(1);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
// 加载纹理
GLuint texture[2];
texture[0] = glslConfig.loadTexture("/home/ryan/zxp/Rendering/demo/resource/brickwall.jpg", GL_RGB);
texture[1] = glslConfig.loadTexture("/home/ryan/zxp/Rendering/demo/resource/awesomeface.png", GL_RGBA);
// 设置投影矩阵
glm::mat4 projection = glm::ortho(0.0f, 800.0f, 0.0f, 800.0f, -1.0f, 1.0f);
glUseProgram(shaderProgram);
GLuint projLoc = glGetUniformLocation(shaderProgram, "projection");
glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(projection));
// 激活纹理单元
glActiveTexture(GL_TEXTURE3);
GLuint textureLocation = glGetUniformLocation(shaderProgram, "textureClass");
glUniform1i(textureLocation, 3);
glActiveTexture(GL_TEXTURE6);
GLuint textureLocation2 = glGetUniformLocation(shaderProgram, "textureClass2");
glUniform1i(textureLocation2, 6);
// 渲染循环
while (!glfwWindowShouldClose(window)) {
glViewport(0, 0, 800, 800);
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glUseProgram(shaderProgram);
glBindVertexArray(VAO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glActiveTexture(GL_TEXTURE3);
glBindTexture(GL_TEXTURE_2D, texture[0]);
glActiveTexture(GL_TEXTURE6);
glBindTexture(GL_TEXTURE_2D, texture[1]);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
glBindVertexArray(0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glBindTexture(GL_TEXTURE_2D, 0);
glfwSwapBuffers(window);
glfwPollEvents();
}
// 清理资源
glDeleteVertexArrays(1, &VAO);
glDeleteBuffers(2, VBO);
glDeleteBuffers(1, &EBO);
glDeleteProgram(shaderProgram);
glfwTerminate();
return 0;
}
运行效果
别说,还蛮好看的伐!
感谢阅读^ _ ^
如有问题欢迎评论区交流指正,如需要对应源码资源可私聊我。