上一篇将 vao vbo写入类中进行封装,本篇将shader进行封装。
Shader shader("res/shaders/Basic.shader");
shader.Bind();
shader.SetUniform4f("u_Color", 0.2f, 0.3f, 0.8f, 1.0f);
shader.h
#pragma once
#include
#include
struct ShaderProgramSource
{
std::string VertexSource;
std::string FragmentSource;
};
class Shader
{
private:
std::string m_FilePath;
unsigned int m_RendererID;
// caching for uniforms
std::unordered_map<std::string, int> m_UniformLocationCache;
public:
Shader(const std::string& filepath);
~Shader();
void Bind() const;
void UnBind() const;
//Set Uniform
void SetUniform4f(const std::string& name, float v0, float v1, float v2, float v3);
private:
ShaderProgramSource ParseShader(const std::string& filepath);
unsigned int CompileShader(unsigned int type, const std::string& source);
unsigned int CreateShader(const std::string& vertexShader, const std::string& fragmentShader);
int GetUniformLocation(const std::string& name);
};
shader.cpp
#include "Shader.h"
#include
#include
#include
#include
#include "Renderer.h"
Shader::Shader(const std::string& filepath)
: m_FilePath(filepath), m_RendererID(0)
{
ShaderProgramSource source = ParseShader(filepath);
m_RendererID = CreateShader(source.VertexSource, source.FragmentSource);
}
Shader::~Shader()
{
GLCall(glDeleteProgram(m_RendererID));
}
void Shader::Bind() const
{
GLCall(glUseProgram(m_RendererID));
}
void Shader::UnBind() const
{
GLCall(glUseProgram(0));
}
void Shader::SetUniform4f(const std::string& name, float v0, float v1, float v2, float v3)
{
GLCall(glUniform4f(GetUniformLocation(name), v0, v1, v2, v3));
}
int Shader::GetUniformLocation(const std::string& name)
{
if (m_UniformLocationCache.find(name) != m_UniformLocationCache.end())
{
return m_UniformLocationCache[name];
}
GLCall( int location = glGetUniformLocation(m_RendererID, name.c_str()));
if (location == -1)
{
std::cout << "Warning: unifrom '" << name << "' doesn't exist!" << std::endl;
}
m_UniformLocationCache[name] = location;
return location;
}
ShaderProgramSource Shader::ParseShader(const std::string& filepath) {
std::ifstream stream(filepath);
enum class ShaderType { /* 带作用域的枚举类型,不是类*/
NONE = -1, VERTEX = 0, FRAGMENT = 1
};
std::string line;
std::stringstream ss[2];
ShaderType type = ShaderType::NONE;
while (getline(stream, line)) {
if (line.find("#shader") != std::string::npos) { /* 找到了*/
if (line.find("vertex") != std::string::npos) {
// set mode to vertex
type = ShaderType::VERTEX;
}
else if (line.find("fragment") != std::string::npos) {
// set mode to fragment
type = ShaderType::FRAGMENT;
}
}
else {
ss[(int)type] << line << '\n';
}
}
return { ss[0].str(), ss[1].str() };
}
unsigned int Shader::CompileShader(unsigned int type, const std::string& source) {
unsigned int id = glCreateShader(type);/*vertex 或者 fragment */
const char* src = source.c_str(); /*或者写 &source[0]*/
glShaderSource(id, 1, &src, nullptr);
glCompileShader(id);
int result;
glGetShaderiv(id, GL_COMPILE_STATUS, &result);
if (result == GL_FALSE) {
int length;
glGetShaderiv(id, GL_INFO_LOG_LENGTH, &length);
// char message[length]; /*这里会发现因为长度不定,无法栈分配,但你仍要这么做*/
char* message = (char*)alloca(length * sizeof(char));
glGetShaderInfoLog(id, length, &length, message);
std::cout << "Failed to compile " <<
(type == GL_VERTEX_SHADER ? "vertex" : "fragment") << "shader!请定位到此行" << std::endl;
std::cout << message << std::endl;
glDeleteShader(id);
return 0;
}
return id;
}
unsigned int Shader::CreateShader(const std::string& vertexShader, const std::string& fragmentShader) {
/*使用unsigned是因为它接受的参数就是这样,
或者可以使用 GLuint,但是作者不喜欢这样,因为它要使用多个图像api*/
unsigned int program = glCreateProgram();
unsigned int vs = CompileShader(GL_VERTEX_SHADER, vertexShader);
unsigned int fs = CompileShader(GL_FRAGMENT_SHADER, fragmentShader);
glAttachShader(program, vs);
glAttachShader(program, fs);
glLinkProgram(program);
glValidateProgram(program);
glDeleteShader(vs);
glDeleteShader(fs);
return program;
}