canvis de shaders al vol amb els cursors

This commit is contained in:
2025-10-25 11:57:37 +02:00
parent 98a16148cc
commit 8d17b6c047
2 changed files with 196 additions and 42 deletions

12
src/defines.hpp Normal file
View File

@@ -0,0 +1,12 @@
#pragma once
// Nombre de la aplicación
constexpr const char* APP_NAME = "Shadertoy";
// Tamaño de ventana por defecto
constexpr int WINDOW_WIDTH = 800;
constexpr int WINDOW_HEIGHT = 800;
// Rutas
constexpr const char* SHADERS_FOLDER = "shaders";
constexpr const char* DEFAULT_SHADER = "shaders/test.frag.glsl";

View File

@@ -4,11 +4,14 @@
#include <sstream> #include <sstream>
#include <string> #include <string>
#include <vector> #include <vector>
#include <algorithm>
#include <type_traits> #include <type_traits>
#include <filesystem> #include <filesystem>
#include <SDL3/SDL.h> #include <SDL3/SDL.h>
#include <glad/glad.h> #include <glad/glad.h>
#include "defines.hpp"
// Simple logger compatible con el estilo que usas // Simple logger compatible con el estilo que usas
struct Logger { struct Logger {
static void info(const std::string& s) { std::cout << "[INFO] " << s << '\n'; } static void info(const std::string& s) { std::cout << "[INFO] " << s << '\n'; }
@@ -32,12 +35,12 @@ struct DisplayMonitor {
static DisplayMonitor display_monitor_; static DisplayMonitor display_monitor_;
static SDL_Window* window_ = nullptr; static SDL_Window* window_ = nullptr;
// Constante por defecto del fragment shader // Sistema de shaders
static constexpr const char* DEFAULT_FRAG = "shaders/test.frag.glsl"; static std::vector<std::filesystem::path> shader_list_;
static size_t current_shader_index_ = 0;
// Tamaño de ventana por defecto static std::filesystem::path shaders_directory_;
static constexpr int WINDOW_WIDTH = 800; static GLuint current_program_ = 0;
static constexpr int WINDOW_HEIGHT = 800; static Uint32 shader_start_ticks_ = 0;
// Vertex shader embebido // Vertex shader embebido
static const char* vertexShaderSrc = R"glsl( static const char* vertexShaderSrc = R"glsl(
@@ -60,6 +63,38 @@ static bool loadFileToString(const std::filesystem::path& path, std::string& out
return true; return true;
} }
static std::vector<std::filesystem::path> scanShaderDirectory(const std::filesystem::path& directory) {
std::vector<std::filesystem::path> shaders;
if (!std::filesystem::exists(directory) || !std::filesystem::is_directory(directory)) {
Logger::error("Shader directory does not exist: " + directory.string());
return shaders;
}
for (const auto& entry : std::filesystem::directory_iterator(directory)) {
if (entry.is_regular_file()) {
auto ext = entry.path().extension().string();
if (ext == ".glsl") {
shaders.push_back(entry.path());
}
}
}
// Ordenar alfabéticamente
std::sort(shaders.begin(), shaders.end());
Logger::info("Found " + std::to_string(shaders.size()) + " shader(s) in " + directory.string());
return shaders;
}
static void updateWindowTitle() {
if (!window_ || shader_list_.empty()) return;
std::string filename = shader_list_[current_shader_index_].filename().string();
std::string title = std::string(APP_NAME) + " (" + filename + ")";
SDL_SetWindowTitle(window_, title.c_str());
}
static GLuint compileShader(GLenum type, const char* src) { static GLuint compileShader(GLenum type, const char* src) {
GLuint s = glCreateShader(type); GLuint s = glCreateShader(type);
glShaderSource(s, 1, &src, nullptr); glShaderSource(s, 1, &src, nullptr);
@@ -97,6 +132,44 @@ static GLuint linkProgram(GLuint vs, GLuint fs) {
return p; return p;
} }
static GLuint loadAndCompileShader(size_t index) {
if (index >= shader_list_.size()) {
Logger::error("Invalid shader index: " + std::to_string(index));
return 0;
}
const auto& shaderPath = shader_list_[index];
Logger::info("Loading shader: " + shaderPath.string());
std::string fragSrc;
if (!loadFileToString(shaderPath, fragSrc)) {
Logger::error("Failed to load shader file: " + shaderPath.string());
return 0;
}
GLuint vs = compileShader(GL_VERTEX_SHADER, vertexShaderSrc);
GLuint fs = compileShader(GL_FRAGMENT_SHADER, fragSrc.c_str());
if (!vs || !fs) {
if (vs) glDeleteShader(vs);
if (fs) glDeleteShader(fs);
Logger::error("Shader compilation failed for: " + shaderPath.string());
return 0;
}
GLuint program = linkProgram(vs, fs);
glDeleteShader(vs);
glDeleteShader(fs);
if (!program) {
Logger::error("Program linking failed for: " + shaderPath.string());
return 0;
}
Logger::info("Shader loaded successfully: " + shaderPath.filename().string());
return program;
}
// --- Funciones basadas en tu código --- // --- Funciones basadas en tu código ---
void getDisplayInfo() { void getDisplayInfo() {
int num_displays = 0; int num_displays = 0;
@@ -148,6 +221,36 @@ void toggleFullscreen() {
setFullscreenMode(); setFullscreenMode();
} }
void switchShader(int direction) {
if (shader_list_.empty()) return;
// Calcular nuevo índice con wrap-around cíclico
size_t new_index = current_shader_index_;
if (direction > 0) {
new_index = (current_shader_index_ + 1) % shader_list_.size();
} else if (direction < 0) {
new_index = (current_shader_index_ == 0) ? shader_list_.size() - 1 : current_shader_index_ - 1;
}
// Intentar cargar el nuevo shader
GLuint new_program = loadAndCompileShader(new_index);
if (new_program == 0) {
Logger::error("Failed to switch shader, keeping current one");
return;
}
// Éxito: eliminar programa anterior y actualizar
if (current_program_ != 0) {
glDeleteProgram(current_program_);
}
current_program_ = new_program;
current_shader_index_ = new_index;
shader_start_ticks_ = SDL_GetTicks();
updateWindowTitle();
}
// Manejo de teclas // Manejo de teclas
void handleDebugEvents(const SDL_Event& event) { void handleDebugEvents(const SDL_Event& event) {
// evitar repetición de teclas: event.key.repeat disponible en SDL3 // evitar repetición de teclas: event.key.repeat disponible en SDL3
@@ -157,6 +260,14 @@ void handleDebugEvents(const SDL_Event& event) {
toggleFullscreen(); toggleFullscreen();
break; break;
} }
case SDLK_LEFT: {
switchShader(-1);
break;
}
case SDLK_RIGHT: {
switchShader(+1);
break;
}
default: default:
break; break;
} }
@@ -172,7 +283,7 @@ int main(int argc, char** argv) {
if (a == "-F" || a == "--fullscreen") { fullscreenFlag = true; continue; } if (a == "-F" || a == "--fullscreen") { fullscreenFlag = true; continue; }
if (shaderPath.empty()) shaderPath = a; if (shaderPath.empty()) shaderPath = a;
} }
if (shaderPath.empty()) shaderPath = DEFAULT_FRAG; if (shaderPath.empty()) shaderPath = DEFAULT_SHADER;
Options_video.fullscreen = fullscreenFlag; Options_video.fullscreen = fullscreenFlag;
// Inicializar SDL3 // Inicializar SDL3
@@ -192,7 +303,7 @@ int main(int argc, char** argv) {
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
// Crear ventana // Crear ventana
window_ = SDL_CreateWindow("Shadertoy SDL3 + OpenGL", WINDOW_WIDTH, WINDOW_HEIGHT, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE); window_ = SDL_CreateWindow(APP_NAME, WINDOW_WIDTH, WINDOW_HEIGHT, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);
if (!window_) { Logger::error(std::string("SDL_CreateWindow error: ") + SDL_GetError()); SDL_Quit(); return -1; } if (!window_) { Logger::error(std::string("SDL_CreateWindow error: ") + SDL_GetError()); SDL_Quit(); return -1; }
// Aplicar fullscreen si el flag estaba activado // Aplicar fullscreen si el flag estaba activado
@@ -215,41 +326,69 @@ int main(int argc, char** argv) {
return -1; return -1;
} }
// Localizar y cargar el fragment shader (intenta rutas comunes) // Determinar carpeta de shaders
std::vector<std::filesystem::path> candidates; std::filesystem::path shaderFile(shaderPath);
candidates.emplace_back(shaderPath); if (shaderFile.has_parent_path()) {
candidates.emplace_back(std::filesystem::path("..") / shaderPath); shaders_directory_ = shaderFile.parent_path();
candidates.emplace_back(std::filesystem::path(".") / shaderPath); } else {
if (argc > 0 && argv[0]) { shaders_directory_ = SHADERS_FOLDER;
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; // Escanear carpeta de shaders
std::filesystem::path found; shader_list_ = scanShaderDirectory(shaders_directory_);
for (auto &p : candidates) { if (shader_list_.empty()) {
if (loadFileToString(p, fragSrc)) { found = p; break; } Logger::error("No shaders found in directory: " + shaders_directory_.string());
}
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_GL_DestroyContext(glContext);
SDL_DestroyWindow(window_); SDL_DestroyWindow(window_);
SDL_Quit(); SDL_Quit();
return -1; return -1;
} }
Logger::info("Loaded fragment shader from: " + found.string());
GLuint vs = compileShader(GL_VERTEX_SHADER, vertexShaderSrc); // Determinar shader inicial
GLuint fs = compileShader(GL_FRAGMENT_SHADER, fragSrc.c_str()); size_t initial_index = 0;
if (!vs || !fs) { SDL_GL_DestroyContext(glContext); SDL_DestroyWindow(window_); SDL_Quit(); return -1; } bool found_shader = false;
GLuint program = linkProgram(vs, fs);
glDeleteShader(vs); // Intentar encontrar el shader especificado
glDeleteShader(fs); std::filesystem::path target_shader = shaderFile.has_parent_path() ? shaderFile : (shaders_directory_ / shaderFile.filename());
if (!program) { SDL_GL_DestroyContext(glContext); SDL_DestroyWindow(window_); SDL_Quit(); return -1; } for (size_t i = 0; i < shader_list_.size(); ++i) {
if (shader_list_[i] == target_shader) {
initial_index = i;
found_shader = true;
break;
}
}
// Si no se encuentra, intentar con DEFAULT_SHADER
if (!found_shader) {
std::filesystem::path default_path(DEFAULT_SHADER);
for (size_t i = 0; i < shader_list_.size(); ++i) {
if (shader_list_[i] == default_path) {
initial_index = i;
found_shader = true;
break;
}
}
}
// Si aún no se encuentra, usar el primer shader de la lista
if (!found_shader) {
Logger::info("Specified shader not found, using first shader in directory");
initial_index = 0;
}
// Cargar shader inicial
current_shader_index_ = initial_index;
current_program_ = loadAndCompileShader(current_shader_index_);
if (current_program_ == 0) {
Logger::error("Failed to load initial shader");
SDL_GL_DestroyContext(glContext);
SDL_DestroyWindow(window_);
SDL_Quit();
return -1;
}
shader_start_ticks_ = SDL_GetTicks();
updateWindowTitle();
// Quad setup // Quad setup
float quadVertices[] = { float quadVertices[] = {
@@ -270,11 +409,7 @@ int main(int argc, char** argv) {
glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0); glBindVertexArray(0);
GLint locRes = glGetUniformLocation(program, "iResolution");
GLint locTime = glGetUniformLocation(program, "iTime");
bool running = true; bool running = true;
Uint32 startTicks = SDL_GetTicks();
while (running) { while (running) {
SDL_Event e; SDL_Event e;
@@ -297,10 +432,15 @@ int main(int argc, char** argv) {
glClearColor(0.0f, 0.0f, 0.0f, 1.0f); glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT);
glUseProgram(program); glUseProgram(current_program_);
// Obtener uniform locations (se recalculan porque el shader puede cambiar)
GLint locRes = glGetUniformLocation(current_program_, "iResolution");
GLint locTime = glGetUniformLocation(current_program_, "iTime");
if (locRes >= 0) glUniform2f(locRes, float(w), float(h)); if (locRes >= 0) glUniform2f(locRes, float(w), float(h));
if (locTime >= 0) { if (locTime >= 0) {
float t = (SDL_GetTicks() - startTicks) / 1000.0f; float t = (SDL_GetTicks() - shader_start_ticks_) / 1000.0f;
glUniform1f(locTime, t); glUniform1f(locTime, t);
} }
@@ -315,7 +455,9 @@ int main(int argc, char** argv) {
// Cleanup // Cleanup
glDeleteBuffers(1, &vbo); glDeleteBuffers(1, &vbo);
glDeleteVertexArrays(1, &vao); glDeleteVertexArrays(1, &vao);
glDeleteProgram(program); if (current_program_ != 0) {
glDeleteProgram(current_program_);
}
SDL_GL_DestroyContext(glContext); SDL_GL_DestroyContext(glContext);
SDL_DestroyWindow(window_); SDL_DestroyWindow(window_);