llevant el soport de opengl
This commit is contained in:
@@ -10,13 +10,6 @@ set(CMAKE_CXX_STANDARD_REQUIRED True)
|
||||
# Exportar comandos de compilación para herramientas de análisis
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
|
||||
# Establece la política CMP0072 para indicar cómo se debe seleccionar la implementación de OpenGL.
|
||||
# En este caso, se elige la opción "GLVND", que utiliza bibliotecas modernas y modulares (libOpenGL, libGLX),
|
||||
# en lugar de la biblioteca OpenGL clásica (libGL). Esto mejora la compatibilidad con drivers recientes
|
||||
# y evita ambigüedades cuando se encuentran múltiples implementaciones de OpenGL en el sistema.
|
||||
cmake_policy(SET CMP0072 NEW)
|
||||
set(OpenGL_GL_PREFERENCE GLVND)
|
||||
|
||||
# --- GENERACIÓN DE VERSIÓN AUTOMÁTICA ---
|
||||
find_package(Git QUIET)
|
||||
if(GIT_FOUND)
|
||||
@@ -110,18 +103,10 @@ set(APP_SOURCES
|
||||
source/main.cpp
|
||||
)
|
||||
|
||||
# Fuentes del sistema de renderizado
|
||||
# En macOS usamos SDL3 GPU (Metal), no OpenGL
|
||||
if(APPLE)
|
||||
# Fuentes del sistema de renderizado (SDL3 GPU para todas las plataformas)
|
||||
set(RENDERING_SOURCES
|
||||
source/core/rendering/sdl3gpu/sdl3gpu_shader.cpp
|
||||
)
|
||||
else()
|
||||
set(RENDERING_SOURCES
|
||||
source/core/rendering/opengl/opengl_shader.cpp
|
||||
source/core/rendering/sdl3gpu/sdl3gpu_shader.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
# Fuentes de debug (solo en modo Debug)
|
||||
set(DEBUG_SOURCES
|
||||
@@ -160,7 +145,7 @@ target_compile_definitions(${PROJECT_NAME} PRIVATE $<$<CONFIG:RELEASE>:RELEASE_B
|
||||
# Configuración específica para cada plataforma
|
||||
if(WIN32)
|
||||
target_compile_definitions(${PROJECT_NAME} PRIVATE WINDOWS_BUILD)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE ws2_32 mingw32 opengl32)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE ws2_32 mingw32)
|
||||
elseif(APPLE)
|
||||
target_compile_definitions(${PROJECT_NAME} PRIVATE MACOS_BUILD)
|
||||
target_compile_options(${PROJECT_NAME} PRIVATE -Wno-deprecated)
|
||||
@@ -169,19 +154,6 @@ elseif(UNIX AND NOT APPLE)
|
||||
target_compile_definitions(${PROJECT_NAME} PRIVATE LINUX_BUILD)
|
||||
endif()
|
||||
|
||||
# Configuración común para OpenGL (no requerido en macOS: usamos SDL3 GPU API / Metal)
|
||||
if(WIN32)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE opengl32)
|
||||
elseif(NOT APPLE)
|
||||
find_package(OpenGL REQUIRED)
|
||||
if(OPENGL_FOUND)
|
||||
message(STATUS "OpenGL encontrado: ${OPENGL_LIBRARIES}")
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE ${OPENGL_LIBRARIES})
|
||||
else()
|
||||
message(FATAL_ERROR "OpenGL no encontrado")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Especificar la ubicación del ejecutable
|
||||
set_target_properties(${PROJECT_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR})
|
||||
|
||||
|
||||
@@ -56,17 +56,6 @@ assets:
|
||||
- type: PALETTE
|
||||
path: ${PREFIX}/data/palette/steam-lords.pal
|
||||
|
||||
# SHADERS
|
||||
shaders:
|
||||
- type: DATA
|
||||
path: ${PREFIX}/data/shaders/crtpi_vertex.glsl
|
||||
- type: DATA
|
||||
path: ${PREFIX}/data/shaders/crtpi_fragment.glsl
|
||||
- type: DATA
|
||||
path: ${PREFIX}/data/shaders/crtpi_vertex_es.glsl
|
||||
- type: DATA
|
||||
path: ${PREFIX}/data/shaders/crtpi_fragment_es.glsl
|
||||
|
||||
# INPUT
|
||||
input:
|
||||
- type: DATA
|
||||
|
||||
@@ -1,160 +0,0 @@
|
||||
#version 330 core
|
||||
|
||||
// Configuración
|
||||
#define SCANLINES
|
||||
#define MULTISAMPLE
|
||||
|
||||
#define SCANLINE_WEIGHT 6.0
|
||||
#define SCANLINE_GAP_BRIGHTNESS 0.12
|
||||
#define BLOOM_FACTOR 3.5
|
||||
|
||||
// Inputs desde vertex shader
|
||||
in vec2 vTexCoord;
|
||||
in float vFilterWidth;
|
||||
|
||||
// Output
|
||||
out vec4 FragColor;
|
||||
|
||||
// Uniforms existentes
|
||||
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
|
||||
uniform float uOutputHeight; // altura del viewport en pixels de pantalla
|
||||
|
||||
// Nuevos uniforms
|
||||
uniform float uMask; // 0 = sin máscara de fósforo, 1 = máxima
|
||||
uniform float uGamma; // 0 = sin corrección gamma, 1 = gamma 2.4/2.2 plena
|
||||
uniform float uCurvature; // 0 = plana, 1 = curvatura barrel máxima
|
||||
uniform float uBleeding; // 0 = sin sangrado NTSC, 1 = máximo
|
||||
|
||||
// Conversores YCbCr para efecto NTSC
|
||||
vec3 rgb_to_ycc(vec3 rgb) {
|
||||
return vec3(
|
||||
0.299*rgb.r + 0.587*rgb.g + 0.114*rgb.b,
|
||||
-0.169*rgb.r - 0.331*rgb.g + 0.500*rgb.b + 0.5,
|
||||
0.500*rgb.r - 0.419*rgb.g - 0.081*rgb.b + 0.5
|
||||
);
|
||||
}
|
||||
vec3 ycc_to_rgb(vec3 ycc) {
|
||||
float y = ycc.x;
|
||||
float cb = ycc.y - 0.5;
|
||||
float cr = ycc.z - 0.5;
|
||||
return clamp(vec3(
|
||||
y + 1.402*cr,
|
||||
y - 0.344*cb - 0.714*cr,
|
||||
y + 1.772*cb
|
||||
), 0.0, 1.0);
|
||||
}
|
||||
|
||||
float CalcScanLineWeight(float dist)
|
||||
{
|
||||
return max(1.0 - dist * dist * SCANLINE_WEIGHT, SCANLINE_GAP_BRIGHTNESS);
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
vec2 texcoord = vTexCoord;
|
||||
|
||||
// Curvatura barrel CRT (distorsión en UV antes de muestrear)
|
||||
if (uCurvature > 0.0) {
|
||||
vec2 c = texcoord - 0.5;
|
||||
float rsq = dot(c, c);
|
||||
vec2 dist = vec2(0.05, 0.1) * uCurvature;
|
||||
vec2 barrelScale = 1.0 - 0.23 * dist;
|
||||
c += c * (dist * rsq);
|
||||
c *= barrelScale;
|
||||
if (abs(c.x) >= 0.5 || abs(c.y) >= 0.5) {
|
||||
FragColor = vec4(0.0);
|
||||
return;
|
||||
}
|
||||
texcoord = c + 0.5;
|
||||
}
|
||||
|
||||
vec2 texcoordInPixels = texcoord * TextureSize;
|
||||
|
||||
float tempY = floor(texcoordInPixels.y) + 0.5;
|
||||
float yCoord = tempY / TextureSize.y;
|
||||
|
||||
// Scanline en espacio de pantalla (subpíxel)
|
||||
float scaleY = uOutputHeight / TextureSize.y;
|
||||
float screenY = texcoord.y * uOutputHeight;
|
||||
float posInRow = mod(screenY, scaleY);
|
||||
float scanLineDY = posInRow / scaleY - 0.5;
|
||||
float localFilterWidth = 1.0 / scaleY;
|
||||
float scanLineWeight = CalcScanLineWeight(scanLineDY);
|
||||
scanLineWeight += CalcScanLineWeight(scanLineDY - localFilterWidth);
|
||||
scanLineWeight += CalcScanLineWeight(scanLineDY + localFilterWidth);
|
||||
scanLineWeight *= 0.3333333;
|
||||
|
||||
// Phosphor blur en espacio textura
|
||||
float dy = texcoordInPixels.y - tempY;
|
||||
float signY = sign(dy);
|
||||
dy = dy * dy;
|
||||
dy = dy * dy;
|
||||
dy *= 8.0;
|
||||
dy /= TextureSize.y;
|
||||
dy *= signY;
|
||||
vec2 tc = vec2(texcoord.x, yCoord + dy);
|
||||
|
||||
// Muestra base en centro
|
||||
vec3 base = texture(Texture, tc).rgb;
|
||||
|
||||
// Sangrado NTSC — difuminado horizontal de crominancia
|
||||
vec3 colour;
|
||||
if (uBleeding > 0.0) {
|
||||
float tw = TextureSize.x;
|
||||
vec3 ycc = rgb_to_ycc(base);
|
||||
vec3 ycc_l2 = rgb_to_ycc(texture(Texture, tc - vec2(2.0/tw, 0.0)).rgb);
|
||||
vec3 ycc_l1 = rgb_to_ycc(texture(Texture, tc - vec2(1.0/tw, 0.0)).rgb);
|
||||
vec3 ycc_r1 = rgb_to_ycc(texture(Texture, tc + vec2(1.0/tw, 0.0)).rgb);
|
||||
vec3 ycc_r2 = rgb_to_ycc(texture(Texture, tc + vec2(2.0/tw, 0.0)).rgb);
|
||||
ycc.yz = (ycc_l2.yz + ycc_l1.yz*2.0 + ycc.yz*2.0 + ycc_r1.yz*2.0 + ycc_r2.yz) / 8.0;
|
||||
colour = mix(base, ycc_to_rgb(ycc), uBleeding);
|
||||
} else {
|
||||
colour = base;
|
||||
}
|
||||
|
||||
// Aberración cromática
|
||||
float ca = uChroma * 0.005;
|
||||
colour.r = texture(Texture, tc + vec2(ca, 0.0)).r;
|
||||
colour.b = texture(Texture, tc - vec2(ca, 0.0)).b;
|
||||
|
||||
// Corrección gamma (linealizar antes de scanlines, codificar después)
|
||||
if (uGamma > 0.0) {
|
||||
vec3 lin = pow(colour, vec3(2.4));
|
||||
colour = mix(colour, lin, uGamma);
|
||||
}
|
||||
|
||||
// Scanlines
|
||||
scanLineWeight *= BLOOM_FACTOR;
|
||||
colour *= mix(1.0, scanLineWeight, uScanlines);
|
||||
|
||||
if (uGamma > 0.0) {
|
||||
vec3 enc = pow(colour, vec3(1.0 / 2.2));
|
||||
colour = mix(colour, enc, uGamma);
|
||||
}
|
||||
|
||||
// Viñeta
|
||||
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);
|
||||
}
|
||||
|
||||
// Máscara de fósforo RGB
|
||||
if (uMask > 0.0) {
|
||||
float whichMask = fract(gl_FragCoord.x * 0.3333333);
|
||||
vec3 mask = vec3(0.80);
|
||||
if (whichMask < 0.3333333)
|
||||
mask.x = 1.0;
|
||||
else if (whichMask < 0.6666666)
|
||||
mask.y = 1.0;
|
||||
else
|
||||
mask.z = 1.0;
|
||||
colour = mix(colour, colour * mask, uMask);
|
||||
}
|
||||
|
||||
FragColor = vec4(colour, 1.0);
|
||||
}
|
||||
@@ -1,163 +0,0 @@
|
||||
#version 300 es
|
||||
|
||||
// OpenGL ES 3.0 - Compatible con Raspberry Pi 5
|
||||
precision highp float;
|
||||
|
||||
// Configuración
|
||||
#define SCANLINES
|
||||
#define MULTISAMPLE
|
||||
|
||||
#define SCANLINE_WEIGHT 6.0
|
||||
#define SCANLINE_GAP_BRIGHTNESS 0.12
|
||||
#define BLOOM_FACTOR 3.5
|
||||
|
||||
// Inputs desde vertex shader
|
||||
in vec2 vTexCoord;
|
||||
in float vFilterWidth;
|
||||
|
||||
// Output
|
||||
out vec4 FragColor;
|
||||
|
||||
// Uniforms existentes
|
||||
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
|
||||
uniform float uOutputHeight; // altura del viewport en pixels de pantalla
|
||||
|
||||
// Nuevos uniforms
|
||||
uniform float uMask; // 0 = sin máscara de fósforo, 1 = máxima
|
||||
uniform float uGamma; // 0 = sin corrección gamma, 1 = gamma 2.4/2.2 plena
|
||||
uniform float uCurvature; // 0 = plana, 1 = curvatura barrel máxima
|
||||
uniform float uBleeding; // 0 = sin sangrado NTSC, 1 = máximo
|
||||
|
||||
// Conversores YCbCr para efecto NTSC
|
||||
vec3 rgb_to_ycc(vec3 rgb) {
|
||||
return vec3(
|
||||
0.299*rgb.r + 0.587*rgb.g + 0.114*rgb.b,
|
||||
-0.169*rgb.r - 0.331*rgb.g + 0.500*rgb.b + 0.5,
|
||||
0.500*rgb.r - 0.419*rgb.g - 0.081*rgb.b + 0.5
|
||||
);
|
||||
}
|
||||
vec3 ycc_to_rgb(vec3 ycc) {
|
||||
float y = ycc.x;
|
||||
float cb = ycc.y - 0.5;
|
||||
float cr = ycc.z - 0.5;
|
||||
return clamp(vec3(
|
||||
y + 1.402*cr,
|
||||
y - 0.344*cb - 0.714*cr,
|
||||
y + 1.772*cb
|
||||
), 0.0, 1.0);
|
||||
}
|
||||
|
||||
float CalcScanLineWeight(float dist)
|
||||
{
|
||||
return max(1.0 - dist * dist * SCANLINE_WEIGHT, SCANLINE_GAP_BRIGHTNESS);
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
vec2 texcoord = vTexCoord;
|
||||
|
||||
// Curvatura barrel CRT (distorsión en UV antes de muestrear)
|
||||
if (uCurvature > 0.0) {
|
||||
vec2 c = texcoord - vec2(0.5);
|
||||
float rsq = dot(c, c);
|
||||
vec2 dist = vec2(0.05, 0.1) * uCurvature;
|
||||
vec2 barrelScale = vec2(1.0) - 0.23 * dist;
|
||||
c += c * (dist * rsq);
|
||||
c *= barrelScale;
|
||||
if (abs(c.x) >= 0.5 || abs(c.y) >= 0.5) {
|
||||
FragColor = vec4(0.0);
|
||||
return;
|
||||
}
|
||||
texcoord = c + vec2(0.5);
|
||||
}
|
||||
|
||||
vec2 texcoordInPixels = texcoord * TextureSize;
|
||||
|
||||
float tempY = floor(texcoordInPixels.y) + 0.5;
|
||||
float yCoord = tempY / TextureSize.y;
|
||||
|
||||
// Scanline en espacio de pantalla (subpíxel)
|
||||
float scaleY = uOutputHeight / TextureSize.y;
|
||||
float screenY = texcoord.y * uOutputHeight;
|
||||
float posInRow = mod(screenY, scaleY);
|
||||
float scanLineDY = posInRow / scaleY - 0.5;
|
||||
float localFilterWidth = 1.0 / scaleY;
|
||||
float scanLineWeight = CalcScanLineWeight(scanLineDY);
|
||||
scanLineWeight += CalcScanLineWeight(scanLineDY - localFilterWidth);
|
||||
scanLineWeight += CalcScanLineWeight(scanLineDY + localFilterWidth);
|
||||
scanLineWeight *= 0.3333333;
|
||||
|
||||
// Phosphor blur en espacio textura
|
||||
float dy = texcoordInPixels.y - tempY;
|
||||
float signY = sign(dy);
|
||||
dy = dy * dy;
|
||||
dy = dy * dy;
|
||||
dy *= 8.0;
|
||||
dy /= TextureSize.y;
|
||||
dy *= signY;
|
||||
vec2 tc = vec2(texcoord.x, yCoord + dy);
|
||||
|
||||
// Muestra base en centro
|
||||
vec3 base = texture(Texture, tc).rgb;
|
||||
|
||||
// Sangrado NTSC — difuminado horizontal de crominancia
|
||||
vec3 colour;
|
||||
if (uBleeding > 0.0) {
|
||||
float tw = TextureSize.x;
|
||||
vec3 ycc = rgb_to_ycc(base);
|
||||
vec3 ycc_l2 = rgb_to_ycc(texture(Texture, tc - vec2(2.0/tw, 0.0)).rgb);
|
||||
vec3 ycc_l1 = rgb_to_ycc(texture(Texture, tc - vec2(1.0/tw, 0.0)).rgb);
|
||||
vec3 ycc_r1 = rgb_to_ycc(texture(Texture, tc + vec2(1.0/tw, 0.0)).rgb);
|
||||
vec3 ycc_r2 = rgb_to_ycc(texture(Texture, tc + vec2(2.0/tw, 0.0)).rgb);
|
||||
ycc.yz = (ycc_l2.yz + ycc_l1.yz*2.0 + ycc.yz*2.0 + ycc_r1.yz*2.0 + ycc_r2.yz) / 8.0;
|
||||
colour = mix(base, ycc_to_rgb(ycc), uBleeding);
|
||||
} else {
|
||||
colour = base;
|
||||
}
|
||||
|
||||
// Aberración cromática
|
||||
float ca = uChroma * 0.005;
|
||||
colour.r = texture(Texture, tc + vec2(ca, 0.0)).r;
|
||||
colour.b = texture(Texture, tc - vec2(ca, 0.0)).b;
|
||||
|
||||
// Corrección gamma (linealizar antes de scanlines, codificar después)
|
||||
if (uGamma > 0.0) {
|
||||
vec3 lin = pow(colour, vec3(2.4));
|
||||
colour = mix(colour, lin, uGamma);
|
||||
}
|
||||
|
||||
// Scanlines
|
||||
scanLineWeight *= BLOOM_FACTOR;
|
||||
colour *= mix(1.0, scanLineWeight, uScanlines);
|
||||
|
||||
if (uGamma > 0.0) {
|
||||
vec3 enc = pow(colour, vec3(1.0 / 2.2));
|
||||
colour = mix(colour, enc, uGamma);
|
||||
}
|
||||
|
||||
// Viñeta
|
||||
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);
|
||||
}
|
||||
|
||||
// Máscara de fósforo RGB
|
||||
if (uMask > 0.0) {
|
||||
float whichMask = fract(gl_FragCoord.x * 0.3333333);
|
||||
vec3 mask = vec3(0.80);
|
||||
if (whichMask < 0.3333333)
|
||||
mask.x = 1.0;
|
||||
else if (whichMask < 0.6666666)
|
||||
mask.y = 1.0;
|
||||
else
|
||||
mask.z = 1.0;
|
||||
colour = mix(colour, colour * mask, uMask);
|
||||
}
|
||||
|
||||
FragColor = vec4(colour, 1.0);
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
#version 330 core
|
||||
|
||||
// Configuración
|
||||
#define SCANLINES
|
||||
#define MULTISAMPLE
|
||||
#define GAMMA
|
||||
//#define FAKE_GAMMA
|
||||
//#define CURVATURE
|
||||
//#define SHARPER
|
||||
#define MASK_TYPE 2
|
||||
|
||||
#define CURVATURE_X 0.05
|
||||
#define CURVATURE_Y 0.1
|
||||
#define MASK_BRIGHTNESS 0.80
|
||||
#define SCANLINE_WEIGHT 6.0
|
||||
#define SCANLINE_GAP_BRIGHTNESS 0.12
|
||||
#define BLOOM_FACTOR 3.5
|
||||
#define INPUT_GAMMA 2.4
|
||||
#define OUTPUT_GAMMA 2.2
|
||||
|
||||
// Inputs (desde VAO)
|
||||
layout(location = 0) in vec2 aPosition;
|
||||
layout(location = 1) in vec2 aTexCoord;
|
||||
|
||||
// Outputs al fragment shader
|
||||
out vec2 vTexCoord;
|
||||
out float vFilterWidth;
|
||||
#if defined(CURVATURE)
|
||||
out vec2 vScreenScale;
|
||||
#endif
|
||||
|
||||
// Uniforms
|
||||
uniform vec2 TextureSize;
|
||||
|
||||
void main()
|
||||
{
|
||||
#if defined(CURVATURE)
|
||||
vScreenScale = vec2(1.0, 1.0);
|
||||
#endif
|
||||
// Calcula filterWidth dinámicamente basándose en la altura de la textura
|
||||
vFilterWidth = (768.0 / TextureSize.y) / 3.0;
|
||||
|
||||
// Pasar coordenadas de textura (invertir Y para SDL)
|
||||
vTexCoord = vec2(aTexCoord.x, 1.0 - aTexCoord.y) * 1.0001;
|
||||
|
||||
// Posición del vértice (ya en espacio de clip [-1, 1])
|
||||
gl_Position = vec4(aPosition, 0.0, 1.0);
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
#version 300 es
|
||||
|
||||
// OpenGL ES 3.0 - Compatible con Raspberry Pi 5
|
||||
precision highp float;
|
||||
|
||||
// Configuración
|
||||
#define SCANLINES
|
||||
#define MULTISAMPLE
|
||||
#define GAMMA
|
||||
//#define FAKE_GAMMA
|
||||
//#define CURVATURE
|
||||
//#define SHARPER
|
||||
#define MASK_TYPE 2
|
||||
|
||||
#define CURVATURE_X 0.05
|
||||
#define CURVATURE_Y 0.1
|
||||
#define MASK_BRIGHTNESS 0.80
|
||||
#define SCANLINE_WEIGHT 6.0
|
||||
#define SCANLINE_GAP_BRIGHTNESS 0.12
|
||||
#define BLOOM_FACTOR 3.5
|
||||
#define INPUT_GAMMA 2.4
|
||||
#define OUTPUT_GAMMA 2.2
|
||||
|
||||
// Inputs (desde VAO)
|
||||
layout(location = 0) in vec2 aPosition;
|
||||
layout(location = 1) in vec2 aTexCoord;
|
||||
|
||||
// Outputs al fragment shader
|
||||
out vec2 vTexCoord;
|
||||
out float vFilterWidth;
|
||||
#if defined(CURVATURE)
|
||||
out vec2 vScreenScale;
|
||||
#endif
|
||||
|
||||
// Uniforms
|
||||
uniform vec2 TextureSize;
|
||||
|
||||
void main()
|
||||
{
|
||||
#if defined(CURVATURE)
|
||||
vScreenScale = vec2(1.0, 1.0);
|
||||
#endif
|
||||
// Calcula filterWidth dinámicamente basándose en la altura de la textura
|
||||
vFilterWidth = (768.0 / TextureSize.y) / 3.0;
|
||||
|
||||
// Pasar coordenadas de textura (invertir Y para SDL)
|
||||
vTexCoord = vec2(aTexCoord.x, 1.0 - aTexCoord.y) * 1.0001;
|
||||
|
||||
// Posición del vértice (ya en espacio de clip [-1, 1])
|
||||
gl_Position = vec4(aPosition, 0.0, 1.0);
|
||||
}
|
||||
@@ -1,542 +0,0 @@
|
||||
#include "core/rendering/opengl/opengl_shader.hpp"
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
|
||||
namespace Rendering {
|
||||
|
||||
OpenGLShader::~OpenGLShader() {
|
||||
cleanup();
|
||||
}
|
||||
|
||||
#ifndef __APPLE__
|
||||
auto OpenGLShader::initGLExtensions() -> bool {
|
||||
glCreateShader = (PFNGLCREATESHADERPROC)SDL_GL_GetProcAddress("glCreateShader");
|
||||
glShaderSource = (PFNGLSHADERSOURCEPROC)SDL_GL_GetProcAddress("glShaderSource");
|
||||
glCompileShader = (PFNGLCOMPILESHADERPROC)SDL_GL_GetProcAddress("glCompileShader");
|
||||
glGetShaderiv = (PFNGLGETSHADERIVPROC)SDL_GL_GetProcAddress("glGetShaderiv");
|
||||
glGetShaderInfoLog = (PFNGLGETSHADERINFOLOGPROC)SDL_GL_GetProcAddress("glGetShaderInfoLog");
|
||||
glDeleteShader = (PFNGLDELETESHADERPROC)SDL_GL_GetProcAddress("glDeleteShader");
|
||||
glAttachShader = (PFNGLATTACHSHADERPROC)SDL_GL_GetProcAddress("glAttachShader");
|
||||
glCreateProgram = (PFNGLCREATEPROGRAMPROC)SDL_GL_GetProcAddress("glCreateProgram");
|
||||
glLinkProgram = (PFNGLLINKPROGRAMPROC)SDL_GL_GetProcAddress("glLinkProgram");
|
||||
glValidateProgram = (PFNGLVALIDATEPROGRAMPROC)SDL_GL_GetProcAddress("glValidateProgram");
|
||||
glGetProgramiv = (PFNGLGETPROGRAMIVPROC)SDL_GL_GetProcAddress("glGetProgramiv");
|
||||
glGetProgramInfoLog = (PFNGLGETPROGRAMINFOLOGPROC)SDL_GL_GetProcAddress("glGetProgramInfoLog");
|
||||
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");
|
||||
glDeleteVertexArrays = (PFNGLDELETEVERTEXARRAYSPROC)SDL_GL_GetProcAddress("glDeleteVertexArrays");
|
||||
glGenBuffers = (PFNGLGENBUFFERSPROC)SDL_GL_GetProcAddress("glGenBuffers");
|
||||
glBindBuffer = (PFNGLBINDBUFFERPROC)SDL_GL_GetProcAddress("glBindBuffer");
|
||||
glBufferData = (PFNGLBUFFERDATAPROC)SDL_GL_GetProcAddress("glBufferData");
|
||||
glDeleteBuffers = (PFNGLDELETEBUFFERSPROC)SDL_GL_GetProcAddress("glDeleteBuffers");
|
||||
glVertexAttribPointer = (PFNGLVERTEXATTRIBPOINTERPROC)SDL_GL_GetProcAddress("glVertexAttribPointer");
|
||||
glEnableVertexAttribArray = (PFNGLENABLEVERTEXATTRIBARRAYPROC)SDL_GL_GetProcAddress("glEnableVertexAttribArray");
|
||||
|
||||
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) &&
|
||||
(glUniform1f != nullptr) && (glUniform2f != nullptr) &&
|
||||
(glGenVertexArrays != nullptr) && (glBindVertexArray != nullptr) && (glDeleteVertexArrays != nullptr) &&
|
||||
(glGenBuffers != nullptr) && (glBindBuffer != nullptr) && (glBufferData != nullptr) && (glDeleteBuffers != nullptr) &&
|
||||
(glVertexAttribPointer != nullptr) && (glEnableVertexAttribArray != nullptr);
|
||||
}
|
||||
#endif
|
||||
|
||||
void OpenGLShader::checkGLError(const char* operation) {
|
||||
GLenum error = glGetError();
|
||||
if (error != GL_NO_ERROR) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
||||
"Error OpenGL en %s: 0x%x",
|
||||
operation,
|
||||
error);
|
||||
}
|
||||
}
|
||||
|
||||
auto OpenGLShader::compileShader(const std::string& source, GLenum shader_type) -> GLuint {
|
||||
if (source.empty()) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
||||
"ERROR: El código fuente del shader está vacío");
|
||||
return 0;
|
||||
}
|
||||
|
||||
GLuint shader_id = glCreateShader(shader_type);
|
||||
if (shader_id == 0) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error al crear shader");
|
||||
checkGLError("glCreateShader");
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char* sources[1] = {source.c_str()};
|
||||
glShaderSource(shader_id, 1, sources, nullptr);
|
||||
checkGLError("glShaderSource");
|
||||
|
||||
glCompileShader(shader_id);
|
||||
checkGLError("glCompileShader");
|
||||
|
||||
// Verificar compilación
|
||||
GLint compiled = GL_FALSE;
|
||||
glGetShaderiv(shader_id, GL_COMPILE_STATUS, &compiled);
|
||||
if (compiled != GL_TRUE) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
||||
"Error en compilación del shader");
|
||||
GLint log_length;
|
||||
glGetShaderiv(shader_id, GL_INFO_LOG_LENGTH, &log_length);
|
||||
if (log_length > 0) {
|
||||
std::vector<char> log(log_length);
|
||||
glGetShaderInfoLog(shader_id, log_length, &log_length, log.data());
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
||||
"Log de compilación: %s",
|
||||
log.data());
|
||||
}
|
||||
glDeleteShader(shader_id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return shader_id;
|
||||
}
|
||||
|
||||
auto OpenGLShader::linkProgram(GLuint vertex_shader, GLuint fragment_shader) -> GLuint {
|
||||
GLuint program = glCreateProgram();
|
||||
if (program == 0) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
||||
"Error al crear programa de shaders");
|
||||
return 0;
|
||||
}
|
||||
|
||||
glAttachShader(program, vertex_shader);
|
||||
checkGLError("glAttachShader(vertex)");
|
||||
glAttachShader(program, fragment_shader);
|
||||
checkGLError("glAttachShader(fragment)");
|
||||
|
||||
glLinkProgram(program);
|
||||
checkGLError("glLinkProgram");
|
||||
|
||||
// Verificar enlace
|
||||
GLint linked = GL_FALSE;
|
||||
glGetProgramiv(program, GL_LINK_STATUS, &linked);
|
||||
if (linked != GL_TRUE) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
||||
"Error al enlazar programa");
|
||||
GLint log_length;
|
||||
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &log_length);
|
||||
if (log_length > 0) {
|
||||
std::vector<char> log(log_length);
|
||||
glGetProgramInfoLog(program, log_length, &log_length, log.data());
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
||||
"Log de enlace: %s",
|
||||
log.data());
|
||||
}
|
||||
glDeleteProgram(program);
|
||||
return 0;
|
||||
}
|
||||
|
||||
glValidateProgram(program);
|
||||
checkGLError("glValidateProgram");
|
||||
|
||||
return program;
|
||||
}
|
||||
|
||||
void OpenGLShader::createQuadGeometry() {
|
||||
// Datos del quad: posición (x, y) + coordenadas de textura (u, v)
|
||||
// Formato: x, y, u, v
|
||||
float vertices[] = {
|
||||
// Posición // TexCoords
|
||||
-1.0F,
|
||||
-1.0F,
|
||||
0.0F,
|
||||
0.0F, // Inferior izquierda
|
||||
1.0F,
|
||||
-1.0F,
|
||||
1.0F,
|
||||
0.0F, // Inferior derecha
|
||||
1.0F,
|
||||
1.0F,
|
||||
1.0F,
|
||||
1.0F, // Superior derecha
|
||||
-1.0F,
|
||||
1.0F,
|
||||
0.0F,
|
||||
1.0F // Superior izquierda
|
||||
};
|
||||
|
||||
// Índices para dibujar el quad con dos triángulos
|
||||
unsigned int indices[] = {
|
||||
0,
|
||||
1,
|
||||
2, // Primer triángulo
|
||||
2,
|
||||
3,
|
||||
0 // Segundo triángulo
|
||||
};
|
||||
|
||||
// Generar y configurar VAO
|
||||
glGenVertexArrays(1, &vao_);
|
||||
glBindVertexArray(vao_);
|
||||
checkGLError("glBindVertexArray");
|
||||
|
||||
// Generar y configurar VBO
|
||||
glGenBuffers(1, &vbo_);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, vbo_);
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
|
||||
checkGLError("glBufferData(VBO)");
|
||||
|
||||
// Generar y configurar EBO
|
||||
glGenBuffers(1, &ebo_);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo_);
|
||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
|
||||
checkGLError("glBufferData(EBO)");
|
||||
|
||||
// Atributo 0: Posición (2 floats)
|
||||
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)nullptr);
|
||||
glEnableVertexAttribArray(0);
|
||||
checkGLError("glVertexAttribPointer(position)");
|
||||
|
||||
// Atributo 1: Coordenadas de textura (2 floats)
|
||||
// NOLINTNEXTLINE(performance-no-int-to-ptr) - OpenGL uses pointer as buffer offset
|
||||
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), reinterpret_cast<void*>(static_cast<uintptr_t>(2 * sizeof(float))));
|
||||
glEnableVertexAttribArray(1);
|
||||
checkGLError("glVertexAttribPointer(texcoord)");
|
||||
|
||||
// Desvincular
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
glBindVertexArray(0);
|
||||
}
|
||||
|
||||
auto OpenGLShader::getTextureID(SDL_Texture* texture) -> GLuint {
|
||||
if (texture == nullptr) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
SDL_PropertiesID props = SDL_GetTextureProperties(texture);
|
||||
GLuint texture_id = 0;
|
||||
|
||||
// Intentar obtener ID de textura OpenGL
|
||||
texture_id = (GLuint)(uintptr_t)SDL_GetPointerProperty(props, "SDL.texture.opengl.texture", nullptr);
|
||||
|
||||
if (texture_id == 0) {
|
||||
texture_id = (GLuint)(uintptr_t)SDL_GetPointerProperty(props, "texture.opengl.texture", nullptr);
|
||||
}
|
||||
|
||||
if (texture_id == 0) {
|
||||
texture_id = (GLuint)SDL_GetNumberProperty(props, "SDL.texture.opengl.texture", 1);
|
||||
}
|
||||
|
||||
if (texture_id == 0) {
|
||||
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
|
||||
"No se pudo obtener ID de textura OpenGL, usando 1 por defecto");
|
||||
texture_id = 1;
|
||||
}
|
||||
|
||||
return texture_id;
|
||||
}
|
||||
|
||||
auto OpenGLShader::init(SDL_Window* window,
|
||||
SDL_Texture* texture,
|
||||
const std::string& vertex_source,
|
||||
const std::string& fragment_source) -> bool {
|
||||
window_ = window;
|
||||
back_buffer_ = texture;
|
||||
renderer_ = SDL_GetRenderer(window);
|
||||
|
||||
if (renderer_ == nullptr) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
||||
"Error: No se pudo obtener el renderer");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Obtener tamaños
|
||||
SDL_GetWindowSize(window_, &window_width_, &window_height_);
|
||||
SDL_GetTextureSize(back_buffer_, &texture_width_, &texture_height_);
|
||||
|
||||
SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION,
|
||||
"Initializing shaders: window=%dx%d, texture=%.0fx%.0f",
|
||||
window_width_,
|
||||
window_height_,
|
||||
texture_width_,
|
||||
texture_height_);
|
||||
|
||||
// Verificar que es OpenGL
|
||||
const char* renderer_name = SDL_GetRendererName(renderer_);
|
||||
if ((renderer_name == nullptr) || strncmp(renderer_name, "opengl", 6) != 0) {
|
||||
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
|
||||
"Renderer is not OpenGL: %s",
|
||||
(renderer_name != nullptr) ? renderer_name : "unknown");
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifndef __APPLE__
|
||||
// Inicializar extensiones OpenGL en Windows/Linux
|
||||
if (!initGLExtensions()) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
||||
"Failed to initialize OpenGL extensions");
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Limpiar shader anterior si existe
|
||||
if (program_id_ != 0) {
|
||||
glDeleteProgram(program_id_);
|
||||
program_id_ = 0;
|
||||
}
|
||||
|
||||
// Compilar shaders
|
||||
GLuint vertex_shader = compileShader(vertex_source, GL_VERTEX_SHADER);
|
||||
GLuint fragment_shader = compileShader(fragment_source, GL_FRAGMENT_SHADER);
|
||||
|
||||
if (vertex_shader == 0 || fragment_shader == 0) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
||||
"Failed to compile shaders");
|
||||
if (vertex_shader != 0) {
|
||||
glDeleteShader(vertex_shader);
|
||||
}
|
||||
if (fragment_shader != 0) {
|
||||
glDeleteShader(fragment_shader);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Enlazar programa
|
||||
program_id_ = linkProgram(vertex_shader, fragment_shader);
|
||||
|
||||
// Limpiar shaders (ya no necesarios tras el enlace)
|
||||
glDeleteShader(vertex_shader);
|
||||
glDeleteShader(fragment_shader);
|
||||
|
||||
if (program_id_ == 0) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
||||
"Failed to create shader program");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Crear geometría del quad
|
||||
createQuadGeometry();
|
||||
|
||||
// Obtener ubicaciones de uniforms y configurar valores iniciales
|
||||
glUseProgram(program_id_);
|
||||
texture_size_location_ = glGetUniformLocation(program_id_, "TextureSize");
|
||||
if (texture_size_location_ != -1) {
|
||||
SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION,
|
||||
"Configuring TextureSize uniform: %.0fx%.0f",
|
||||
texture_width_,
|
||||
texture_height_);
|
||||
glUniform2f(texture_size_location_, texture_width_, texture_height_);
|
||||
checkGLError("glUniform2f(TextureSize)");
|
||||
} else {
|
||||
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");
|
||||
output_height_location_ = glGetUniformLocation(program_id_, "uOutputHeight");
|
||||
mask_location_ = glGetUniformLocation(program_id_, "uMask");
|
||||
gamma_location_ = glGetUniformLocation(program_id_, "uGamma");
|
||||
curvature_location_ = glGetUniformLocation(program_id_, "uCurvature");
|
||||
bleeding_location_ = glGetUniformLocation(program_id_, "uBleeding");
|
||||
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_); }
|
||||
if (mask_location_ != -1) { glUniform1f(mask_location_, postfx_mask_); }
|
||||
if (gamma_location_ != -1) { glUniform1f(gamma_location_, postfx_gamma_); }
|
||||
if (curvature_location_ != -1) { glUniform1f(curvature_location_, postfx_curvature_); }
|
||||
if (bleeding_location_ != -1) { glUniform1f(bleeding_location_, postfx_bleeding_); }
|
||||
glUseProgram(0);
|
||||
|
||||
is_initialized_ = true;
|
||||
SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION,
|
||||
"** OpenGL 3.3 Shader Backend initialized successfully");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void OpenGLShader::render() {
|
||||
if (!is_initialized_ || program_id_ == 0) {
|
||||
// Fallback: renderizado SDL normal
|
||||
SDL_SetRenderDrawColor(renderer_, 0, 0, 0, 255);
|
||||
SDL_SetRenderTarget(renderer_, nullptr);
|
||||
SDL_RenderClear(renderer_);
|
||||
SDL_RenderTexture(renderer_, back_buffer_, nullptr, nullptr);
|
||||
SDL_RenderPresent(renderer_);
|
||||
return;
|
||||
}
|
||||
|
||||
// Obtener tamaño actual de ventana (puede haber cambiado)
|
||||
int current_width;
|
||||
int current_height;
|
||||
SDL_GetWindowSize(window_, ¤t_width, ¤t_height);
|
||||
|
||||
// Guardar estados OpenGL
|
||||
GLint old_program;
|
||||
glGetIntegerv(GL_CURRENT_PROGRAM, &old_program);
|
||||
|
||||
GLint old_viewport[4];
|
||||
glGetIntegerv(GL_VIEWPORT, old_viewport);
|
||||
|
||||
GLboolean was_texture_enabled = glIsEnabled(GL_TEXTURE_2D);
|
||||
GLint old_texture;
|
||||
glGetIntegerv(GL_TEXTURE_BINDING_2D, &old_texture);
|
||||
|
||||
GLint old_vao;
|
||||
glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &old_vao);
|
||||
|
||||
// Preparar renderizado
|
||||
SDL_SetRenderDrawColor(renderer_, 0, 0, 0, 255);
|
||||
SDL_SetRenderTarget(renderer_, nullptr);
|
||||
SDL_RenderClear(renderer_);
|
||||
|
||||
// Obtener y bindear textura
|
||||
GLuint texture_id = getTextureID(back_buffer_);
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
glBindTexture(GL_TEXTURE_2D, texture_id);
|
||||
checkGLError("glBindTexture");
|
||||
|
||||
// Usar nuestro programa
|
||||
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_); }
|
||||
if (mask_location_ != -1) { glUniform1f(mask_location_, postfx_mask_); }
|
||||
if (gamma_location_ != -1) { glUniform1f(gamma_location_, postfx_gamma_); }
|
||||
if (curvature_location_ != -1) { glUniform1f(curvature_location_, postfx_curvature_); }
|
||||
if (bleeding_location_ != -1) { glUniform1f(bleeding_location_, postfx_bleeding_); }
|
||||
|
||||
// Configurar viewport (obtener tamaño lógico de SDL)
|
||||
int logical_w;
|
||||
int logical_h;
|
||||
SDL_RendererLogicalPresentation mode;
|
||||
SDL_GetRenderLogicalPresentation(renderer_, &logical_w, &logical_h, &mode);
|
||||
|
||||
if (logical_w == 0 || logical_h == 0) {
|
||||
logical_w = current_width;
|
||||
logical_h = current_height;
|
||||
}
|
||||
|
||||
// Calcular viewport considerando aspect ratio
|
||||
int viewport_x = 0;
|
||||
int viewport_y = 0;
|
||||
int viewport_w = current_width;
|
||||
int viewport_h = current_height;
|
||||
|
||||
if (mode == SDL_LOGICAL_PRESENTATION_INTEGER_SCALE) {
|
||||
int scale_x = current_width / logical_w;
|
||||
int scale_y = current_height / logical_h;
|
||||
int scale = (scale_x < scale_y) ? scale_x : scale_y;
|
||||
scale = std::max(scale, 1);
|
||||
|
||||
viewport_w = logical_w * scale;
|
||||
viewport_h = logical_h * scale;
|
||||
viewport_x = (current_width - viewport_w) / 2;
|
||||
viewport_y = (current_height - viewport_h) / 2;
|
||||
} else {
|
||||
float window_aspect = static_cast<float>(current_width) / current_height;
|
||||
float logical_aspect = static_cast<float>(logical_w) / logical_h;
|
||||
|
||||
if (window_aspect > logical_aspect) {
|
||||
viewport_w = static_cast<int>(logical_aspect * current_height);
|
||||
viewport_x = (current_width - viewport_w) / 2;
|
||||
} else {
|
||||
viewport_h = static_cast<int>(current_width / logical_aspect);
|
||||
viewport_y = (current_height - viewport_h) / 2;
|
||||
}
|
||||
}
|
||||
|
||||
glViewport(viewport_x, viewport_y, viewport_w, viewport_h);
|
||||
checkGLError("glViewport");
|
||||
|
||||
if (output_height_location_ != -1) {
|
||||
glUniform1f(output_height_location_, static_cast<float>(viewport_h));
|
||||
}
|
||||
|
||||
// Dibujar quad usando VAO
|
||||
glBindVertexArray(vao_);
|
||||
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, nullptr);
|
||||
checkGLError("glDrawElements");
|
||||
|
||||
// Presentar
|
||||
SDL_GL_SwapWindow(window_);
|
||||
|
||||
// Restaurar estados OpenGL
|
||||
glUseProgram(old_program);
|
||||
glBindTexture(GL_TEXTURE_2D, old_texture);
|
||||
if (was_texture_enabled == 0U) {
|
||||
glDisable(GL_TEXTURE_2D);
|
||||
}
|
||||
glBindVertexArray(old_vao);
|
||||
glViewport(old_viewport[0], old_viewport[1], old_viewport[2], old_viewport[3]);
|
||||
}
|
||||
|
||||
void OpenGLShader::setPostFXParams(const PostFXParams& p) {
|
||||
postfx_vignette_ = p.vignette;
|
||||
postfx_scanlines_ = p.scanlines;
|
||||
postfx_chroma_ = p.chroma;
|
||||
postfx_mask_ = p.mask;
|
||||
postfx_gamma_ = p.gamma;
|
||||
postfx_curvature_ = p.curvature;
|
||||
postfx_bleeding_ = p.bleeding;
|
||||
}
|
||||
|
||||
void OpenGLShader::setTextureSize(float width, float height) {
|
||||
if (!is_initialized_ || program_id_ == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
texture_width_ = width;
|
||||
texture_height_ = height;
|
||||
|
||||
GLint old_program;
|
||||
glGetIntegerv(GL_CURRENT_PROGRAM, &old_program);
|
||||
|
||||
glUseProgram(program_id_);
|
||||
|
||||
if (texture_size_location_ != -1) {
|
||||
glUniform2f(texture_size_location_, width, height);
|
||||
checkGLError("glUniform2f(TextureSize)");
|
||||
}
|
||||
|
||||
glUseProgram(old_program);
|
||||
}
|
||||
|
||||
void OpenGLShader::cleanup() {
|
||||
if (vao_ != 0) {
|
||||
glDeleteVertexArrays(1, &vao_);
|
||||
vao_ = 0;
|
||||
}
|
||||
|
||||
if (vbo_ != 0) {
|
||||
glDeleteBuffers(1, &vbo_);
|
||||
vbo_ = 0;
|
||||
}
|
||||
|
||||
if (ebo_ != 0) {
|
||||
glDeleteBuffers(1, &ebo_);
|
||||
ebo_ = 0;
|
||||
}
|
||||
|
||||
if (program_id_ != 0) {
|
||||
glDeleteProgram(program_id_);
|
||||
program_id_ = 0;
|
||||
}
|
||||
|
||||
is_initialized_ = false;
|
||||
window_ = nullptr;
|
||||
renderer_ = nullptr;
|
||||
back_buffer_ = nullptr;
|
||||
}
|
||||
|
||||
} // namespace Rendering
|
||||
@@ -1,119 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "core/rendering/shader_backend.hpp"
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <OpenGL/gl3.h>
|
||||
#else
|
||||
#include <SDL3/SDL_opengl.h>
|
||||
#endif
|
||||
|
||||
namespace Rendering {
|
||||
|
||||
/**
|
||||
* @brief Backend de shaders usando OpenGL 3.3 Core Profile
|
||||
*
|
||||
* Implementa el renderizado de shaders usando APIs modernas de OpenGL:
|
||||
* - VAO (Vertex Array Objects)
|
||||
* - VBO (Vertex Buffer Objects)
|
||||
* - Shaders GLSL #version 330 core
|
||||
*/
|
||||
class OpenGLShader : public ShaderBackend {
|
||||
public:
|
||||
OpenGLShader() = default;
|
||||
~OpenGLShader() override;
|
||||
|
||||
auto init(SDL_Window* window,
|
||||
SDL_Texture* texture,
|
||||
const std::string& vertex_source,
|
||||
const std::string& fragment_source) -> bool override;
|
||||
|
||||
void render() override;
|
||||
void setTextureSize(float width, float height) override;
|
||||
void setPostFXParams(const PostFXParams& p) override;
|
||||
void cleanup() final;
|
||||
[[nodiscard]] auto isHardwareAccelerated() const -> bool override { return is_initialized_; }
|
||||
|
||||
private:
|
||||
// Funciones auxiliares
|
||||
auto initGLExtensions() -> bool;
|
||||
auto compileShader(const std::string& source, GLenum shader_type) -> GLuint;
|
||||
auto linkProgram(GLuint vertex_shader, GLuint fragment_shader) -> GLuint;
|
||||
void createQuadGeometry();
|
||||
static auto getTextureID(SDL_Texture* texture) -> GLuint;
|
||||
static void checkGLError(const char* operation);
|
||||
|
||||
// Estado SDL
|
||||
SDL_Window* window_ = nullptr;
|
||||
SDL_Renderer* renderer_ = nullptr;
|
||||
SDL_Texture* back_buffer_ = nullptr;
|
||||
|
||||
// Estado OpenGL
|
||||
GLuint program_id_ = 0;
|
||||
GLuint vao_ = 0; // Vertex Array Object
|
||||
GLuint vbo_ = 0; // Vertex Buffer Object
|
||||
GLuint ebo_ = 0; // Element Buffer Object
|
||||
|
||||
// Ubicaciones de uniforms
|
||||
GLint texture_size_location_ = -1;
|
||||
GLint vignette_location_ = -1;
|
||||
GLint scanlines_location_ = -1;
|
||||
GLint chroma_location_ = -1;
|
||||
GLint output_height_location_ = -1;
|
||||
GLint mask_location_ = -1;
|
||||
GLint gamma_location_ = -1;
|
||||
GLint curvature_location_ = -1;
|
||||
GLint bleeding_location_ = -1;
|
||||
|
||||
// Valores cacheados de PostFX
|
||||
float postfx_vignette_ = 0.6F;
|
||||
float postfx_scanlines_ = 0.7F;
|
||||
float postfx_chroma_ = 0.15F;
|
||||
float postfx_mask_ = 0.0F;
|
||||
float postfx_gamma_ = 0.0F;
|
||||
float postfx_curvature_ = 0.0F;
|
||||
float postfx_bleeding_ = 0.0F;
|
||||
|
||||
// Tamaños
|
||||
int window_width_ = 0;
|
||||
int window_height_ = 0;
|
||||
float texture_width_ = 0.0F;
|
||||
float texture_height_ = 0.0F;
|
||||
|
||||
// Estado
|
||||
bool is_initialized_ = false;
|
||||
|
||||
#ifndef __APPLE__
|
||||
// NOLINTBEGIN
|
||||
// Punteros a funciones OpenGL en Windows/Linux
|
||||
PFNGLCREATESHADERPROC glCreateShader = nullptr;
|
||||
PFNGLSHADERSOURCEPROC glShaderSource = nullptr;
|
||||
PFNGLCOMPILESHADERPROC glCompileShader = nullptr;
|
||||
PFNGLGETSHADERIVPROC glGetShaderiv = nullptr;
|
||||
PFNGLGETSHADERINFOLOGPROC glGetShaderInfoLog = nullptr;
|
||||
PFNGLDELETESHADERPROC glDeleteShader = nullptr;
|
||||
PFNGLATTACHSHADERPROC glAttachShader = nullptr;
|
||||
PFNGLCREATEPROGRAMPROC glCreateProgram = nullptr;
|
||||
PFNGLLINKPROGRAMPROC glLinkProgram = nullptr;
|
||||
PFNGLVALIDATEPROGRAMPROC glValidateProgram = nullptr;
|
||||
PFNGLGETPROGRAMIVPROC glGetProgramiv = nullptr;
|
||||
PFNGLGETPROGRAMINFOLOGPROC glGetProgramInfoLog = nullptr;
|
||||
PFNGLUSEPROGRAMPROC glUseProgram = nullptr;
|
||||
PFNGLDELETEPROGRAMPROC glDeleteProgram = nullptr;
|
||||
PFNGLGETUNIFORMLOCATIONPROC glGetUniformLocation = nullptr;
|
||||
PFNGLUNIFORM1FPROC glUniform1f = nullptr;
|
||||
PFNGLUNIFORM2FPROC glUniform2f = nullptr;
|
||||
PFNGLGENVERTEXARRAYSPROC glGenVertexArrays = nullptr;
|
||||
PFNGLBINDVERTEXARRAYPROC glBindVertexArray = nullptr;
|
||||
PFNGLDELETEVERTEXARRAYSPROC glDeleteVertexArrays = nullptr;
|
||||
PFNGLGENBUFFERSPROC glGenBuffers = nullptr;
|
||||
PFNGLBINDBUFFERPROC glBindBuffer = nullptr;
|
||||
PFNGLBUFFERDATAPROC glBufferData = nullptr;
|
||||
PFNGLDELETEBUFFERSPROC glDeleteBuffers = nullptr;
|
||||
PFNGLVERTEXATTRIBPOINTERPROC glVertexAttribPointer = nullptr;
|
||||
PFNGLENABLEVERTEXATTRIBARRAYPROC glEnableVertexAttribArray = nullptr;
|
||||
// NOLINTEND
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace Rendering
|
||||
@@ -10,9 +10,6 @@
|
||||
#include <string> // Para char_traits, string, operator+, operator==
|
||||
|
||||
#include "core/input/mouse.hpp" // Para updateCursorVisibility
|
||||
#ifndef __APPLE__
|
||||
#include "core/rendering/opengl/opengl_shader.hpp" // Para OpenGLShader
|
||||
#endif
|
||||
#include "core/rendering/sdl3gpu/sdl3gpu_shader.hpp" // Para SDL3GPUShader
|
||||
#include "core/rendering/surface.hpp" // Para Surface, readPalFile
|
||||
#include "core/rendering/text.hpp" // Para Text
|
||||
@@ -223,8 +220,6 @@ void Screen::togglePostFX() {
|
||||
// Recarga el shader del preset actual sin toggle
|
||||
void Screen::reloadPostFX() {
|
||||
if (Options::video.postfx) {
|
||||
vertex_shader_source_.clear();
|
||||
fragment_shader_source_.clear();
|
||||
initShaders();
|
||||
}
|
||||
}
|
||||
@@ -446,38 +441,6 @@ auto loadData(const std::string& filepath) -> std::vector<uint8_t> {
|
||||
return Resource::Helper::loadFile(filepath);
|
||||
}
|
||||
|
||||
// Carga el contenido de los archivos GLSL
|
||||
void Screen::loadShaders() {
|
||||
if (vertex_shader_source_.empty()) {
|
||||
// Detectar si necesitamos OpenGL ES (Raspberry Pi)
|
||||
// Intentar cargar versión ES primero si existe
|
||||
auto data = loadData(Resource::List::get()->get("crtpi_vertex_es.glsl"));
|
||||
|
||||
if (data.empty()) {
|
||||
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";
|
||||
}
|
||||
|
||||
if (!data.empty()) {
|
||||
vertex_shader_source_ = std::string(data.begin(), data.end());
|
||||
}
|
||||
}
|
||||
if (fragment_shader_source_.empty()) {
|
||||
// Intentar cargar versión ES primero si existe
|
||||
auto data = loadData(Resource::List::get()->get("crtpi_fragment_es.glsl"));
|
||||
|
||||
if (data.empty()) {
|
||||
data = loadData(Resource::List::get()->get("crtpi_fragment.glsl"));
|
||||
}
|
||||
|
||||
if (!data.empty()) {
|
||||
fragment_shader_source_ = std::string(data.begin(), data.end());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Aplica los parámetros del preset actual al backend de shaders
|
||||
void Screen::applyCurrentPostFXPreset() {
|
||||
if (shader_backend_ && !Options::postfx_presets.empty()) {
|
||||
@@ -496,20 +459,10 @@ void Screen::initShaders() {
|
||||
|
||||
SDL_Texture* tex = Options::video.border.enabled ? border_texture_ : game_texture_;
|
||||
|
||||
#ifdef __APPLE__
|
||||
// macOS: usar SDL3 GPU API (Metal) via SDL3GPUShader
|
||||
if (!shader_backend_) {
|
||||
shader_backend_ = std::make_unique<Rendering::SDL3GPUShader>();
|
||||
}
|
||||
shader_backend_->init(window_, tex, "", "");
|
||||
#else
|
||||
// Win/Linux: usar OpenGL + GLSL
|
||||
loadShaders();
|
||||
if (!shader_backend_) {
|
||||
shader_backend_ = std::make_unique<Rendering::OpenGLShader>();
|
||||
}
|
||||
shader_backend_->init(window_, tex, vertex_shader_source_, fragment_shader_source_);
|
||||
#endif
|
||||
|
||||
applyCurrentPostFXPreset();
|
||||
}
|
||||
@@ -576,38 +529,12 @@ auto Screen::initSDLVideo() -> bool {
|
||||
if (!SDL_SetHint(SDL_HINT_RENDER_DRIVER, "metal")) {
|
||||
std::cout << "WARNING: Failed to set Metal hint!\n";
|
||||
}
|
||||
#else
|
||||
// Configurar hint de render driver
|
||||
if (!SDL_SetHint(SDL_HINT_RENDER_DRIVER, "opengl")) {
|
||||
std::cout << "WARNING: Failed to set OpenGL hint!\n";
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
// Windows: Pedir explícitamente OpenGL 3.3 Core Profile
|
||||
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);
|
||||
std::cout << "Solicitando OpenGL 3.3 Core Profile\n";
|
||||
#else
|
||||
// Linux: Dejar que SDL elija (Desktop 3.3 en PC, ES 3.0 en RPi automáticamente)
|
||||
std::cout << "Usando OpenGL por defecto del sistema\n";
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Crear ventana
|
||||
const auto WINDOW_WIDTH = Options::video.border.enabled ? Options::game.width + (Options::video.border.width * 2) : Options::game.width;
|
||||
const auto WINDOW_HEIGHT = Options::video.border.enabled ? Options::game.height + (Options::video.border.height * 2) : Options::game.height;
|
||||
#ifdef __APPLE__
|
||||
// SDL_WINDOW_METAL no es necesario: SDL3GPU autodetecta Metal via SDL_CreateGPUDevice.
|
||||
// SDL_Renderer también usará Metal si está disponible (via hint o autoselección).
|
||||
SDL_WindowFlags window_flags = 0;
|
||||
#elif defined(LINUX_BUILD)
|
||||
// En Linux, SDL_WINDOW_OPENGL puede entrar en conflicto con el backend del renderer;
|
||||
// el hint SDL_HINT_RENDER_DRIVER="opengl" es suficiente para seleccionar OpenGL.
|
||||
SDL_WindowFlags window_flags = 0;
|
||||
#else
|
||||
SDL_WindowFlags window_flags = SDL_WINDOW_OPENGL;
|
||||
#endif
|
||||
if (Options::video.fullscreen) {
|
||||
window_flags |= SDL_WINDOW_FULLSCREEN;
|
||||
}
|
||||
@@ -621,11 +548,6 @@ auto Screen::initSDLVideo() -> bool {
|
||||
|
||||
// Crear renderer
|
||||
renderer_ = SDL_CreateRenderer(window_, nullptr);
|
||||
if (renderer_ == nullptr) {
|
||||
// Fallback: reintentar sin forzar OpenGL (SDL elige el mejor disponible)
|
||||
std::cerr << "WARNING: OpenGL renderer failed (" << SDL_GetError() << "), trying auto-select...\n";
|
||||
SDL_SetHint(SDL_HINT_RENDER_DRIVER, "");
|
||||
renderer_ = SDL_CreateRenderer(window_, nullptr);
|
||||
if (renderer_ == nullptr) {
|
||||
std::cerr << "FATAL: Failed to create renderer! SDL Error: " << SDL_GetError() << '\n';
|
||||
SDL_DestroyWindow(window_);
|
||||
@@ -633,10 +555,6 @@ auto Screen::initSDLVideo() -> bool {
|
||||
SDL_Quit();
|
||||
return false;
|
||||
}
|
||||
// Sin OpenGL garantizado, deshabilitar shaders
|
||||
Options::video.postfx = false;
|
||||
std::cout << "WARNING: PostFX disabled (OpenGL not available)\n";
|
||||
}
|
||||
|
||||
// Configurar renderer
|
||||
const int EXTRA_WIDTH = Options::video.border.enabled ? Options::video.border.width * 2 : 0;
|
||||
|
||||
@@ -116,7 +116,6 @@ class Screen {
|
||||
void renderOverlays(); // Renderiza todos los overlays
|
||||
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
|
||||
@@ -157,8 +156,6 @@ class Screen {
|
||||
|
||||
// Shaders
|
||||
std::string info_resolution_; // Texto con la información de la pantalla
|
||||
std::string vertex_shader_source_; // Almacena el vertex shader
|
||||
std::string fragment_shader_source_; // Almacena el fragment shader
|
||||
std::vector<Uint32> pixel_buffer_; // Buffer intermedio para SDL3GPU path (surface → ARGB)
|
||||
|
||||
#ifdef _DEBUG
|
||||
|
||||
Reference in New Issue
Block a user