本章参考官方教程:learnopengl-cn
本章主要讲述对纹理的使用,实现功能为将纹理贴到一个三角形内。
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。
运行截图:
感谢阅读^ _ ^
如有问题欢迎评论区交流指正。