diff --git a/config/assets.txt b/config/assets.txt index 59bd652..c5d677d 100644 --- a/config/assets.txt +++ b/config/assets.txt @@ -74,10 +74,13 @@ SOUND|${PREFIX}/data/sound/voice_recover.wav SOUND|${PREFIX}/data/sound/voice_thankyou.wav SOUND|${PREFIX}/data/sound/walk.wav -# Shaders +# Shaders OpenGL Desktop 3.3 (Windows/Linux) DATA|${PREFIX}/data/shaders/crtpi_vertex.glsl DATA|${PREFIX}/data/shaders/crtpi_fragment.glsl +# Shaders OpenGL ES 3.1 (Raspberry Pi) - opcionales +DATA|${PREFIX}/data/shaders/crtpi_vertex_es.glsl|optional +DATA|${PREFIX}/data/shaders/crtpi_fragment_es.glsl|optional # Texturas - Balloons ANIMATION|${PREFIX}/data/gfx/balloon/balloon0.ani ANIMATION|${PREFIX}/data/gfx/balloon/balloon1.ani diff --git a/data/shaders/crtpi_fragment_es.glsl b/data/shaders/crtpi_fragment_es.glsl new file mode 100644 index 0000000..7d5d0d0 --- /dev/null +++ b/data/shaders/crtpi_fragment_es.glsl @@ -0,0 +1,160 @@ +#version 310 es + +// OpenGL ES 3.1 - 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 vertex shader +in vec2 vTexCoord; +in float vFilterWidth; +#if defined(CURVATURE) +in vec2 vScreenScale; +#endif + +// Output +out vec4 FragColor; + +// Uniforms +uniform sampler2D Texture; +uniform vec2 TextureSize; + +#if defined(CURVATURE) +vec2 Distort(vec2 coord) +{ + vec2 CURVATURE_DISTORTION = vec2(CURVATURE_X, CURVATURE_Y); + vec2 barrelScale = vec2(1.0) - (0.23 * CURVATURE_DISTORTION); + coord *= vScreenScale; + coord -= vec2(0.5); + float rsq = coord.x * coord.x + coord.y * coord.y; + coord += coord * (CURVATURE_DISTORTION * rsq); + coord *= barrelScale; + if (abs(coord.x) >= 0.5 || abs(coord.y) >= 0.5) + coord = vec2(-1.0); + else + { + coord += vec2(0.5); + coord /= vScreenScale; + } + return coord; +} +#endif + +float CalcScanLineWeight(float dist) +{ + return max(1.0 - dist * dist * SCANLINE_WEIGHT, SCANLINE_GAP_BRIGHTNESS); +} + +float CalcScanLine(float dy) +{ + float scanLineWeight = CalcScanLineWeight(dy); +#if defined(MULTISAMPLE) + scanLineWeight += CalcScanLineWeight(dy - vFilterWidth); + scanLineWeight += CalcScanLineWeight(dy + vFilterWidth); + scanLineWeight *= 0.3333333; +#endif + return scanLineWeight; +} + +void main() +{ +#if defined(CURVATURE) + vec2 texcoord = Distort(vTexCoord); + if (texcoord.x < 0.0) { + FragColor = vec4(0.0); + return; + } +#else + vec2 texcoord = vTexCoord; +#endif + + vec2 texcoordInPixels = texcoord * TextureSize; + +#if defined(SHARPER) + vec2 tempCoord = floor(texcoordInPixels) + vec2(0.5); + vec2 coord = tempCoord / TextureSize; + vec2 deltas = texcoordInPixels - tempCoord; + float scanLineWeight = CalcScanLine(deltas.y); + vec2 signs = sign(deltas); + deltas.x *= 2.0; + deltas = deltas * deltas; + deltas.y = deltas.y * deltas.y; + deltas.x *= 0.5; + deltas.y *= 8.0; + deltas /= TextureSize; + deltas *= signs; + vec2 tc = coord + deltas; +#else + float tempY = floor(texcoordInPixels.y) + 0.5; + float yCoord = tempY / TextureSize.y; + float dy = texcoordInPixels.y - tempY; + float scanLineWeight = CalcScanLine(dy); + 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); +#endif + + vec3 colour = texture(Texture, tc).rgb; + +#if defined(SCANLINES) +#if defined(GAMMA) +#if defined(FAKE_GAMMA) + colour = colour * colour; +#else + colour = pow(colour, vec3(INPUT_GAMMA)); +#endif +#endif + scanLineWeight *= BLOOM_FACTOR; + colour *= scanLineWeight; + +#if defined(GAMMA) +#if defined(FAKE_GAMMA) + colour = sqrt(colour); +#else + colour = pow(colour, vec3(1.0 / OUTPUT_GAMMA)); +#endif +#endif +#endif + +#if MASK_TYPE == 0 + FragColor = vec4(colour, 1.0); +#elif MASK_TYPE == 1 + float whichMask = fract(gl_FragCoord.x * 0.5); + vec3 mask; + if (whichMask < 0.5) + mask = vec3(MASK_BRIGHTNESS, 1.0, MASK_BRIGHTNESS); + else + mask = vec3(1.0, MASK_BRIGHTNESS, 1.0); + FragColor = vec4(colour * mask, 1.0); +#elif MASK_TYPE == 2 + float whichMask = fract(gl_FragCoord.x * 0.3333333); + vec3 mask = vec3(MASK_BRIGHTNESS, MASK_BRIGHTNESS, MASK_BRIGHTNESS); + if (whichMask < 0.3333333) + mask.x = 1.0; + else if (whichMask < 0.6666666) + mask.y = 1.0; + else + mask.z = 1.0; + FragColor = vec4(colour * mask, 1.0); +#endif +} diff --git a/data/shaders/crtpi_vertex_es.glsl b/data/shaders/crtpi_vertex_es.glsl new file mode 100644 index 0000000..b4c8be6 --- /dev/null +++ b/data/shaders/crtpi_vertex_es.glsl @@ -0,0 +1,51 @@ +#version 310 es + +// OpenGL ES 3.1 - 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); +} diff --git a/source/screen.cpp b/source/screen.cpp index 35090ce..74f2ed0 100644 --- a/source/screen.cpp +++ b/source/screen.cpp @@ -229,15 +229,37 @@ void Screen::renderInfo() { // Carga el contenido de los archivos GLSL void Screen::loadShaders() { if (vertex_shader_source_.empty()) { - const std::string VERTEX_FILE = "crtpi_vertex.glsl"; + // Detectar si necesitamos OpenGL ES (Raspberry Pi) + // Intentar cargar versión ES primero si existe + std::string VERTEX_FILE = "crtpi_vertex_es.glsl"; auto data = Asset::get()->loadData(VERTEX_FILE); + + if (data.empty()) { + // Si no existe versión ES, usar versión Desktop + VERTEX_FILE = "crtpi_vertex.glsl"; + data = Asset::get()->loadData(VERTEX_FILE); + SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, + "Usando shaders OpenGL Desktop 3.3"); + } else { + SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, + "Usando shaders OpenGL ES 3.1 (Raspberry Pi)"); + } + if (!data.empty()) { vertex_shader_source_ = std::string(data.begin(), data.end()); } } if (fragment_shader_source_.empty()) { - const std::string FRAGMENT_FILE = "crtpi_fragment.glsl"; + // Intentar cargar versión ES primero si existe + std::string FRAGMENT_FILE = "crtpi_fragment_es.glsl"; auto data = Asset::get()->loadData(FRAGMENT_FILE); + + if (data.empty()) { + // Si no existe versión ES, usar versión Desktop + FRAGMENT_FILE = "crtpi_fragment.glsl"; + data = Asset::get()->loadData(FRAGMENT_FILE); + } + if (!data.empty()) { fragment_shader_source_ = std::string(data.begin(), data.end()); } @@ -330,10 +352,19 @@ auto Screen::initSDLVideo() -> bool { SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Warning: Failed to set OpenGL hint!"); } - // Configurar contexto OpenGL 3.3 Core Profile + // Configurar contexto OpenGL + // En Raspberry Pi, NO pedir Core Profile porque solo soporta OpenGL ES + // SDL elegirá automáticamente OpenGL ES 3.1 si está disponible +#ifdef _WIN32 + // Solo en Windows pedimos 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); + SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Solicitando OpenGL 3.3 Core Profile"); +#else + // Linux/RPi: dejar que SDL elija (probablemente OpenGL ES) + SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Usando OpenGL por defecto del sistema"); +#endif #endif // Crear ventana