在前面的文章中我们已经了解了Shader在OpenGL中的使用流程,但是这样每一次都要链接与创建实在是很不方便。而在c++中,类对象能很好的帮助我们实现重复的功能模块,我们需要自定义一个Shader类,如下:
#ifndef SHADERTOOL_H
#define SHADERTOOL_H
#include <glad/glad.h>
#include <string>
#include <fstream>
#include <sstream>
#include <iostream>
class Shader
{
public:
unsigned int ID;
Shader(const char *vertexPath, const char *fragmentPath)
{
// 声明临时变量
std::string vertexCode;
std::string fragmentCode;
std::ifstream vShaderFile;
std::ifstream fShaderFile;
// 确保文件流可以输出错误信息
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;
// 读取文件buffer到流
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_SUCCESSFULLY_READ: " << e.what() << std::endl;
}
const char *vShaderCode = vertexCode.c_str();
const char *fShaderCode = fragmentCode.c_str();
// 组合shader
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");
glDeleteShader(vertex);
glDeleteShader(fragment);
}
/// @brief 调用Shader
void use()
{
glUseProgram(ID);
}
/// @brief Shader全局bool参数设置
/// @param name Shader参数名
/// @param value 参数值
void setBool(const std::string &name, bool value) const
{
glUniform1i(glGetUniformLocation(ID, name.c_str()), (int)value);
}
/// @brief Shader全局int参数设置
/// @param name Shader属性名
/// @param value 属性值
void setInt(const std::string &name, int value) const
{
glUniform1i(glGetUniformLocation(ID, name.c_str()), value);
}
/// @brief Shader全局float参数设置
/// @param name Shader属性名
/// @param value 属性值
void setFloat(const std::string &name, float value) const
{
glUniform1f(glGetUniformLocation(ID, name.c_str()), value);
}
/// @brief 销毁进程
void release()
{
glDeleteProgram(ID);
}
private:
/// @brief 检测Shader组合与编译的工具方法
/// @param shader shaderID
/// @param type 检测的类型
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
使用:
顶点Shader
#version 330 core
layout (location=0) in vec3 aPos;
layout (location=1) in vec3 color;
out vec3 vertexColor;
void main()
{
gl_Position = vec4(aPos.x,aPos.y,aPos.z,1.0);
vertexColor = color;
}
片元Shader:
#version 330 core
in vec3 vertexColor;
out vec4 fragColor;
void main()
{
fragColor=vec4(vertexColor,1.0);
}
主函数
#include <shadertool.h>
#include <GLFW/glfw3.h>
unsigned int Screen_Width = 800;
unsigned int Screen_Height = 600;
void player_input(GLFWwindow *window)
{
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
glfwSetWindowShouldClose(window, true);
}
void frame_sizebuffer_callback(GLFWwindow *window, int width, int height)
{
glViewport(0, 0, width, height);
}
int main()
{
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_COMPAT_PROFILE, GLFW_OPENGL_CORE_PROFILE);
#ifdef __APPLE__
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE);
#endif
GLFWwindow *window = glfwCreateWindow(Screen_Width, Screen_Height, "DrawTriangle", NULL, NULL);
if (window == NULL)
{
std::cout << "Fail to create window !" << std::endl;
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
glfwSetFramebufferSizeCallback(window, frame_sizebuffer_callback);
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout << "Fail to initialize glad !" << std::endl;
return -1;
}
Shader triangle_shader("shaders/vertex.vs", "shaders/fragment.fs");
float vertices[] = {
// 位置 // 顶点色
0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, // 右下
-0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, // 左下
0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 1.0f // 上
};
unsigned int VBO, VAO;
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
// 先绑定VAO,再绑定和设置VBO,然后配置顶点属性
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
// 顶点位置属性
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void *)0);
glEnableVertexAttribArray(0);
// 顶点颜色属性
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void *)(3 * sizeof(float)));
glEnableVertexAttribArray(1);
while (!glfwWindowShouldClose(window))
{
/* code */
player_input(window);
glClear(GL_COLOR_BUFFER_BIT);
triangle_shader.use();
glBindVertexArray(VAO);
glDrawArrays(GL_TRIANGLES, 0, 3);
glfwSwapBuffers(window);
glfwPollEvents();
}
glDeleteVertexArrays(1, &VAO);
glDeleteBuffers(1, &VBO);
triangle_shader.release();
glfwTerminate();
return 0;
}