// Demos del dominio gfx — primitivos OpenGL/shader que viven en // cpp/functions/gfx/. La pieza distintiva de shaders_lab es el // shader_canvas: framebuffer + fullscreen quad + programa GL animado por // time/resolution/mouse. #include "demos.h" #include "demo.h" #include "gfx/shader_canvas.h" #include "gfx/gl_shader.h" #include "gfx/gl_loader.h" #include #include namespace gallery { namespace { // Fragment shader sintetico — gradiente animado con celdas. Usa los uniforms // estandar que compile_fragment inyecta: u_resolution, u_time, u_mouse. const char* kShaderSrc = R"( void mainImage() { vec2 uv = gl_FragCoord.xy / u_resolution; vec2 cell = uv * 8.0; vec2 ipos = floor(cell); vec2 fpos = fract(cell) - 0.5; float t = u_time * 0.6; float wave = sin(ipos.x * 0.7 + ipos.y * 0.5 + t); float dist = length(fpos); vec3 a = vec3(0.30, 0.43, 0.96); // indigo vec3 b = vec3(0.95, 0.45, 0.85); // pink vec3 col = mix(a, b, 0.5 + 0.5 * wave); // Mouse focus: oscurecemos celdas lejanas al cursor. vec2 m = u_mouse / u_resolution; float fm = 1.0 - smoothstep(0.0, 0.6, length(uv - m)); col *= 0.6 + 0.4 * fm; // Disco interior por celda con borde suave. col *= smoothstep(0.5, 0.45, dist); fragColor = vec4(col, 1.0); } void main() { mainImage(); } )"; struct CanvasState { fn::gfx::ShaderCanvas canvas; bool compiled = false; bool compile_failed = false; std::string err_msg; std::chrono::steady_clock::time_point t0; }; CanvasState& state() { static CanvasState s; return s; } } // namespace void demo_shader_canvas() { demo_header("shader_canvas", "v1.0.0", "Framebuffer + fullscreen quad + shader GLSL animado. La misma pieza " "que usa shaders_lab para el preview en vivo. Uniforms u_time / u_resolution / u_mouse " "los inyecta gl_shader::compile_fragment automaticamente."); auto& s = state(); // Compilacion lazy (en el primer frame ya hay contexto GL valido). if (!s.compiled && !s.compile_failed) { fn::gfx::gl_loader_init(); fn::gfx::canvas_init(s.canvas); auto cr = fn::gfx::compile_fragment(kShaderSrc); if (!cr.ok) { s.compile_failed = true; s.err_msg = cr.err_msg; } else { fn::gfx::canvas_set_program(s.canvas, cr.program); s.t0 = std::chrono::steady_clock::now(); s.compiled = true; } } if (s.compile_failed) { ImGui::TextColored(ImVec4(1, 0.4f, 0.4f, 1), "Compilacion del fragment shader fallo:\n%s", s.err_msg.c_str()); return; } section("Live preview"); // Render del shader en un panel ~480x300 px. canvas_render hace resize // automatico segun GetContentRegionAvail si lo dejas crecer. ImGui::BeginChild("##shader_preview", ImVec2(480, 300), ImGuiChildFlags_Borders); const float dt = std::chrono::duration( std::chrono::steady_clock::now() - s.t0).count(); fn::gfx::canvas_render(s.canvas, dt); ImGui::EndChild(); code_block( "#include \"gfx/shader_canvas.h\"\n" "#include \"gfx/gl_shader.h\"\n\n" "static fn::gfx::ShaderCanvas canvas;\n" "// Setup (una vez):\n" "fn::gfx::canvas_init(canvas);\n" "auto cr = fn::gfx::compile_fragment(user_glsl);\n" "if (cr.ok) fn::gfx::canvas_set_program(canvas, cr.program);\n\n" "// Cada frame, dentro de un Begin/End:\n" "fn::gfx::canvas_render(canvas, time_seconds);" ); } } // namespace gallery