From 4a339c09d19ac9a710fec66a112d43b17c2f0459 Mon Sep 17 00:00:00 2001 From: Sergio Valor Date: Tue, 21 Oct 2025 18:38:43 +0200 Subject: [PATCH] afegits altres shaders pantalla completa tecla esc readme.md --- README.md | 79 +++++++ shaders/creation.frac.glsl | 42 ++++ shaders/fractal_pyramid.frag.glsl | 65 ++++++ shaders/kishimisu.frag.glsl | 47 +++++ shaders/octograms.frag.glsl | 104 ++++++++++ shaders/test.frag.glsl | 32 +++ src/main.cpp | 328 ++++++++++++++++++++---------- 7 files changed, 592 insertions(+), 105 deletions(-) create mode 100644 README.md create mode 100644 shaders/creation.frac.glsl create mode 100644 shaders/fractal_pyramid.frag.glsl create mode 100644 shaders/kishimisu.frag.glsl create mode 100644 shaders/octograms.frag.glsl create mode 100644 shaders/test.frag.glsl diff --git a/README.md b/README.md new file mode 100644 index 0000000..24c7864 --- /dev/null +++ b/README.md @@ -0,0 +1,79 @@ +# Shadertoy SDL3 + OpenGL + +Proyecto minimal para ejecutar fragment shaders tipo Shadertoy usando SDL3 + GLAD + OpenGL 3.3. + +## Estructura +- src/ — código fuente C++ (incluye main.cpp). +- shaders/ — shaders fragment (.frag.glsl) que se cargan en runtime. +- third_party/glad/ — glad.c + headers. +- CMakeLists.txt — configuración de build. +- .gitignore — recomendado. + +## Requisitos +- CMake >= 3.16 +- Compilador con soporte C++17 +- SDL3 development headers and library +- GL loader (GLAD) incluido en third_party/glad o disponible en el sistema + +Instalación típica: +- Debian/Ubuntu: + sudo apt install build-essential cmake libsdl3-dev +- macOS (Homebrew): + brew install cmake sdl3 + +## Build + mkdir build + cd build + cmake .. + cmake --build . --config Release + +## Uso +Por defecto el ejecutable carga shaders/test.frag.glsl. + +Ejecutar en ventana: + ./shadertoy_sdl3 ../shaders/test.frag.glsl + +Ejecutar en fullscreen nativo: + ./shadertoy_sdl3 -F ../shaders/test.frag.glsl + +Si ejecutas desde la raíz del repo: + ./build/shadertoy_sdl3 shaders/frag_tile_iter.frag.glsl + +Atajos en ejecución +- Escape — salir. +- F11 — alternar fullscreen desktop. + +## Formato de shader esperado +- #version 330 core +- Debe declarar: + uniform vec2 iResolution; + uniform float iTime; + in vec2 vUV; + out vec4 FragColor; +- Función de entrada esperada (opcional, el main llama a mainImage): + void mainImage(out vec4 fragColor, in vec2 fragCoord); +- main() debe convertir vUV a fragCoord y llamar a mainImage. + +## Rutas y ejecución +El ejecutable intenta varias rutas relativas para localizar el shader: +- ruta tal cual pasada en argv +- ./ +- ../ +- relativo al directorio del ejecutable + +Si obtienes "Failed to load fragment shader file", ejecuta desde la carpeta build o pasa la ruta correcta a tu shader. + +## Multi-pass / Buffers (nota) +Ejemplo actual es single-pass. Para portar Shadertoy con BufferA/B/C necesitas: +- crear FBOs y textures por buffer +- renderizar buffers en orden y pasar las texturas como iChannelN a pases posteriores +- evitar leer y escribir la misma textura (usar ping-pong si hace falta) + +## Troubleshooting +- SDL_Init devuelve error vacío: asegúrate que SDL3.dll (Windows) está en el mismo directorio que el ejecutable o en PATH. +- Errores de compilación/constantes: verifica que usas SDL3 headers (#include ) y no SDL2. +- Shaders que fallan a compilar: revisa glGetShaderInfoLog en la salida; versiones GLSL y funciones incompatibles son la causa habitual. + +## Licencia y créditos +- Código de ejemplo: libre para uso personal y adaptación. +- Respeta créditos de shaders copiados de Shadertoy/otros autores cuando los reutilices. diff --git a/shaders/creation.frac.glsl b/shaders/creation.frac.glsl new file mode 100644 index 0000000..be3e6f9 --- /dev/null +++ b/shaders/creation.frac.glsl @@ -0,0 +1,42 @@ +#version 330 core +precision highp float; + +out vec4 FragColor; +in vec2 vUV; +uniform vec2 iResolution; +uniform float iTime; + +#define t iTime +#define r iResolution.xy + +void mainImage(out vec4 fragColor, in vec2 fragCoord) { + vec3 c = vec3(0.0); + float l; + float z = t; + for (int i = 0; i < 3; i++) { + vec2 uv, p = fragCoord.xy / r; + uv = p; + p -= 0.5; + p.x *= r.x / r.y; + z += 0.07; + l = length(p); + // evitar división por cero + float invl = (l > 0.0) ? (1.0 / l) : 0.0; + uv += p * invl * (sin(z) + 1.0) * abs(sin(l * 9.0 - z - z)); + vec2 m = mod(uv, 1.0) - 0.5; + float denom = length(m); + // evitar división por cero en el denominador + if (denom < 1e-6) denom = 1e-6; + c[i] = 0.01 / denom; + } + // si l es cero, usar 1.0 para evitar NaN + float L = (l > 0.0) ? l : 1.0; + fragColor = vec4(c / L, t); +} + +void main() { + vec2 fragCoordPixels = vUV * iResolution; + vec4 outColor; + mainImage(outColor, fragCoordPixels); + FragColor = outColor; +} diff --git a/shaders/fractal_pyramid.frag.glsl b/shaders/fractal_pyramid.frag.glsl new file mode 100644 index 0000000..85b324d --- /dev/null +++ b/shaders/fractal_pyramid.frag.glsl @@ -0,0 +1,65 @@ +#version 330 core +precision highp float; + +out vec4 FragColor; +in vec2 vUV; +uniform vec2 iResolution; +uniform float iTime; + +vec3 palette(float d){ + return mix(vec3(0.2,0.7,0.9), vec3(1.0,0.0,1.0), d); +} + +vec2 rotate(vec2 p, float a){ + float c = cos(a); + float s = sin(a); + return p * mat2(c, s, -s, c); +} + +float mapScene(vec3 p){ + for (int i = 0; i < 8; ++i) { + float t = iTime * 0.2; + p.xz = rotate(p.xz, t); + p.xy = rotate(p.xy, t * 1.89); + p.xz = abs(p.xz); + p.xz -= 0.5; + } + return dot(sign(p), p) / 5.0; +} + +vec4 rm(vec3 ro, vec3 rd){ + float t = 0.0; + vec3 col = vec3(0.0); + float d = 1.0; + for (int i = 0; i < 64; ++i){ + vec3 p = ro + rd * t; + d = mapScene(p) * 0.5; + if (d < 0.02) break; + if (d > 100.0) break; + col += palette(length(p) * 0.1) / (400.0 * d); + t += d; + } + return vec4(col, 1.0 / (d * 100.0)); +} + +void mainImage(out vec4 fragColor, in vec2 fragCoord){ + vec2 uv = (fragCoord - (iResolution.xy * 0.5)) / iResolution.x; + vec3 ro = vec3(0.0, 0.0, -50.0); + ro.xz = rotate(ro.xz, iTime); + vec3 cf = normalize(-ro); + vec3 cs = normalize(cross(cf, vec3(0.0, 1.0, 0.0))); + vec3 cu = normalize(cross(cf, cs)); + + vec3 uuv = ro + cf * 3.0 + uv.x * cs + uv.y * cu; + vec3 rd = normalize(uuv - ro); + + vec4 col = rm(ro, rd); + fragColor = col; +} + +void main(){ + vec2 fragCoordPixels = vUV * iResolution; + vec4 outColor; + mainImage(outColor, fragCoordPixels); + FragColor = outColor; +} diff --git a/shaders/kishimisu.frag.glsl b/shaders/kishimisu.frag.glsl new file mode 100644 index 0000000..7c8ac3c --- /dev/null +++ b/shaders/kishimisu.frag.glsl @@ -0,0 +1,47 @@ +#version 330 core +precision highp float; + +out vec4 FragColor; +in vec2 vUV; +uniform vec2 iResolution; +uniform float iTime; + +vec3 palette( float t) { + vec3 a = vec3(0.5, 0.5, 0.5); + vec3 b = vec3(0.5, 0.5, 0.5); + vec3 c = vec3(1.0, 1.0, 1.0); + vec3 d = vec3(0.263, 0.416, 0.557); + return a + b * cos( 6.28318 * (c * t * d) ); +} + +void mainImage( out vec4 fragColor, in vec2 fragCoord ) +{ + vec2 uv = (fragCoord * 2.0 - iResolution.xy) / iResolution.y; + vec2 uv0 = uv; + vec3 finalColor = vec3(0.0); + + for (float i = 0.0; i < 4.0; i++) { + uv = fract(uv * 1.5) - 0.5; + + float d = length(uv) * exp(-length(uv0)); + + vec3 col = palette(length(uv0) + i * 0.4 + iTime * 0.4); + + d = sin(d * 8.0 + iTime) / 8.0; + + d = abs(d); + + d = pow(0.01 / d, 1.2); + + finalColor += col * d; + } + + fragColor = vec4(finalColor, 1.0); +} + +void main() { + vec2 fragCoordPixels = vUV * iResolution; + vec4 outColor; + mainImage(outColor, fragCoordPixels); + FragColor = outColor; +} diff --git a/shaders/octograms.frag.glsl b/shaders/octograms.frag.glsl new file mode 100644 index 0000000..4a601d6 --- /dev/null +++ b/shaders/octograms.frag.glsl @@ -0,0 +1,104 @@ +#version 330 core +precision highp float; + +out vec4 FragColor; +in vec2 vUV; +uniform vec2 iResolution; +uniform float iTime; + +float gTime = 0.0; +const float REPEAT = 5.0; + +// rotación 2D +mat2 rot(float a) { + float c = cos(a), s = sin(a); + return mat2(c, s, -s, c); +} + +float sdBox(vec3 p, vec3 b) { + vec3 q = abs(p) - b; + return length(max(q, vec3(0.0))) + min(max(q.x, max(q.y, q.z)), 0.0); +} + +float boxGeom(vec3 pos, float scale) { + pos *= scale; + float base = sdBox(pos, vec3(.4, .4, .1)) / 1.5; + pos.xy *= 5.0; + pos.y -= 3.5; + pos.xy *= rot(.75); + float result = -base; + return result; +} + +float box_set(vec3 pos, float iTimeLocal) { + vec3 pos_origin = pos; + pos = pos_origin; + pos.y += sin(gTime * 0.4) * 2.5; + pos.xy *= rot(.8); + float box1 = boxGeom(pos, 2.0 - abs(sin(gTime * 0.4)) * 1.5); + + pos = pos_origin; + pos.y -= sin(gTime * 0.4) * 2.5; + pos.xy *= rot(.8); + float box2 = boxGeom(pos, 2.0 - abs(sin(gTime * 0.4)) * 1.5); + + pos = pos_origin; + pos.x += sin(gTime * 0.4) * 2.5; + pos.xy *= rot(.8); + float box3 = boxGeom(pos, 2.0 - abs(sin(gTime * 0.4)) * 1.5); + + pos = pos_origin; + pos.x -= sin(gTime * 0.4) * 2.5; + pos.xy *= rot(.8); + float box4 = boxGeom(pos, 2.0 - abs(sin(gTime * 0.4)) * 1.5); + + pos = pos_origin; + pos.xy *= rot(.8); + float box5 = boxGeom(pos, .5) * 6.0; + + pos = pos_origin; + float box6 = boxGeom(pos, .5) * 6.0; + + float result = max(max(max(max(max(box1, box2), box3), box4), box5), box6); + return result; +} + +float mapScene(vec3 pos, float iTimeLocal) { + return box_set(pos, iTimeLocal); +} + +void mainImage(out vec4 fragColor, in vec2 fragCoord) { + vec2 p = (fragCoord.xy * 2.0 - iResolution.xy) / min(iResolution.x, iResolution.y); + vec3 ro = vec3(0.0, -0.2, iTime * 4.0); + vec3 ray = normalize(vec3(p, 1.5)); + ray.xy = ray.xy * rot(sin(iTime * .03) * 5.0); + ray.yz = ray.yz * rot(sin(iTime * .05) * .2); + float t = 0.1; + vec3 col = vec3(0.0); + float ac = 0.0; + + for (int i = 0; i < 99; i++) { + vec3 pos = ro + ray * t; + pos = mod(pos - 2.0, 4.0) - 2.0; + gTime = iTime - float(i) * 0.01; + + float d = mapScene(pos, iTime); + + d = max(abs(d), 0.01); + ac += exp(-d * 23.0); + + t += d * 0.55; + } + + col = vec3(ac * 0.02); + col += vec3(0.0, 0.2 * abs(sin(iTime)), 0.5 + sin(iTime) * 0.2); + + fragColor = vec4(col, 1.0 - t * (0.02 + 0.02 * sin(iTime))); +} + +void main() { + vec2 fragCoordPixels = vUV * iResolution; + vec4 outColor; + mainImage(outColor, fragCoordPixels); + FragColor = outColor; +} diff --git a/shaders/test.frag.glsl b/shaders/test.frag.glsl new file mode 100644 index 0000000..ff836f5 --- /dev/null +++ b/shaders/test.frag.glsl @@ -0,0 +1,32 @@ +#version 330 core +out vec4 FragColor; +in vec2 vUV; +uniform vec2 iResolution; +uniform float iTime; + +vec3 palette( float t) { + vec3 a = vec3(1.0, 0.5, 0.5); + vec3 b = vec3(1.0, 0.5, 0.5); + vec3 c = vec3(1.0, 1.0, 1.0); + vec3 d = vec3(0.263, 0.416, 0.557); + return a + b * cos( 6.28318 * (c * t * d) ); +} + +void mainImage( out vec4 fragColor, in vec2 fragCoord ) +{ + vec2 uv = (fragCoord * 2.0 - iResolution.xy) / iResolution.y; + float d = length(uv); + vec3 col = palette(d); + d = sin(d * 8.0 + iTime) / 8.0; + d = abs(d); + d = 0.02 / d; + col *= d; + fragColor = vec4(col, 1.0); +} + +void main() { + vec2 fragCoordPixels = vUV * iResolution; + vec4 outColor; + mainImage(outColor, fragCoordPixels); + FragColor = outColor; +} diff --git a/src/main.cpp b/src/main.cpp index c45d511..7750d81 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,11 +1,42 @@ // src/main.cpp #include +#include +#include #include +#include +#include +#include #include #include -// Vertex shader: genera posiciones de pantalla y una coordenada uv en [0,1] -static const char *vertexShaderSrc = R"glsl( +// Simple logger compatible con el estilo que usas +struct Logger { + static void info(const std::string& s) { std::cout << "[INFO] " << s << '\n'; } + static void error(const std::string& s) { std::cerr << "[ERROR] " << s << '\n'; } +}; + +// Opciones mínimas parecidas a las tuyas +struct VideoOptions { + bool fullscreen = false; +} Options_video; + +// Estructura para guardar info del display +struct DisplayMonitor { + std::string name; + int width = 0; + int height = 0; + int refresh_rate = 0; +}; + +// Globales simplificados (tu proyecto puede integrarlo en clases) +static DisplayMonitor display_monitor_; +static SDL_Window* window_ = nullptr; + +// Constante por defecto del fragment shader +static constexpr const char* DEFAULT_FRAG = "shaders/test.frag.glsl"; + +// Vertex shader embebido +static const char* vertexShaderSrc = R"glsl( #version 330 core layout(location = 0) in vec2 aPos; out vec2 vUV; @@ -15,146 +46,230 @@ void main() { } )glsl"; -// Fragment shader: adapta tu shader de Shadertoy -static const char *fragmentShaderSrc = R"glsl( -#version 330 core -out vec4 FragColor; -in vec2 vUV; -uniform vec2 iResolution; -uniform float iTime; - -vec3 palette( float t) { - vec3 a = vec3(1.0, 0.5, 0.5); - vec3 b = vec3(1.0, 0.5, 0.5); - vec3 c = vec3(1.0, 1.0, 1.0); - vec3 d = vec3(0.263, 0.416, 0.557); - return a + b * cos( 6.28318 * (c * t * d) ); +// Helpers de carga +static bool loadFileToString(const std::filesystem::path& path, std::string& out) { + std::ifstream ifs(path, std::ios::in | std::ios::binary); + if (!ifs) return false; + std::ostringstream ss; + ss << ifs.rdbuf(); + out = ss.str(); + return true; } -void mainImage( out vec4 fragColor, in vec2 fragCoord ) -{ - vec2 uv = (fragCoord * 2.0 - iResolution.xy) / iResolution.y; - float d = length(uv); - vec3 col = palette(d); - d = sin(d * 8.0 + iTime) / 8.0; - d = abs(d); - d = 0.02 / d; - col *= d; - fragColor = vec4(col, 1.0); -} - -void main() { - vec2 fragCoordPixels = vUV * iResolution; - vec4 outColor; - mainImage(outColor, fragCoordPixels); - FragColor = outColor; -} -)glsl"; - -// helpers -static GLuint compileShader(GLenum type, const char *src) -{ +static GLuint compileShader(GLenum type, const char* src) { GLuint s = glCreateShader(type); glShaderSource(s, 1, &src, nullptr); glCompileShader(s); GLint ok = 0; glGetShaderiv(s, GL_COMPILE_STATUS, &ok); - if (!ok) - { + if (!ok) { GLint len = 0; glGetShaderiv(s, GL_INFO_LOG_LENGTH, &len); - std::string log(len, ' '); + std::string log(len > 0 ? len : 1, ' '); glGetShaderInfoLog(s, len, nullptr, &log[0]); - std::cerr << "Shader compile error: " << log << '\n'; + Logger::error("Shader compile error: " + log); glDeleteShader(s); return 0; } return s; } -static GLuint linkProgram(GLuint vs, GLuint fs) -{ +static GLuint linkProgram(GLuint vs, GLuint fs) { GLuint p = glCreateProgram(); glAttachShader(p, vs); glAttachShader(p, fs); glLinkProgram(p); GLint ok = 0; glGetProgramiv(p, GL_LINK_STATUS, &ok); - if (!ok) - { + if (!ok) { GLint len = 0; glGetProgramiv(p, GL_INFO_LOG_LENGTH, &len); - std::string log(len, ' '); + std::string log(len > 0 ? len : 1, ' '); glGetProgramInfoLog(p, len, nullptr, &log[0]); - std::cerr << "Program link error: " << log << '\n'; + Logger::error("Program link error: " + log); glDeleteProgram(p); return 0; } return p; } -int main(int argc, char **argv) -{ - if (!SDL_Init(SDL_INIT_VIDEO)) - { - std::cerr << "SDL_Init error: " << SDL_GetError() << '\n'; - return -1; +// --- Funciones basadas en tu código --- +void getDisplayInfo() { + int num_displays = 0; + SDL_DisplayID* displays = SDL_GetDisplays(&num_displays); + if (displays != nullptr && num_displays > 0) { + for (int i = 0; i < num_displays; ++i) { + SDL_DisplayID instance_id = displays[i]; + const char* name = SDL_GetDisplayName(instance_id); + Logger::info(std::string("Display ") + std::to_string(instance_id) + ": " + (name != nullptr ? name : "Unknown")); + } + + const SDL_DisplayMode* dm = SDL_GetCurrentDisplayMode(displays[0]); + const char* first_display_name = SDL_GetDisplayName(displays[0]); + display_monitor_.name = (first_display_name != nullptr) ? first_display_name : "Unknown"; + if (dm) { + display_monitor_.width = static_cast(dm->w); + display_monitor_.height = static_cast(dm->h); + display_monitor_.refresh_rate = static_cast(dm->refresh_rate); + } else { + Logger::info("SDL_GetCurrentDisplayMode returned null"); + } + } else { + Logger::info("No displays found or SDL_GetDisplays failed"); + } +} + +void setFullscreenMode() { + // SDL3: la API acepta un bool para fullscreen (true = fullscreen desktop / exclusive depending on param) + // En tu repos tienes Options::video.fullscreen (bool), así lo usamos: + if (!window_) return; + + // Si se pide fullscreen = true, preferimos fullscreen exclusivo si se puede ajustar tamaño + if (Options_video.fullscreen) { + // Si conocemos la resolución nativa, forzamos tamaño de ventana y pedimos fullscreen exclusivo + if (display_monitor_.width > 0 && display_monitor_.height > 0) { + SDL_SetWindowSize(window_, display_monitor_.width, display_monitor_.height); + // SDL3 acepta SDL_SetWindowFullscreen(window, true) para fullscreen; algunos drivers pueden ofrecer exclusive/fullscreen desktop. + if (SDL_SetWindowFullscreen(window_, true) != 0) { + // fallback: intentar fullscreen desktop (true funciona como desktop fullscreen en muchas implementaciones) + Logger::info("SDL_SetWindowFullscreen(exclusive) failed, fallback to fullscreen desktop"); + SDL_SetWindowFullscreen(window_, true); // ya es el mismo en SDL3; dejamos registro + } + } else { + // desconocemos resolución, pedimos fullscreen desktop + SDL_SetWindowFullscreen(window_, true); + } + } else { + // volver a ventana + SDL_SetWindowFullscreen(window_, false); + } +} + +void toggleFullscreen() { + Options_video.fullscreen = !Options_video.fullscreen; + setFullscreenMode(); +} + +// Manejo de teclas de debug (adaptado a tu estilo) +void handleDebugEvents(const SDL_Event& event) { + // evitar repetición de teclas: event.key.repeat disponible en SDL3 + if (event.type == SDL_EVENT_KEY_DOWN && static_cast(event.key.repeat) == 0) { + switch (event.key.key) { + case SDLK_1: { + Logger::info("Key 1 pressed (action placeholder)"); + break; + } + case SDLK_2: { + static bool deploy_balloons_ = true; + deploy_balloons_ = !deploy_balloons_; + Logger::info(std::string("Toggle balloons: ") + (deploy_balloons_ ? "on" : "off")); + break; + } + case SDLK_F11: { + toggleFullscreen(); + break; + } + default: + break; + } + } +} + +// --- main (integra todo y soporta -F y argv[1] para shader path) --- +int main(int argc, char** argv) { + std::string shaderPath; + bool fullscreenFlag = false; + for (int i = 1; i < argc; ++i) { + std::string a = argv[i]; + if (a == "-F" || a == "--fullscreen") { fullscreenFlag = true; continue; } + if (shaderPath.empty()) shaderPath = a; + } + if (shaderPath.empty()) shaderPath = DEFAULT_FRAG; + Options_video.fullscreen = fullscreenFlag; + + // Inicializar SDL3 + auto initResult = SDL_Init(SDL_INIT_VIDEO); + if constexpr (std::is_same_v) { + if (!initResult) { Logger::error(SDL_GetError()); return -1; } + } else { + if (initResult != 0) { Logger::error(SDL_GetError()); return -1; } } - // Pedir contexto OpenGL 3.3 Core + // Obtener información del display antes de crear ventana + getDisplayInfo(); + + // Atributos GL SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); + // Crear ventana int winW = 800, winH = 800; - // SDL3: SDL_CreateWindow signature es (title, w, h, flags) - SDL_Window *window = SDL_CreateWindow("Shadertoy SDL3 + OpenGL", - winW, winH, - SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE); - if (!window) - { - std::cerr << "SDL_CreateWindow error: " << SDL_GetError() << '\n'; + window_ = SDL_CreateWindow("Shadertoy SDL3 + OpenGL", winW, winH, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE); + if (!window_) { Logger::error(std::string("SDL_CreateWindow error: ") + SDL_GetError()); SDL_Quit(); return -1; } + + // Aplicar fullscreen si el flag estaba activado + setFullscreenMode(); + + // Crear contexto GL + SDL_GLContext glContext = SDL_GL_CreateContext(window_); + if (!glContext) { + Logger::error(std::string("SDL_GL_CreateContext error: ") + SDL_GetError()); + SDL_DestroyWindow(window_); SDL_Quit(); return -1; } - SDL_GLContext glContext = SDL_GL_CreateContext(window); - if (!glContext) - { - std::cerr << "SDL_GL_CreateContext error: " << SDL_GetError() << '\n'; - SDL_DestroyWindow(window); - SDL_Quit(); - return -1; - } - - if (!gladLoadGLLoader((GLADloadproc)SDL_GL_GetProcAddress)) - { - std::cerr << "Failed to initialize GL loader\n"; + if (!gladLoadGLLoader((GLADloadproc)SDL_GL_GetProcAddress)) { + Logger::error("Failed to initialize GL loader"); SDL_GL_DestroyContext(glContext); - SDL_DestroyWindow(window); + SDL_DestroyWindow(window_); SDL_Quit(); return -1; } + // Localizar y cargar el fragment shader (intenta rutas comunes) + std::vector candidates; + candidates.emplace_back(shaderPath); + candidates.emplace_back(std::filesystem::path("..") / shaderPath); + candidates.emplace_back(std::filesystem::path(".") / shaderPath); + if (argc > 0 && argv[0]) { + std::filesystem::path exe = argv[0]; + if (exe.has_parent_path()) { + candidates.emplace_back(exe.parent_path() / shaderPath); + candidates.emplace_back(exe.parent_path() / ".." / shaderPath); + } + } + + std::string fragSrc; + std::filesystem::path found; + for (auto &p : candidates) { + if (loadFileToString(p, fragSrc)) { found = p; break; } + } + if (found.empty()) { + Logger::error("Failed to load fragment shader file. Tried paths:"); + for (auto &p : candidates) Logger::error(" " + p.string()); + SDL_GL_DestroyContext(glContext); + SDL_DestroyWindow(window_); + SDL_Quit(); + return -1; + } + Logger::info("Loaded fragment shader from: " + found.string()); + GLuint vs = compileShader(GL_VERTEX_SHADER, vertexShaderSrc); - GLuint fs = compileShader(GL_FRAGMENT_SHADER, fragmentShaderSrc); - if (!vs || !fs) - return -1; + GLuint fs = compileShader(GL_FRAGMENT_SHADER, fragSrc.c_str()); + if (!vs || !fs) { SDL_GL_DestroyContext(glContext); SDL_DestroyWindow(window_); SDL_Quit(); return -1; } GLuint program = linkProgram(vs, fs); glDeleteShader(vs); glDeleteShader(fs); - if (!program) - return -1; + if (!program) { SDL_GL_DestroyContext(glContext); SDL_DestroyWindow(window_); SDL_Quit(); return -1; } + // Quad setup float quadVertices[] = { - -1.0f, - -1.0f, - 1.0f, - -1.0f, - -1.0f, - 1.0f, - 1.0f, - 1.0f, + -1.0f, -1.0f, + 1.0f, -1.0f, + -1.0f, 1.0f, + 1.0f, 1.0f, }; GLuint vao = 0, vbo = 0; @@ -164,7 +279,7 @@ int main(int argc, char **argv) glBindBuffer(GL_ARRAY_BUFFER, vbo); glBufferData(GL_ARRAY_BUFFER, sizeof(quadVertices), quadVertices, GL_STATIC_DRAW); glEnableVertexAttribArray(0); - glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), (void *)0); + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), (void*)0); glBindBuffer(GL_ARRAY_BUFFER, 0); glBindVertexArray(0); @@ -174,28 +289,30 @@ int main(int argc, char **argv) bool running = true; Uint32 startTicks = SDL_GetTicks(); - while (running) - { + while (running) { SDL_Event e; - while (SDL_PollEvent(&e)) - { - if (e.type == SDL_EVENT_QUIT) - running = false; - if (e.type == SDL_EVENT_WINDOW_CLOSE_REQUESTED) - running = false; + while (SDL_PollEvent(&e)) { + if (e.type == SDL_EVENT_QUIT) running = false; + else if (e.type == SDL_EVENT_WINDOW_CLOSE_REQUESTED) running = false; + else if (e.type == SDL_EVENT_KEY_DOWN) { + // Escape cierra la app + if (e.key.key == SDLK_ESCAPE) running = false; + // handle your debug keys + handleDebugEvents(e); + } else if (e.type == SDL_EVENT_WINDOW_RESIZED) { + // opcional: podrías actualizar algo con new size + } } int w, h; - SDL_GetWindowSize(window, &w, &h); + SDL_GetWindowSize(window_, &w, &h); glViewport(0, 0, w, h); glClearColor(0.0f, 0.0f, 0.0f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); glUseProgram(program); - if (locRes >= 0) - glUniform2f(locRes, float(w), float(h)); - if (locTime >= 0) - { + if (locRes >= 0) glUniform2f(locRes, float(w), float(h)); + if (locTime >= 0) { float t = (SDL_GetTicks() - startTicks) / 1000.0f; glUniform1f(locTime, t); } @@ -204,16 +321,17 @@ int main(int argc, char **argv) glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); glBindVertexArray(0); - SDL_GL_SwapWindow(window); + SDL_GL_SwapWindow(window_); SDL_Delay(1); } + // Cleanup glDeleteBuffers(1, &vbo); glDeleteVertexArrays(1, &vao); glDeleteProgram(program); SDL_GL_DestroyContext(glContext); - SDL_DestroyWindow(window); + SDL_DestroyWindow(window_); SDL_Quit(); return 0; }