【8】shader写入类中
上一篇将 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 <string>
#include <unordered_map>struct ShaderProgramSource
{std::string VertexSource;std::string FragmentSource;
};class Shader
{
private:std::string m_FilePath;unsigned int m_RendererID;// caching for uniformsstd::unordered_map<std::string, int> m_UniformLocationCache;
public:Shader(const std::string& filepath);~Shader();void Bind() const;void UnBind() const;//Set Uniformvoid 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 <fstream>
#include <sstream>
#include <string>
#include <iostream>#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 vertextype = ShaderType::VERTEX;}else if (line.find("fragment") != std::string::npos) {// set mode to fragmenttype = 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;
}