前言
在Application中写大段的shader逻辑显然不合理,而且每个shader都会走一遍编译、链接的逻辑,很直观的想到,将重复的逻辑抽象出来,写成shader类
Shader封装
提出vertex和fragmetn着色器,再加上Bind()、UnBind()方法,就组成了Shader的基本内容
Shader类
Sandbox/Hazel/src/Hazel/Renderer/Shader.h
#pragma once
#include <string>
namespace Hazel {
class Shader {
public:
Shader(const std::string& vertexSrc, const std::string& fragmentSrc);
~Shader();
void Bind() const;
void Unbind() const;
private:
uint32_t m_RendererID;
};
}
Shader实现,即将之前裸露在Application中的shader逻辑抠出来 Sandbox/Hazel/src/Hazel/Renderer/Shader.cpp
#include "Shader.h"
#include "glad/glad.h"
#include "Log.h"
#include "Base.h"
#include <vector>
namespace Hazel {
Shader::Shader(const std::string &vertexSrc, const std::string &fragmentSrc) {
// Create an empty vertex shader handle
GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
// Send the vertex shader source code to GL
// Note that std::string's .c_str is NULL character terminated.
//reference to https://www.khronos.org/opengl/wiki/Shader_Compilation#Example
const GLchar* source = vertexSrc.c_str();
glShaderSource(vertexShader, 1, &source, 0);
// Compile the vertex shader
glCompileShader(vertexShader);
GLint isCompiled = 0;
glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &isCompiled);
if (isCompiled == GL_FALSE)
{
GLint maxLength = 0;
glGetShaderiv(vertexShader, GL_INFO_LOG_LENGTH, &maxLength);
// The maxLength includes the NULL character
std::vector<GLchar> infoLog(maxLength);
glGetShaderInfoLog(vertexShader, maxLength, &maxLength, &infoLog[0]);
// We don't need the shader anymore.
glDeleteShader(vertexShader);
HZ_CORE_ERROR("{0}", infoLog.data());
HZ_CORE_ASSERT(false, "Vertex shader compilation failure!");
return;
}
// Create an empty fragment shader handle
GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
// Send the fragment shader source code to GL
// Note that std::string's .c_str is NULL character terminated.
source = fragmentSrc.c_str();
glShaderSource(fragmentShader, 1, &source, 0);
// Compile the fragment shader
glCompileShader(fragmentShader);
glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &isCompiled);
if (isCompiled == GL_FALSE)
{
GLint maxLength = 0;
glGetShaderiv(fragmentShader, GL_INFO_LOG_LENGTH, &maxLength);
// The maxLength includes the NULL character
std::vector<GLchar> infoLog(maxLength);
glGetShaderInfoLog(fragmentShader, maxLength, &maxLength, &infoLog[0]);
// We don't need the shader anymore.
glDeleteShader(fragmentShader);
// Either of them. Don't leak shaders.
glDeleteShader(vertexShader);
HZ_CORE_ERROR("{0}", infoLog.data());
HZ_CORE_ASSERT(false, "Fragment shader compilation failure!");
return;
}
// Vertex and fragment shaders are successfully compiled.
// Now time to link them together into a program.
// Get a program object.
m_RendererID = glCreateProgram();
GLuint program = m_RendererID;
// Attach our shaders to our program
glAttachShader(program, vertexShader);
glAttachShader(program, fragmentShader);
// Link our program
glLinkProgram(program);
// Note the different functions here: glGetProgram* instead of glGetShader*.
GLint isLinked = 0;
glGetProgramiv(program, GL_LINK_STATUS, (int*)&isLinked);
if (isLinked == GL_FALSE)
{
GLint maxLength = 0;
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &maxLength);
// The maxLength includes the NULL character
std::vector<GLchar> infoLog(maxLength);
glGetProgramInfoLog(program, maxLength, &maxLength, &infoLog[0]);
// We don't need the program anymore.
glDeleteProgram(program);
// Don't leak shaders either.
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
HZ_CORE_ERROR("{0}", infoLog.data());
HZ_CORE_ASSERT(false, "Shader link failure!");
return;
}
// Always detach shaders after a successful link.
glDetachShader(program, vertexShader);
glDetachShader(program, fragmentShader);
}
Shader::~Shader() {
glDeleteProgram(m_RendererID);
}
void Shader::Bind() const {
glUseProgram(m_RendererID);
}
void Shader::Unbind() const {
glUseProgram(0);
}
}
这段shader逻辑可以参考OpenGL官网opengl/wiki/Shader_Compilation
Application中调用shader
声明Shader Sandbox/Hazel/src/Hazel/Application.h
...
#include "Renderer/Shader.h"
namespace Hazel{
class Application {
public:
...
std::unique_ptr<Shader> m_Shader;
...
Sandbox/Hazel/src/Hazel/Application.cpp
Application::Application() {
...
std::string vertexSrc = R"(
#version 330 core
layout(location = 0) in vec3 a_Position;
out vec3 v_Position;
void main()
{
v_Position = a_Position;
gl_Position = vec4(a_Position, 1.0);
}
)";
std::string fragmentSrc = R"(
#version 330 core
layout(location = 0) out vec4 color;
in vec3 v_Position;
void main()
{
color = vec4(v_Position * 0.5 + 0.5, 1.0);
}
)";
m_Shader.reset(new Shader(vertexSrc, fragmentSrc));
}
void Application::PushLayer(Layer *layer) {
...
glClear(GL_COLOR_BUFFER_BIT);
m_Shader->Bind();
glBindVertexArray(m_VertexArray);
glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_INT, nullptr);
}
注意,声明字符串vertexSrc和fragmentSrc用R""的方式,保留原始格式,避免每行加\n。
完整代码参考:shader Encapsulation