218 lines
5.6 KiB
C++
218 lines
5.6 KiB
C++
|
|
#include <renderer/shader.h>
|
||
|
|
#include <utils/logger.h>
|
||
|
|
#include <glad/glad.h>
|
||
|
|
#include <fstream>
|
||
|
|
#include <sstream>
|
||
|
|
|
||
|
|
namespace extra2d {
|
||
|
|
|
||
|
|
Shader::Shader() = default;
|
||
|
|
|
||
|
|
Shader::~Shader() {
|
||
|
|
if (program_ != 0) {
|
||
|
|
glDeleteProgram(program_);
|
||
|
|
program_ = 0;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
bool Shader::loadFromFile(const std::string& vsPath, const std::string& fsPath) {
|
||
|
|
// 读取顶点着色器
|
||
|
|
std::ifstream vsFile(vsPath);
|
||
|
|
if (!vsFile.is_open()) {
|
||
|
|
E2D_LOG_ERROR("Failed to open vertex shader: {}", vsPath);
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
std::stringstream vsStream;
|
||
|
|
vsStream << vsFile.rdbuf();
|
||
|
|
std::string vsSource = vsStream.str();
|
||
|
|
|
||
|
|
// 读取片段着色器
|
||
|
|
std::ifstream fsFile(fsPath);
|
||
|
|
if (!fsFile.is_open()) {
|
||
|
|
E2D_LOG_ERROR("Failed to open fragment shader: {}", fsPath);
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
std::stringstream fsStream;
|
||
|
|
fsStream << fsFile.rdbuf();
|
||
|
|
std::string fsSource = fsStream.str();
|
||
|
|
|
||
|
|
return loadFromSource(vsSource, fsSource);
|
||
|
|
}
|
||
|
|
|
||
|
|
bool Shader::loadFromSource(const std::string& vsSource, const std::string& fsSource) {
|
||
|
|
// 删除旧程序
|
||
|
|
if (program_ != 0) {
|
||
|
|
glDeleteProgram(program_);
|
||
|
|
program_ = 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
uniformCache_.clear();
|
||
|
|
|
||
|
|
// 处理源码(添加版本声明)
|
||
|
|
std::string processedVS = addVersionIfNeeded(vsSource, GL_VERTEX_SHADER);
|
||
|
|
std::string processedFS = addVersionIfNeeded(fsSource, GL_FRAGMENT_SHADER);
|
||
|
|
|
||
|
|
// 编译顶点着色器
|
||
|
|
GLuint vs = compileShader(GL_VERTEX_SHADER, processedVS);
|
||
|
|
if (vs == 0) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
// 编译片段着色器
|
||
|
|
GLuint fs = compileShader(GL_FRAGMENT_SHADER, processedFS);
|
||
|
|
if (fs == 0) {
|
||
|
|
glDeleteShader(vs);
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
// 链接程序
|
||
|
|
if (!linkProgram(vs, fs)) {
|
||
|
|
glDeleteShader(vs);
|
||
|
|
glDeleteShader(fs);
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
// 清理着色器对象
|
||
|
|
glDeleteShader(vs);
|
||
|
|
glDeleteShader(fs);
|
||
|
|
|
||
|
|
E2D_LOG_DEBUG("Shader program created successfully");
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
void Shader::bind() const {
|
||
|
|
if (program_ != 0) {
|
||
|
|
glUseProgram(program_);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void Shader::unbind() const {
|
||
|
|
glUseProgram(0);
|
||
|
|
}
|
||
|
|
|
||
|
|
void Shader::setUniformBlock(const std::string& name, uint32_t binding) {
|
||
|
|
if (program_ == 0) return;
|
||
|
|
|
||
|
|
GLuint index = glGetUniformBlockIndex(program_, name.c_str());
|
||
|
|
if (index != GL_INVALID_INDEX) {
|
||
|
|
glUniformBlockBinding(program_, index, binding);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void Shader::setInt(const std::string& name, int value) {
|
||
|
|
GLint location = getUniformLocation(name);
|
||
|
|
if (location != -1) {
|
||
|
|
glUniform1i(location, value);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void Shader::setFloat(const std::string& name, float value) {
|
||
|
|
GLint location = getUniformLocation(name);
|
||
|
|
if (location != -1) {
|
||
|
|
glUniform1f(location, value);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void Shader::setVec2(const std::string& name, float x, float y) {
|
||
|
|
GLint location = getUniformLocation(name);
|
||
|
|
if (location != -1) {
|
||
|
|
glUniform2f(location, x, y);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void Shader::setVec4(const std::string& name, float x, float y, float z, float w) {
|
||
|
|
GLint location = getUniformLocation(name);
|
||
|
|
if (location != -1) {
|
||
|
|
glUniform4f(location, x, y, z, w);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void Shader::setMat4(const std::string& name, const float* value) {
|
||
|
|
GLint location = getUniformLocation(name);
|
||
|
|
if (location != -1) {
|
||
|
|
glUniformMatrix4fv(location, 1, GL_FALSE, value);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
GLuint Shader::compileShader(GLenum type, const std::string& source) {
|
||
|
|
GLuint shader = glCreateShader(type);
|
||
|
|
|
||
|
|
const char* src = source.c_str();
|
||
|
|
glShaderSource(shader, 1, &src, nullptr);
|
||
|
|
glCompileShader(shader);
|
||
|
|
|
||
|
|
// 检查编译状态
|
||
|
|
GLint success;
|
||
|
|
glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
|
||
|
|
if (!success) {
|
||
|
|
char infoLog[512];
|
||
|
|
glGetShaderInfoLog(shader, 512, nullptr, infoLog);
|
||
|
|
|
||
|
|
const char* typeStr = (type == GL_VERTEX_SHADER) ? "vertex" : "fragment";
|
||
|
|
E2D_LOG_ERROR("{} shader compilation failed: {}", typeStr, infoLog);
|
||
|
|
|
||
|
|
glDeleteShader(shader);
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
return shader;
|
||
|
|
}
|
||
|
|
|
||
|
|
bool Shader::linkProgram(GLuint vertexShader, GLuint fragmentShader) {
|
||
|
|
program_ = glCreateProgram();
|
||
|
|
glAttachShader(program_, vertexShader);
|
||
|
|
glAttachShader(program_, fragmentShader);
|
||
|
|
glLinkProgram(program_);
|
||
|
|
|
||
|
|
// 检查链接状态
|
||
|
|
GLint success;
|
||
|
|
glGetProgramiv(program_, GL_LINK_STATUS, &success);
|
||
|
|
if (!success) {
|
||
|
|
char infoLog[512];
|
||
|
|
glGetProgramInfoLog(program_, 512, nullptr, infoLog);
|
||
|
|
E2D_LOG_ERROR("Shader program linking failed: {}", infoLog);
|
||
|
|
|
||
|
|
glDeleteProgram(program_);
|
||
|
|
program_ = 0;
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
GLint Shader::getUniformLocation(const std::string& name) {
|
||
|
|
if (program_ == 0) return -1;
|
||
|
|
|
||
|
|
// 检查缓存
|
||
|
|
auto it = uniformCache_.find(name);
|
||
|
|
if (it != uniformCache_.end()) {
|
||
|
|
return it->second;
|
||
|
|
}
|
||
|
|
|
||
|
|
// 查询 uniform 位置
|
||
|
|
GLint location = glGetUniformLocation(program_, name.c_str());
|
||
|
|
uniformCache_[name] = location;
|
||
|
|
|
||
|
|
return location;
|
||
|
|
}
|
||
|
|
|
||
|
|
std::string Shader::addVersionIfNeeded(const std::string& source, GLenum type) {
|
||
|
|
// 如果已经包含版本声明,直接返回
|
||
|
|
if (source.find("#version") != std::string::npos) {
|
||
|
|
return source;
|
||
|
|
}
|
||
|
|
|
||
|
|
// 添加 OpenGL ES 3.2 版本声明
|
||
|
|
std::string result = "#version 320 es\n";
|
||
|
|
|
||
|
|
// 片段着色器需要添加精度声明
|
||
|
|
if (type == GL_FRAGMENT_SHADER) {
|
||
|
|
result += "precision mediump float;\n";
|
||
|
|
}
|
||
|
|
|
||
|
|
result += source;
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
|
||
|
|
} // namespace extra2d
|