diff --git a/data/shaders/crtpi_fragment.glsl b/data/shaders/crtpi_fragment.glsl index cd63e80..c17d77d 100644 --- a/data/shaders/crtpi_fragment.glsl +++ b/data/shaders/crtpi_fragment.glsl @@ -31,6 +31,9 @@ out vec4 FragColor; // Uniforms uniform sampler2D Texture; uniform vec2 TextureSize; +uniform float uVignette; // 0 = sin viñeta, 1 = máxima +uniform float uScanlines; // 0 = desactivadas, 1 = plenas +uniform float uChroma; // 0 = sin aberración, 1 = máxima #if defined(CURVATURE) vec2 Distort(vec2 coord) @@ -111,7 +114,11 @@ void main() vec2 tc = vec2(texcoord.x, yCoord + dy); #endif - vec3 colour = texture(Texture, tc).rgb; + float ca = uChroma * 0.005; + vec3 colour; + colour.r = texture(Texture, tc + vec2(ca, 0.0)).r; + colour.g = texture(Texture, tc).g; + colour.b = texture(Texture, tc - vec2(ca, 0.0)).b; #if defined(SCANLINES) #if defined(GAMMA) @@ -122,7 +129,7 @@ void main() #endif #endif scanLineWeight *= BLOOM_FACTOR; - colour *= scanLineWeight; + colour *= mix(1.0, scanLineWeight, uScanlines); #if defined(GAMMA) #if defined(FAKE_GAMMA) @@ -133,6 +140,12 @@ void main() #endif #endif + if (uVignette > 0.0) { + vec2 uv = texcoord - vec2(0.5); + float vig = 1.0 - dot(uv, uv) * uVignette * 4.0; + colour *= clamp(vig, 0.0, 1.0); + } + #if MASK_TYPE == 0 FragColor = vec4(colour, 1.0); #elif MASK_TYPE == 1 diff --git a/data/shaders/crtpi_fragment_es.glsl b/data/shaders/crtpi_fragment_es.glsl index 363b6a9..c10a2d0 100644 --- a/data/shaders/crtpi_fragment_es.glsl +++ b/data/shaders/crtpi_fragment_es.glsl @@ -34,6 +34,9 @@ out vec4 FragColor; // Uniforms uniform sampler2D Texture; uniform vec2 TextureSize; +uniform float uVignette; // 0 = sin viñeta, 1 = máxima +uniform float uScanlines; // 0 = desactivadas, 1 = plenas +uniform float uChroma; // 0 = sin aberración, 1 = máxima #if defined(CURVATURE) vec2 Distort(vec2 coord) @@ -114,7 +117,11 @@ void main() vec2 tc = vec2(texcoord.x, yCoord + dy); #endif - vec3 colour = texture(Texture, tc).rgb; + float ca = uChroma * 0.005; + vec3 colour; + colour.r = texture(Texture, tc + vec2(ca, 0.0)).r; + colour.g = texture(Texture, tc).g; + colour.b = texture(Texture, tc - vec2(ca, 0.0)).b; #if defined(SCANLINES) #if defined(GAMMA) @@ -125,7 +132,7 @@ void main() #endif #endif scanLineWeight *= BLOOM_FACTOR; - colour *= scanLineWeight; + colour *= mix(1.0, scanLineWeight, uScanlines); #if defined(GAMMA) #if defined(FAKE_GAMMA) @@ -136,6 +143,12 @@ void main() #endif #endif + if (uVignette > 0.0) { + vec2 uv = texcoord - vec2(0.5); + float vig = 1.0 - dot(uv, uv) * uVignette * 4.0; + colour *= clamp(vig, 0.0, 1.0); + } + #if MASK_TYPE == 0 FragColor = vec4(colour, 1.0); #elif MASK_TYPE == 1 diff --git a/source/core/rendering/opengl/opengl_shader.cpp b/source/core/rendering/opengl/opengl_shader.cpp index 9085eba..090f79c 100644 --- a/source/core/rendering/opengl/opengl_shader.cpp +++ b/source/core/rendering/opengl/opengl_shader.cpp @@ -30,6 +30,7 @@ auto OpenGLShader::initGLExtensions() -> bool { glUseProgram = (PFNGLUSEPROGRAMPROC)SDL_GL_GetProcAddress("glUseProgram"); glDeleteProgram = (PFNGLDELETEPROGRAMPROC)SDL_GL_GetProcAddress("glDeleteProgram"); glGetUniformLocation = (PFNGLGETUNIFORMLOCATIONPROC)SDL_GL_GetProcAddress("glGetUniformLocation"); + glUniform1f = (PFNGLUNIFORM1FPROC)SDL_GL_GetProcAddress("glUniform1f"); glUniform2f = (PFNGLUNIFORM2FPROC)SDL_GL_GetProcAddress("glUniform2f"); glGenVertexArrays = (PFNGLGENVERTEXARRAYSPROC)SDL_GL_GetProcAddress("glGenVertexArrays"); glBindVertexArray = (PFNGLBINDVERTEXARRAYPROC)SDL_GL_GetProcAddress("glBindVertexArray"); @@ -44,7 +45,8 @@ auto OpenGLShader::initGLExtensions() -> bool { return (glCreateShader != nullptr) && (glShaderSource != nullptr) && (glCompileShader != nullptr) && (glGetShaderiv != nullptr) && (glGetShaderInfoLog != nullptr) && (glDeleteShader != nullptr) && (glAttachShader != nullptr) && (glCreateProgram != nullptr) && (glLinkProgram != nullptr) && (glValidateProgram != nullptr) && (glGetProgramiv != nullptr) && (glGetProgramInfoLog != nullptr) && - (glUseProgram != nullptr) && (glDeleteProgram != nullptr) && (glGetUniformLocation != nullptr) && (glUniform2f != nullptr) && + (glUseProgram != nullptr) && (glDeleteProgram != nullptr) && (glGetUniformLocation != nullptr) && + (glUniform1f != nullptr) && (glUniform2f != nullptr) && (glGenVertexArrays != nullptr) && (glBindVertexArray != nullptr) && (glDeleteVertexArrays != nullptr) && (glGenBuffers != nullptr) && (glBindBuffer != nullptr) && (glBufferData != nullptr) && (glDeleteBuffers != nullptr) && (glVertexAttribPointer != nullptr) && (glEnableVertexAttribArray != nullptr); @@ -320,7 +322,7 @@ auto OpenGLShader::init(SDL_Window* window, // Crear geometría del quad createQuadGeometry(); - // Obtener ubicación del uniform TextureSize + // Obtener ubicaciones de uniforms y configurar valores iniciales glUseProgram(program_id_); texture_size_location_ = glGetUniformLocation(program_id_, "TextureSize"); if (texture_size_location_ != -1) { @@ -334,6 +336,14 @@ auto OpenGLShader::init(SDL_Window* window, SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Uniform 'TextureSize' not found in shader"); } + + // Uniforms PostFX + vignette_location_ = glGetUniformLocation(program_id_, "uVignette"); + scanlines_location_ = glGetUniformLocation(program_id_, "uScanlines"); + chroma_location_ = glGetUniformLocation(program_id_, "uChroma"); + if (vignette_location_ != -1) { glUniform1f(vignette_location_, postfx_vignette_); } + if (scanlines_location_ != -1) { glUniform1f(scanlines_location_, postfx_scanlines_); } + if (chroma_location_ != -1) { glUniform1f(chroma_location_, postfx_chroma_); } glUseProgram(0); is_initialized_ = true; @@ -388,6 +398,11 @@ void OpenGLShader::render() { glUseProgram(program_id_); checkGLError("glUseProgram"); + // Pasar uniforms PostFX + if (vignette_location_ != -1) { glUniform1f(vignette_location_, postfx_vignette_); } + if (scanlines_location_ != -1) { glUniform1f(scanlines_location_, postfx_scanlines_); } + if (chroma_location_ != -1) { glUniform1f(chroma_location_, postfx_chroma_); } + // Configurar viewport (obtener tamaño lógico de SDL) int logical_w; int logical_h; @@ -449,6 +464,12 @@ void OpenGLShader::render() { glViewport(old_viewport[0], old_viewport[1], old_viewport[2], old_viewport[3]); } +void OpenGLShader::setPostFXParams(float vignette, float scanlines, float chroma) { + postfx_vignette_ = vignette; + postfx_scanlines_ = scanlines; + postfx_chroma_ = chroma; +} + void OpenGLShader::setTextureSize(float width, float height) { if (!is_initialized_ || program_id_ == 0) { return; diff --git a/source/core/rendering/opengl/opengl_shader.hpp b/source/core/rendering/opengl/opengl_shader.hpp index b5d9c1a..2e20b4c 100644 --- a/source/core/rendering/opengl/opengl_shader.hpp +++ b/source/core/rendering/opengl/opengl_shader.hpp @@ -30,6 +30,7 @@ class OpenGLShader : public ShaderBackend { void render() override; void setTextureSize(float width, float height) override; + void setPostFXParams(float vignette, float scanlines, float chroma) override; void cleanup() final; [[nodiscard]] auto isHardwareAccelerated() const -> bool override { return is_initialized_; } @@ -55,6 +56,14 @@ class OpenGLShader : public ShaderBackend { // Ubicaciones de uniforms GLint texture_size_location_ = -1; + GLint vignette_location_ = -1; + GLint scanlines_location_ = -1; + GLint chroma_location_ = -1; + + // Valores cacheados de PostFX + float postfx_vignette_ = 0.6F; + float postfx_scanlines_ = 0.7F; + float postfx_chroma_ = 0.15F; // Tamaños int window_width_ = 0; @@ -83,6 +92,7 @@ class OpenGLShader : public ShaderBackend { PFNGLUSEPROGRAMPROC glUseProgram = nullptr; PFNGLDELETEPROGRAMPROC glDeleteProgram = nullptr; PFNGLGETUNIFORMLOCATIONPROC glGetUniformLocation = nullptr; + PFNGLUNIFORM1FPROC glUniform1f = nullptr; PFNGLUNIFORM2FPROC glUniform2f = nullptr; PFNGLGENVERTEXARRAYSPROC glGenVertexArrays = nullptr; PFNGLBINDVERTEXARRAYPROC glBindVertexArray = nullptr; diff --git a/source/core/rendering/screen.cpp b/source/core/rendering/screen.cpp index 0bc7c36..cc845bf 100644 --- a/source/core/rendering/screen.cpp +++ b/source/core/rendering/screen.cpp @@ -448,32 +448,13 @@ auto loadData(const std::string& filepath) -> std::vector { // Carga el contenido de los archivos GLSL void Screen::loadShaders() { - // Obtener nombres de fichero desde el preset actual (o usar fallback) - std::string preset_vertex = "crtpi_vertex.glsl"; - std::string preset_fragment = "crtpi_fragment.glsl"; - if (!Options::postfx_presets.empty()) { - const auto& preset = Options::postfx_presets[static_cast(Options::current_postfx_preset)]; - if (!preset.vertex.empty()) { - preset_vertex = preset.vertex; - } - if (!preset.fragment.empty()) { - preset_fragment = preset.fragment; - } - } - if (vertex_shader_source_.empty()) { // Detectar si necesitamos OpenGL ES (Raspberry Pi) - // Intentar cargar versión ES primero si existe (reemplaza .glsl por _es.glsl) - std::string vertex_es = preset_vertex; - auto pos = vertex_es.rfind(".glsl"); - if (pos != std::string::npos) { - vertex_es.insert(pos, "_es"); - } - auto data = loadData(Resource::List::get()->get(vertex_es)); + // Intentar cargar versión ES primero si existe + auto data = loadData(Resource::List::get()->get("crtpi_vertex_es.glsl")); if (data.empty()) { - // Si no existe versión ES, usar versión Desktop - data = loadData(Resource::List::get()->get(preset_vertex)); + data = loadData(Resource::List::get()->get("crtpi_vertex.glsl")); std::cout << "Usando shaders OpenGL Desktop 3.3\n"; } else { std::cout << "Usando shaders OpenGL ES 3.0 (Raspberry Pi)\n"; @@ -485,16 +466,10 @@ void Screen::loadShaders() { } if (fragment_shader_source_.empty()) { // Intentar cargar versión ES primero si existe - std::string fragment_es = preset_fragment; - auto pos = fragment_es.rfind(".glsl"); - if (pos != std::string::npos) { - fragment_es.insert(pos, "_es"); - } - auto data = loadData(Resource::List::get()->get(fragment_es)); + auto data = loadData(Resource::List::get()->get("crtpi_fragment_es.glsl")); if (data.empty()) { - // Si no existe versión ES, usar versión Desktop - data = loadData(Resource::List::get()->get(preset_fragment)); + data = loadData(Resource::List::get()->get("crtpi_fragment.glsl")); } if (!data.empty()) { @@ -503,6 +478,14 @@ void Screen::loadShaders() { } } +// Aplica los parámetros del preset actual al backend de shaders +void Screen::applyCurrentPostFXPreset() { + if (shader_backend_ && !Options::postfx_presets.empty()) { + const auto& p = Options::postfx_presets[static_cast(Options::current_postfx_preset)]; + shader_backend_->setPostFXParams(p.vignette, p.scanlines, p.chroma); + } +} + // Inicializa los shaders void Screen::initShaders() { if (!Options::video.postfx) { @@ -525,6 +508,8 @@ void Screen::initShaders() { } shader_backend_->init(window_, tex, vertex_shader_source_, fragment_shader_source_); #endif + + applyCurrentPostFXPreset(); } // Obtiene información sobre la pantalla diff --git a/source/core/rendering/screen.hpp b/source/core/rendering/screen.hpp index 79dbd03..f85d4be 100644 --- a/source/core/rendering/screen.hpp +++ b/source/core/rendering/screen.hpp @@ -117,6 +117,7 @@ class Screen { auto findPalette(const std::string& name) -> size_t; // Localiza la paleta dentro del vector de paletas void initShaders(); // Inicializa los shaders void loadShaders(); // Carga el contenido del archivo GLSL + void applyCurrentPostFXPreset(); // Aplica los parámetros del preset actual al backend void renderInfo(); // Muestra información por pantalla void getDisplayInfo(); // Obtiene información sobre la pantalla auto initSDLVideo() -> bool; // Arranca SDL VIDEO y crea la ventana diff --git a/source/core/rendering/sdl3gpu/sdl3gpu_shader.cpp b/source/core/rendering/sdl3gpu/sdl3gpu_shader.cpp index 5fe5e97..0298f07 100644 --- a/source/core/rendering/sdl3gpu/sdl3gpu_shader.cpp +++ b/source/core/rendering/sdl3gpu/sdl3gpu_shader.cpp @@ -416,4 +416,10 @@ auto SDL3GPUShader::createShaderSPIRV(SDL_GPUDevice* device, return shader; } +void SDL3GPUShader::setPostFXParams(float vignette, float scanlines, float chroma) { + uniforms_.vignette_strength = vignette; + uniforms_.scanline_strength = scanlines; + uniforms_.chroma_strength = chroma; +} + } // namespace Rendering diff --git a/source/core/rendering/sdl3gpu/sdl3gpu_shader.hpp b/source/core/rendering/sdl3gpu/sdl3gpu_shader.hpp index 8c09b33..43521af 100644 --- a/source/core/rendering/sdl3gpu/sdl3gpu_shader.hpp +++ b/source/core/rendering/sdl3gpu/sdl3gpu_shader.hpp @@ -41,6 +41,9 @@ class SDL3GPUShader : public ShaderBackend { // Sube píxeles ARGB8888 desde CPU; llamado antes de render() void uploadPixels(const Uint32* pixels, int width, int height) override; + // Actualiza los parámetros de intensidad de los efectos PostFX + void setPostFXParams(float vignette, float scanlines, float chroma) override; + private: static auto createShaderMSL(SDL_GPUDevice* device, const char* msl_source, diff --git a/source/core/rendering/shader_backend.hpp b/source/core/rendering/shader_backend.hpp index 2a55433..559ace0 100644 --- a/source/core/rendering/shader_backend.hpp +++ b/source/core/rendering/shader_backend.hpp @@ -52,6 +52,14 @@ class ShaderBackend { */ virtual void uploadPixels(const Uint32* /*pixels*/, int /*width*/, int /*height*/) {} + /** + * @brief Establece los parámetros de intensidad de los efectos PostFX + * @param vignette Intensidad de la viñeta (0.0 = ninguna, 1.0 = máxima) + * @param scanlines Intensidad de las scanlines (0.0 = desactivadas, 1.0 = máximas) + * @param chroma Intensidad de la aberración cromática (0.0 = ninguna, 1.0 = máxima) + */ + virtual void setPostFXParams(float /*vignette*/, float /*scanlines*/, float /*chroma*/) {} + /** * @brief Verifica si el backend está usando aceleración por hardware * @return true si usa aceleración (OpenGL/Metal/Vulkan) diff --git a/source/game/options.cpp b/source/game/options.cpp index 9d44bc2..bb5c68d 100644 --- a/source/game/options.cpp +++ b/source/game/options.cpp @@ -681,11 +681,14 @@ auto loadPostFXFromFile() -> bool { if (p.contains("name")) { preset.name = p["name"].get_value(); } - if (p.contains("vertex")) { - preset.vertex = p["vertex"].get_value(); + if (p.contains("vignette")) { + try { preset.vignette = p["vignette"].get_value(); } catch (...) {} } - if (p.contains("fragment")) { - preset.fragment = p["fragment"].get_value(); + if (p.contains("scanlines")) { + try { preset.scanlines = p["scanlines"].get_value(); } catch (...) {} + } + if (p.contains("chroma")) { + try { preset.chroma = p["chroma"].get_value(); } catch (...) {} } postfx_presets.push_back(preset); } @@ -720,13 +723,24 @@ auto savePostFXToFile() -> bool { } file << "# JailDoctor's Dilemma - PostFX Presets\n"; - file << "# Add or modify presets to customize post-processing effects.\n"; - file << "# vertex and fragment reference shader filenames from the shaders directory.\n"; + file << "# Each preset defines the intensity of post-processing effects (0.0 to 1.0).\n"; + file << "# vignette: screen darkening at the edges\n"; + file << "# scanlines: horizontal scanline effect\n"; + file << "# chroma: chromatic aberration (RGB color fringing)\n"; file << "\n"; file << "presets:\n"; file << " - name: \"CRT\"\n"; - file << " vertex: \"crtpi_vertex.glsl\"\n"; - file << " fragment: \"crtpi_fragment.glsl\"\n"; + file << " vignette: 0.6\n"; + file << " scanlines: 0.7\n"; + file << " chroma: 0.15\n"; + file << " - name: \"SCANLINES\"\n"; + file << " vignette: 0.0\n"; + file << " scanlines: 0.8\n"; + file << " chroma: 0.0\n"; + file << " - name: \"SUBTLE\"\n"; + file << " vignette: 0.3\n"; + file << " scanlines: 0.4\n"; + file << " chroma: 0.05\n"; file.close(); @@ -736,7 +750,9 @@ auto savePostFXToFile() -> bool { // Cargar los presets recién creados postfx_presets.clear(); - postfx_presets.push_back({"CRT", "crtpi_vertex.glsl", "crtpi_fragment.glsl"}); + postfx_presets.push_back({"CRT", 0.6F, 0.7F, 0.15F}); + postfx_presets.push_back({"SCANLINES", 0.0F, 0.8F, 0.0F}); + postfx_presets.push_back({"SUBTLE", 0.3F, 0.4F, 0.05F}); current_postfx_preset = 0; return true; diff --git a/source/game/options.hpp b/source/game/options.hpp index 42edfff..e27652c 100644 --- a/source/game/options.hpp +++ b/source/game/options.hpp @@ -116,9 +116,10 @@ struct Game { // Estructura para un preset de PostFX struct PostFXPreset { - std::string name; // Nombre del preset - std::string vertex; // Nombre del fichero vertex shader - std::string fragment; // Nombre del fichero fragment shader + std::string name; // Nombre del preset + float vignette{0.6F}; // Intensidad de la viñeta (0.0 = ninguna, 1.0 = máxima) + float scanlines{0.7F}; // Intensidad de las scanlines (0.0 = desactivadas, 1.0 = máximas) + float chroma{0.15F}; // Intensidad de la aberración cromática (0.0 = ninguna, 1.0 = máxima) }; // --- Variables globales ---