feat(shaders_lab): scaffold C++ app with GLSL live-reload canvas
- cpp/functions/gfx: gl_shader, gl_framebuffer, fullscreen_quad, shader_canvas - cpp/apps/shaders_lab: main + 3 seed shaders (plasma, circle, checker) - ImGui docking layout: Code | Canvas | Controls Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,119 @@
|
||||
#define GL_GLEXT_PROTOTYPES
|
||||
#include <GL/gl.h>
|
||||
#include <GL/glext.h>
|
||||
|
||||
#include "gfx/gl_shader.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <regex>
|
||||
#include <string>
|
||||
|
||||
namespace fn::gfx {
|
||||
|
||||
static const char* k_vert_src = R"glsl(
|
||||
#version 330 core
|
||||
const vec2 verts[6] = vec2[](
|
||||
vec2(-1,-1), vec2(1,-1), vec2(-1,1),
|
||||
vec2(1,-1), vec2(1,1), vec2(-1,1)
|
||||
);
|
||||
void main() { gl_Position = vec4(verts[gl_VertexID], 0.0, 1.0); }
|
||||
)glsl";
|
||||
|
||||
static const char* k_frag_preamble =
|
||||
"#version 330 core\n"
|
||||
"out vec4 fragColor;\n"
|
||||
"uniform vec2 u_resolution;\n"
|
||||
"uniform float u_time;\n"
|
||||
"uniform vec2 u_mouse;\n";
|
||||
|
||||
static int parse_err_line(const char* log) {
|
||||
// Try "ERROR: 0:<line>:" format
|
||||
std::regex re1(R"(ERROR:\s*\d+:(\d+):)");
|
||||
// Try "0(<line>)" format
|
||||
std::regex re2(R"(\d+\((\d+)\))");
|
||||
std::cmatch m;
|
||||
if (std::regex_search(log, m, re1)) {
|
||||
return std::stoi(m[1].str());
|
||||
}
|
||||
if (std::regex_search(log, m, re2)) {
|
||||
return std::stoi(m[1].str());
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static unsigned int compile_shader(GLenum type, const char** srcs, int count, std::string& out_err, int& out_line) {
|
||||
unsigned int s = glCreateShader(type);
|
||||
glShaderSource(s, count, srcs, nullptr);
|
||||
glCompileShader(s);
|
||||
GLint ok = 0;
|
||||
glGetShaderiv(s, GL_COMPILE_STATUS, &ok);
|
||||
if (!ok) {
|
||||
GLint len = 0;
|
||||
glGetShaderiv(s, GL_INFO_LOG_LENGTH, &len);
|
||||
out_err.resize(static_cast<size_t>(len));
|
||||
glGetShaderInfoLog(s, len, nullptr, &out_err[0]);
|
||||
out_line = parse_err_line(out_err.c_str());
|
||||
glDeleteShader(s);
|
||||
return 0;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
CompileResult compile_fragment(const std::string& user_fragment_src) {
|
||||
CompileResult result;
|
||||
|
||||
// Vertex shader (no preamble needed — it's a standalone complete shader)
|
||||
std::string vert_err;
|
||||
int vert_line = -1;
|
||||
unsigned int vert = compile_shader(GL_VERTEX_SHADER, &k_vert_src, 1, vert_err, vert_line);
|
||||
if (!vert) {
|
||||
result.err_msg = "vertex: " + vert_err;
|
||||
result.err_line = vert_line;
|
||||
return result;
|
||||
}
|
||||
|
||||
// Fragment shader — prepend preamble, then user body
|
||||
const char* frag_srcs[2] = { k_frag_preamble, user_fragment_src.c_str() };
|
||||
std::string frag_err;
|
||||
int frag_line = -1;
|
||||
unsigned int frag = compile_shader(GL_FRAGMENT_SHADER, frag_srcs, 2, frag_err, frag_line);
|
||||
if (!frag) {
|
||||
glDeleteShader(vert);
|
||||
result.err_msg = frag_err;
|
||||
// Adjust line: subtract preamble lines (4 lines)
|
||||
if (frag_line > 4) result.err_line = frag_line - 4;
|
||||
else result.err_line = frag_line;
|
||||
return result;
|
||||
}
|
||||
|
||||
// Link
|
||||
unsigned int prog = glCreateProgram();
|
||||
glAttachShader(prog, vert);
|
||||
glAttachShader(prog, frag);
|
||||
glLinkProgram(prog);
|
||||
glDeleteShader(vert);
|
||||
glDeleteShader(frag);
|
||||
|
||||
GLint ok = 0;
|
||||
glGetProgramiv(prog, GL_LINK_STATUS, &ok);
|
||||
if (!ok) {
|
||||
GLint len = 0;
|
||||
glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &len);
|
||||
result.err_msg.resize(static_cast<size_t>(len));
|
||||
glGetProgramInfoLog(prog, len, nullptr, &result.err_msg[0]);
|
||||
result.err_line = parse_err_line(result.err_msg.c_str());
|
||||
glDeleteProgram(prog);
|
||||
return result;
|
||||
}
|
||||
|
||||
result.program = prog;
|
||||
result.ok = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
void delete_program(unsigned int program) {
|
||||
if (program) glDeleteProgram(program);
|
||||
}
|
||||
|
||||
} // namespace fn::gfx
|
||||
Reference in New Issue
Block a user