learn-opengl/16-scene-refactor/src/shader.cpp
2026-08-26 19:56:01 -04:00

193 lines
6.4 KiB
C++

#include "shader.hpp"
#include <iostream>
#include <fstream>
#include <sstream>
#include <print>
#include <filesystem>
#include "dbc.hpp"
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
namespace fs = std::filesystem;
inline std::string read_file(const std::string& filename) {
// load the file
std::ifstream in_file{filename, std::ios::binary};
// get the size of the file
std::stringstream in_str;
in_str << in_file.rdbuf();
return in_str.str();
}
void check_error(const std::string& what, unsigned int thing, GLenum check_type) {
int success = 0;
char infoLog[512] = {0};
if(check_type == GL_LINK_STATUS) {
glGetProgramiv(thing, check_type, &success);
if(!success) {
glGetProgramInfoLog(thing, 512, NULL, infoLog);
dbc::sentinel(std::format("ERROR: Program {} compile failed: {}", what, infoLog));
}
} else {
glGetShaderiv(thing, check_type, &success);
if(!success) {
glGetShaderInfoLog(thing, 512, NULL, infoLog);
dbc::sentinel(std::format("ERROR: Shader {} compile failed: {}", what, infoLog));
}
}
}
unsigned int Shader::load_shader(const std::string& filename, GLenum shader_type) {
dbc::check(fs::exists(filename),
std::format("shader file {} does not exist", filename));
// create the shader
std::string shader_code = read_file(filename);
const char* shader_code_ptr = shader_code.c_str();
int shader_id = glCreateShader(shader_type);
glShaderSource(shader_id, 1, &shader_code_ptr, NULL);
glCompileShader(shader_id);
check_error(filename, shader_id, GL_COMPILE_STATUS);
// check compile error
return shader_id;
}
Shader::Shader(const std::string& vertexPath, const std::string& fragmentPath) {
unsigned int vertex = load_shader(vertexPath, GL_VERTEX_SHADER);
unsigned int fragment = load_shader(fragmentPath, GL_FRAGMENT_SHADER);
ID = glCreateProgram();
glAttachShader(ID, vertex);
glAttachShader(ID, fragment);
glLinkProgram(ID);
check_error("link", ID, GL_LINK_STATUS);
glDeleteShader(vertex);
glDeleteShader(fragment);
}
void Shader::use() const {
glUseProgram(ID);
}
void Shader::cleanup() {
glDeleteProgram(ID);
}
void Shader::setBool(const std::string &name, bool value) const {
auto uniform = glGetUniformLocation(ID, name.c_str());
glUniform1i(uniform, (int)value);
}
void Shader::setInt(const std::string &name, int value) const {
auto uniform = glGetUniformLocation(ID, name.c_str());
glUniform1i(uniform, value);
}
void Shader::setFloat(const std::string &name, float value) const {
auto uniform = glGetUniformLocation(ID, name.c_str());
glUniform1f(uniform, value);
}
void Shader::setVec4(const std::string &name, float v1, float v2, float v3, float v4) const {
auto uniform = glGetUniformLocation(ID, name.c_str());
glUniform4f(uniform, v1, v2, v3, v4);
}
void Shader::setVec4(const std::string &name, const glm::vec4& value) const
{
auto uniform = glGetUniformLocation(ID, name.c_str());
glUniform4fv(uniform, 1, &value[0]);
}
void Shader::setVec3(const std::string &name, const glm::vec3& value) const
{
auto uniform = glGetUniformLocation(ID, name.c_str());
glUniform3fv(uniform, 1, &value[0]);
}
void Shader::setVec3(const std::string &name, float v1, float v2, float v3) const {
auto uniform = glGetUniformLocation(ID, name.c_str());
glUniform3f(uniform, v1, v2, v3);
}
void Shader::setMat4(const std::string &name, const glm::mat4& mat) const {
unsigned int loc = glGetUniformLocation(ID, name.c_str());
glUniformMatrix4fv(loc, 1, GL_FALSE, glm::value_ptr(mat));
}
void Shader::apply_material(const Material& material) {
setVec3("material.ambient", material.ambient);
setFloat("material.shininess", material.shininess);
}
void Shader::apply_lighting(const Lighting& lighting) {
setInt("pointLightCount", lighting.positioned.size());
setInt("dirLightCount", lighting.directional.size());
// need +1 for the camera spot light
setInt("spotLightCount", lighting.spot.size() + 1);
for(size_t i = 0; i < lighting.directional.size(); i++) {
apply_dir_light(lighting.directional[i], i);
}
for(size_t i = 0; i < lighting.positioned.size(); i++) {
apply_point_light(lighting.positioned[i], i);
}
// set the first spotlight to the camera light
apply_spot_light(lighting.camera, 0);
for(size_t i = 0; i < lighting.spot.size(); i++) {
// need to be off by one because the camera is a spot light
apply_spot_light(lighting.spot[i], i+1);
}
}
void Shader::apply_dir_light(const Light& light, size_t index) {
dbc::check(index < MAX_LIGHTS, "too many directional lights");
setVec3(std::format("dirLights[{}].direction", index), light.direction);
setVec3(std::format("dirLights[{}].ambient", index), light.ambient);
setVec3(std::format("dirLights[{}].diffuse", index), light.diffuse);
setVec3(std::format("dirLights[{}].specular", index), light.specular);
}
void Shader::apply_point_light(const Light& light, size_t index) {
dbc::check(index < MAX_LIGHTS, "too many positioned lights");
// disgusting, crafting a string on every render?
setVec3(std::format("pointLights[{}].position", index), light.position);
setVec3(std::format("pointLights[{}].ambient", index), light.ambient);
setVec3(std::format("pointLights[{}].diffuse", index), light.diffuse);
setVec3(std::format("pointLights[{}].specular", index), light.specular);
setFloat(std::format("pointLights[{}].constant", index), light.constant);
setFloat(std::format("pointLights[{}].linear", index), light.linear);
setFloat(std::format("pointLights[{}].quadratic", index), light.quadratic);
}
void Shader::apply_spot_light(const Light& light, size_t index) {
dbc::check(index < MAX_LIGHTS, "too many spot lights");
setVec3(std::format("spotLights[{}].position", index), light.position);
setVec3(std::format("spotLights[{}].direction", index), light.direction);
setVec3(std::format("spotLights[{}].ambient", index), light.ambient);
setVec3(std::format("spotLights[{}].diffuse", index), light.diffuse);
setVec3(std::format("spotLights[{}].specular", index), light.specular);
setFloat(std::format("spotLights[{}].constant", index), light.constant);
setFloat(std::format("spotLights[{}].linear", index), light.linear);
setFloat(std::format("spotLights[{}].quadratic", index), light.quadratic);
setFloat(std::format("spotLights[{}].cut_off", index), glm::cos(glm::radians(light.cut_off)));
setFloat(std::format("spotLights[{}].outer_cut_off", index), glm::cos(glm::radians(light.outer_cut_off)));
}