diff --git a/data/shaders/crtpi_240.glsl b/data/shaders/crtpi_240.glsl new file mode 100644 index 0000000..068a6f1 --- /dev/null +++ b/data/shaders/crtpi_240.glsl @@ -0,0 +1,234 @@ +/* + crt-pi - A Raspberry Pi friendly CRT shader. + + Copyright (C) 2015-2016 davej + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your option) + any later version. + + +Notes: + +This shader is designed to work well on Raspberry Pi GPUs (i.e. 1080P @ 60Hz on a game with a 4:3 aspect ratio). It pushes the Pi's GPU hard and enabling some features will slow it down so that it is no longer able to match 1080P @ 60Hz. You will need to overclock your Pi to the fastest setting in raspi-config to get the best results from this shader: 'Pi2' for Pi2 and 'Turbo' for original Pi and Pi Zero. Note: Pi2s are slower at running the shader than other Pis, this seems to be down to Pi2s lower maximum memory speed. Pi2s don't quite manage 1080P @ 60Hz - they drop about 1 in 1000 frames. You probably won't notice this, but if you do, try enabling FAKE_GAMMA. + +SCANLINES enables scanlines. You'll almost certainly want to use it with MULTISAMPLE to reduce moire effects. SCANLINE_WEIGHT defines how wide scanlines are (it is an inverse value so a higher number = thinner lines). SCANLINE_GAP_BRIGHTNESS defines how dark the gaps between the scan lines are. Darker gaps between scan lines make moire effects more likely. + +GAMMA enables gamma correction using the values in INPUT_GAMMA and OUTPUT_GAMMA. FAKE_GAMMA causes it to ignore the values in INPUT_GAMMA and OUTPUT_GAMMA and approximate gamma correction in a way which is faster than true gamma whilst still looking better than having none. You must have GAMMA defined to enable FAKE_GAMMA. + +CURVATURE distorts the screen by CURVATURE_X and CURVATURE_Y. Curvature slows things down a lot. + +By default the shader uses linear blending horizontally. If you find this too blury, enable SHARPER. + +BLOOM_FACTOR controls the increase in width for bright scanlines. + +MASK_TYPE defines what, if any, shadow mask to use. MASK_BRIGHTNESS defines how much the mask type darkens the screen. + +*/ + +#pragma parameter CURVATURE_X "Screen curvature - horizontal" 0.10 0.0 1.0 0.01 +#pragma parameter CURVATURE_Y "Screen curvature - vertical" 0.15 0.0 1.0 0.01 +#pragma parameter MASK_BRIGHTNESS "Mask brightness" 0.70 0.0 1.0 0.01 +#pragma parameter SCANLINE_WEIGHT "Scanline weight" 6.0 0.0 15.0 0.1 +#pragma parameter SCANLINE_GAP_BRIGHTNESS "Scanline gap brightness" 0.12 0.0 1.0 0.01 +#pragma parameter BLOOM_FACTOR "Bloom factor" 1.5 0.0 5.0 0.01 +#pragma parameter INPUT_GAMMA "Input gamma" 2.4 0.0 5.0 0.01 +#pragma parameter OUTPUT_GAMMA "Output gamma" 2.2 0.0 5.0 0.01 + +// Haven't put these as parameters as it would slow the code down. +#define SCANLINES +#define MULTISAMPLE +#define GAMMA +//#define FAKE_GAMMA +//#define CURVATURE +//#define SHARPER +// MASK_TYPE: 0 = none, 1 = green/magenta, 2 = trinitron(ish) +#define MASK_TYPE 2 + + +#ifdef GL_ES +#define COMPAT_PRECISION mediump +precision mediump float; +#else +#define COMPAT_PRECISION +#endif + +#ifdef PARAMETER_UNIFORM +uniform COMPAT_PRECISION float CURVATURE_X; +uniform COMPAT_PRECISION float CURVATURE_Y; +uniform COMPAT_PRECISION float MASK_BRIGHTNESS; +uniform COMPAT_PRECISION float SCANLINE_WEIGHT; +uniform COMPAT_PRECISION float SCANLINE_GAP_BRIGHTNESS; +uniform COMPAT_PRECISION float BLOOM_FACTOR; +uniform COMPAT_PRECISION float INPUT_GAMMA; +uniform COMPAT_PRECISION float OUTPUT_GAMMA; +#else +#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 +#endif + +/* COMPATIBILITY + - GLSL compilers +*/ + +//uniform vec2 TextureSize; +#if defined(CURVATURE) +varying vec2 screenScale; +#endif +varying vec2 TEX0; +varying float filterWidth; + +#if defined(VERTEX) +//uniform mat4 MVPMatrix; +//attribute vec4 VertexCoord; +//attribute vec2 TexCoord; +//uniform vec2 InputSize; +//uniform vec2 OutputSize; + +void main() +{ +#if defined(CURVATURE) + screenScale = vec2(1.0, 1.0); //TextureSize / InputSize; +#endif + filterWidth = (768.0 / 240.0) / 3.0; + TEX0 = vec2(gl_MultiTexCoord0.x, 1.0-gl_MultiTexCoord0.y)*1.0001; + gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; +} +#elif defined(FRAGMENT) + +uniform sampler2D Texture; + +#if defined(CURVATURE) +vec2 Distort(vec2 coord) +{ + vec2 CURVATURE_DISTORTION = vec2(CURVATURE_X, CURVATURE_Y); + // Barrel distortion shrinks the display area a bit, this will allow us to counteract that. + vec2 barrelScale = 1.0 - (0.23 * CURVATURE_DISTORTION); + coord *= screenScale; + 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); // If out of bounds, return an invalid value. + else + { + coord += vec2(0.5); + coord /= screenScale; + } + + 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-filterWidth); + scanLineWeight += CalcScanLineWeight(dy+filterWidth); + scanLineWeight *= 0.3333333; +#endif + return scanLineWeight; +} + +void main() +{ + vec2 TextureSize = vec2(320.0, 240.0); +#if defined(CURVATURE) + vec2 texcoord = Distort(TEX0); + if (texcoord.x < 0.0) + gl_FragColor = vec4(0.0); + else +#else + vec2 texcoord = TEX0; +#endif + { + vec2 texcoordInPixels = texcoord * TextureSize; +#if defined(SHARPER) + vec2 tempCoord = floor(texcoordInPixels) + 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 = texture2D(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 + gl_FragColor = vec4(colour, 1.0); +#else +#if MASK_TYPE == 1 + float whichMask = fract((gl_FragCoord.x*1.0001) * 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); +#elif MASK_TYPE == 2 + float whichMask = fract((gl_FragCoord.x*1.0001) * 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; +#endif + + gl_FragColor = vec4(colour * mask, 1.0); +#endif + } +} +#endif diff --git a/data/shaders/crtpi.glsl b/data/shaders/crtpi_256.glsl similarity index 99% rename from data/shaders/crtpi.glsl rename to data/shaders/crtpi_256.glsl index a5cb4e4..9347bd5 100644 --- a/data/shaders/crtpi.glsl +++ b/data/shaders/crtpi_256.glsl @@ -97,7 +97,6 @@ void main() #if defined(CURVATURE) screenScale = vec2(1.0, 1.0); //TextureSize / InputSize; #endif - //filterWidth = (768.0 / 240.0) / 3.0; filterWidth = (768.0 / 256.0) / 3.0; TEX0 = vec2(gl_MultiTexCoord0.x, 1.0-gl_MultiTexCoord0.y)*1.0001; gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; diff --git a/source/credits.h b/source/credits.h index a8616eb..cd55686 100644 --- a/source/credits.h +++ b/source/credits.h @@ -10,6 +10,8 @@ class TiledBG; class Player; class Fade; +constexpr int PLAY_AREA_HEIGHT = 228; + class Credits { private: @@ -22,18 +24,18 @@ private: std::vector> players_; // Vector con los jugadores // Variables - Uint32 ticks_ = 0; // Contador de ticks para ajustar la velocidad del programa - Uint32 ticks_speed_ = 15; // Velocidad del bucle update - Uint32 counter_ = 0; // Contador para la lógica de la clase - Uint32 counter_pre_fade_ = 0; // Contador para activar el fundido final - Uint32 counter_prevent_endless_ = 0; // Contador para evitar que el juego se quede para siempre en los creditos - int black_bars_size_ = 28; // Tamaño de las barras negras - int mini_logo_final_pos_ = 0; // Ubicación donde se detiene el minilogo - bool fading_ = false; // Indica si se está realizando el fade final - bool want_to_pass_ = false; // Indica si el jugador quiere saltarse los titulos de crédito - bool mini_logo_on_position_ = false; // Indica si el minilogo ya se ha quedado en su posición - int initial_volume_ = options.audio.music.volume; // Volumen actual al crear el objeto - int steps_ = 0; // Cantidad de pasos a dar para ir reduciendo el audio + Uint32 ticks_ = 0; // Contador de ticks para ajustar la velocidad del programa + Uint32 ticks_speed_ = 15; // Velocidad del bucle update + Uint32 counter_ = 0; // Contador para la lógica de la clase + Uint32 counter_pre_fade_ = 0; // Contador para activar el fundido final + Uint32 counter_prevent_endless_ = 0; // Contador para evitar que el juego se quede para siempre en los creditos + int black_bars_size_ = param.game.game_area.rect.h - PLAY_AREA_HEIGHT; // Tamaño de las barras negras + int mini_logo_final_pos_ = 0; // Ubicación donde se detiene el minilogo + bool fading_ = false; // Indica si se está realizando el fade final + bool want_to_pass_ = false; // Indica si el jugador quiere saltarse los titulos de crédito + bool mini_logo_on_position_ = false; // Indica si el minilogo ya se ha quedado en su posición + int initial_volume_ = options.audio.music.volume; // Volumen actual al crear el objeto + int steps_ = 0; // Cantidad de pasos a dar para ir reduciendo el audio // Rectangulos SDL_Rect credits_rect_src_ = param.game.game_area.rect; // Rectangulo con el texto de los créditos (origen) diff --git a/source/director.cpp b/source/director.cpp index d494a1d..ca00bf0 100644 --- a/source/director.cpp +++ b/source/director.cpp @@ -399,7 +399,8 @@ void Director::setFileList() Asset::get()->add(prefix + "/data/sound/logo.wav", AssetType::SOUND); // Shaders - Asset::get()->add(prefix + "/data/shaders/crtpi.glsl", AssetType::DATA); + Asset::get()->add(prefix + "/data/shaders/crtpi_256.glsl", AssetType::DATA); + Asset::get()->add(prefix + "/data/shaders/crtpi_240.glsl", AssetType::DATA); // Texturas diff --git a/source/screen.cpp b/source/screen.cpp index a213ce2..9ddaf35 100644 --- a/source/screen.cpp +++ b/source/screen.cpp @@ -200,7 +200,8 @@ void Screen::setVideoMode(ScreenVideoMode videoMode) if (options.video.shaders) { #ifndef NO_SHADERS - std::ifstream f(Asset::get()->get("crtpi.glsl").c_str()); + const std::string glsl_file = param.game.game_area.rect.h == 256 ? "crtpi_256.glsl" : "crtpi_240.glsl"; + std::ifstream f(Asset::get()->get(glsl_file).c_str()); std::string source((std::istreambuf_iterator(f)), std::istreambuf_iterator()); shader::init(window_, shader_canvas_, source.c_str());