nous postfx
This commit is contained in:
@@ -3,32 +3,19 @@
|
||||
// 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
|
||||
// Uniforms existentes
|
||||
uniform sampler2D Texture;
|
||||
uniform vec2 TextureSize;
|
||||
uniform float uVignette; // 0 = sin viñeta, 1 = máxima
|
||||
@@ -36,78 +23,63 @@ 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
|
||||
|
||||
#if defined(CURVATURE)
|
||||
vec2 Distort(vec2 coord)
|
||||
{
|
||||
vec2 CURVATURE_DISTORTION = vec2(CURVATURE_X, CURVATURE_Y);
|
||||
vec2 barrelScale = 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;
|
||||
// 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);
|
||||
}
|
||||
#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
|
||||
|
||||
// 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;
|
||||
|
||||
#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;
|
||||
|
||||
// Scanline en espacio de pantalla (subpíxel)
|
||||
float scaleY = uOutputHeight / TextureSize.y;
|
||||
float screenY = vTexCoord.y * uOutputHeight;
|
||||
float screenY = texcoord.y * uOutputHeight;
|
||||
float posInRow = mod(screenY, scaleY);
|
||||
float scanLineDY = posInRow / scaleY - 0.5;
|
||||
float localFilterWidth = 1.0 / scaleY;
|
||||
@@ -116,7 +88,7 @@ void main()
|
||||
scanLineWeight += CalcScanLineWeight(scanLineDY + localFilterWidth);
|
||||
scanLineWeight *= 0.3333333;
|
||||
|
||||
// Phosphor blur en espacio textura (sin cambios)
|
||||
// Phosphor blur en espacio textura
|
||||
float dy = texcoordInPixels.y - tempY;
|
||||
float signY = sign(dy);
|
||||
dy = dy * dy;
|
||||
@@ -125,59 +97,64 @@ void main()
|
||||
dy /= TextureSize.y;
|
||||
dy *= signY;
|
||||
vec2 tc = vec2(texcoord.x, yCoord + dy);
|
||||
#endif
|
||||
|
||||
float ca = uChroma * 0.005;
|
||||
// Muestra base en centro
|
||||
vec3 base = texture(Texture, tc).rgb;
|
||||
|
||||
// Sangrado NTSC — difuminado horizontal de crominancia
|
||||
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 (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;
|
||||
}
|
||||
|
||||
#if defined(SCANLINES)
|
||||
#if defined(GAMMA)
|
||||
#if defined(FAKE_GAMMA)
|
||||
colour = colour * colour;
|
||||
#else
|
||||
colour = pow(colour, vec3(INPUT_GAMMA));
|
||||
#endif
|
||||
#endif
|
||||
// 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 defined(GAMMA)
|
||||
#if defined(FAKE_GAMMA)
|
||||
colour = sqrt(colour);
|
||||
#else
|
||||
colour = pow(colour, vec3(1.0 / OUTPUT_GAMMA));
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
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);
|
||||
}
|
||||
|
||||
#if MASK_TYPE == 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);
|
||||
#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
|
||||
}
|
||||
|
||||
@@ -6,32 +6,19 @@ 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
|
||||
// Uniforms existentes
|
||||
uniform sampler2D Texture;
|
||||
uniform vec2 TextureSize;
|
||||
uniform float uVignette; // 0 = sin viñeta, 1 = máxima
|
||||
@@ -39,78 +26,63 @@ 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
|
||||
|
||||
#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;
|
||||
// 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);
|
||||
}
|
||||
#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
|
||||
|
||||
// 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;
|
||||
|
||||
#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;
|
||||
|
||||
// Scanline en espacio de pantalla (subpíxel)
|
||||
float scaleY = uOutputHeight / TextureSize.y;
|
||||
float screenY = vTexCoord.y * uOutputHeight;
|
||||
float screenY = texcoord.y * uOutputHeight;
|
||||
float posInRow = mod(screenY, scaleY);
|
||||
float scanLineDY = posInRow / scaleY - 0.5;
|
||||
float localFilterWidth = 1.0 / scaleY;
|
||||
@@ -119,7 +91,7 @@ void main()
|
||||
scanLineWeight += CalcScanLineWeight(scanLineDY + localFilterWidth);
|
||||
scanLineWeight *= 0.3333333;
|
||||
|
||||
// Phosphor blur en espacio textura (sin cambios)
|
||||
// Phosphor blur en espacio textura
|
||||
float dy = texcoordInPixels.y - tempY;
|
||||
float signY = sign(dy);
|
||||
dy = dy * dy;
|
||||
@@ -128,59 +100,64 @@ void main()
|
||||
dy /= TextureSize.y;
|
||||
dy *= signY;
|
||||
vec2 tc = vec2(texcoord.x, yCoord + dy);
|
||||
#endif
|
||||
|
||||
float ca = uChroma * 0.005;
|
||||
// Muestra base en centro
|
||||
vec3 base = texture(Texture, tc).rgb;
|
||||
|
||||
// Sangrado NTSC — difuminado horizontal de crominancia
|
||||
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 (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;
|
||||
}
|
||||
|
||||
#if defined(SCANLINES)
|
||||
#if defined(GAMMA)
|
||||
#if defined(FAKE_GAMMA)
|
||||
colour = colour * colour;
|
||||
#else
|
||||
colour = pow(colour, vec3(INPUT_GAMMA));
|
||||
#endif
|
||||
#endif
|
||||
// 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 defined(GAMMA)
|
||||
#if defined(FAKE_GAMMA)
|
||||
colour = sqrt(colour);
|
||||
#else
|
||||
colour = pow(colour, vec3(1.0 / OUTPUT_GAMMA));
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
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);
|
||||
}
|
||||
|
||||
#if MASK_TYPE == 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);
|
||||
#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
|
||||
}
|
||||
|
||||
126
data/shaders/postfx.frag
Normal file
126
data/shaders/postfx.frag
Normal file
@@ -0,0 +1,126 @@
|
||||
#version 450
|
||||
|
||||
// Vulkan GLSL fragment shader — PostFX effects
|
||||
// Used for SDL3 GPU API (SPIR-V path, Win/Linux).
|
||||
// Compile: glslc postfx.frag -o postfx.frag.spv
|
||||
// xxd -i postfx.frag.spv > ../../source/core/rendering/sdl3gpu/postfx_frag_spv.h
|
||||
//
|
||||
// PostFXUniforms must match exactly the C++ struct in sdl3gpu_shader.hpp
|
||||
// (8 floats, 32 bytes, std140/scalar layout).
|
||||
|
||||
layout(location = 0) in vec2 v_uv;
|
||||
layout(location = 0) out vec4 out_color;
|
||||
|
||||
layout(set = 2, binding = 0) uniform sampler2D scene;
|
||||
|
||||
layout(set = 3, binding = 0) uniform PostFXUniforms {
|
||||
float vignette_strength;
|
||||
float chroma_strength;
|
||||
float scanline_strength;
|
||||
float screen_height;
|
||||
float mask_strength;
|
||||
float gamma_strength;
|
||||
float curvature;
|
||||
float bleeding;
|
||||
} u;
|
||||
|
||||
// YCbCr helpers for NTSC bleeding
|
||||
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);
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec2 uv = v_uv;
|
||||
|
||||
// Curvatura barrel CRT
|
||||
if (u.curvature > 0.0) {
|
||||
vec2 c = uv - 0.5;
|
||||
float rsq = dot(c, c);
|
||||
vec2 dist = vec2(0.05, 0.1) * u.curvature;
|
||||
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) {
|
||||
out_color = vec4(0.0, 0.0, 0.0, 1.0);
|
||||
return;
|
||||
}
|
||||
uv = c + 0.5;
|
||||
}
|
||||
|
||||
// Muestra base
|
||||
vec3 base = texture(scene, uv).rgb;
|
||||
|
||||
// Sangrado NTSC — difuminado horizontal de crominancia
|
||||
vec3 colour;
|
||||
if (u.bleeding > 0.0) {
|
||||
float tw = float(textureSize(scene, 0).x);
|
||||
vec3 ycc = rgb_to_ycc(base);
|
||||
vec3 ycc_l2 = rgb_to_ycc(texture(scene, uv - vec2(2.0/tw, 0.0)).rgb);
|
||||
vec3 ycc_l1 = rgb_to_ycc(texture(scene, uv - vec2(1.0/tw, 0.0)).rgb);
|
||||
vec3 ycc_r1 = rgb_to_ycc(texture(scene, uv + vec2(1.0/tw, 0.0)).rgb);
|
||||
vec3 ycc_r2 = rgb_to_ycc(texture(scene, uv + 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), u.bleeding);
|
||||
} else {
|
||||
colour = base;
|
||||
}
|
||||
|
||||
// Aberración cromática
|
||||
float ca = u.chroma_strength * 0.005;
|
||||
colour.r = texture(scene, uv + vec2(ca, 0.0)).r;
|
||||
colour.b = texture(scene, uv - vec2(ca, 0.0)).b;
|
||||
|
||||
// Corrección gamma (linealizar antes de scanlines, codificar después)
|
||||
if (u.gamma_strength > 0.0) {
|
||||
vec3 lin = pow(colour, vec3(2.4));
|
||||
colour = mix(colour, lin, u.gamma_strength);
|
||||
}
|
||||
|
||||
// Scanlines
|
||||
float texHeight = float(textureSize(scene, 0).y);
|
||||
float scaleY = u.screen_height / texHeight;
|
||||
float screenY = uv.y * u.screen_height;
|
||||
float posInRow = mod(screenY, scaleY);
|
||||
float scanLineDY = posInRow / scaleY - 0.5;
|
||||
float scan = max(1.0 - scanLineDY * scanLineDY * 6.0, 0.12) * 3.5;
|
||||
colour *= mix(1.0, scan, u.scanline_strength);
|
||||
|
||||
if (u.gamma_strength > 0.0) {
|
||||
vec3 enc = pow(colour, vec3(1.0 / 2.2));
|
||||
colour = mix(colour, enc, u.gamma_strength);
|
||||
}
|
||||
|
||||
// Viñeta
|
||||
vec2 d = uv - 0.5;
|
||||
float vignette = 1.0 - dot(d, d) * u.vignette_strength;
|
||||
colour *= clamp(vignette, 0.0, 1.0);
|
||||
|
||||
// Máscara de fósforo RGB
|
||||
if (u.mask_strength > 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, u.mask_strength);
|
||||
}
|
||||
|
||||
out_color = vec4(colour, 1.0);
|
||||
}
|
||||
24
data/shaders/postfx.vert
Normal file
24
data/shaders/postfx.vert
Normal file
@@ -0,0 +1,24 @@
|
||||
#version 450
|
||||
|
||||
// Vulkan GLSL vertex shader — postfx full-screen triangle
|
||||
// Used for SDL3 GPU API (SPIR-V path, Win/Linux).
|
||||
// Compile: glslc postfx.vert -o postfx.vert.spv
|
||||
// xxd -i postfx.vert.spv > ../../source/core/rendering/sdl3gpu/postfx_vert_spv.h
|
||||
|
||||
layout(location = 0) out vec2 v_uv;
|
||||
|
||||
void main() {
|
||||
// Full-screen triangle (no vertex buffer needed)
|
||||
const vec2 positions[3] = vec2[3](
|
||||
vec2(-1.0, -1.0),
|
||||
vec2( 3.0, -1.0),
|
||||
vec2(-1.0, 3.0)
|
||||
);
|
||||
const vec2 uvs[3] = vec2[3](
|
||||
vec2(0.0, 1.0),
|
||||
vec2(2.0, 1.0),
|
||||
vec2(0.0,-1.0)
|
||||
);
|
||||
gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0);
|
||||
v_uv = uvs[gl_VertexIndex];
|
||||
}
|
||||
Reference in New Issue
Block a user