forked from jaildesigner-jailgames/jaildoctors_dilemma
nous postfx
This commit is contained in:
@@ -3,32 +3,19 @@
|
|||||||
// Configuración
|
// Configuración
|
||||||
#define SCANLINES
|
#define SCANLINES
|
||||||
#define MULTISAMPLE
|
#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_WEIGHT 6.0
|
||||||
#define SCANLINE_GAP_BRIGHTNESS 0.12
|
#define SCANLINE_GAP_BRIGHTNESS 0.12
|
||||||
#define BLOOM_FACTOR 3.5
|
#define BLOOM_FACTOR 3.5
|
||||||
#define INPUT_GAMMA 2.4
|
|
||||||
#define OUTPUT_GAMMA 2.2
|
|
||||||
|
|
||||||
// Inputs desde vertex shader
|
// Inputs desde vertex shader
|
||||||
in vec2 vTexCoord;
|
in vec2 vTexCoord;
|
||||||
in float vFilterWidth;
|
in float vFilterWidth;
|
||||||
#if defined(CURVATURE)
|
|
||||||
in vec2 vScreenScale;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Output
|
// Output
|
||||||
out vec4 FragColor;
|
out vec4 FragColor;
|
||||||
|
|
||||||
// Uniforms
|
// Uniforms existentes
|
||||||
uniform sampler2D Texture;
|
uniform sampler2D Texture;
|
||||||
uniform vec2 TextureSize;
|
uniform vec2 TextureSize;
|
||||||
uniform float uVignette; // 0 = sin viñeta, 1 = máxima
|
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 uChroma; // 0 = sin aberración, 1 = máxima
|
||||||
uniform float uOutputHeight; // altura del viewport en pixels de pantalla
|
uniform float uOutputHeight; // altura del viewport en pixels de pantalla
|
||||||
|
|
||||||
#if defined(CURVATURE)
|
// Nuevos uniforms
|
||||||
vec2 Distort(vec2 coord)
|
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
|
||||||
vec2 CURVATURE_DISTORTION = vec2(CURVATURE_X, CURVATURE_Y);
|
uniform float uCurvature; // 0 = plana, 1 = curvatura barrel máxima
|
||||||
vec2 barrelScale = 1.0 - (0.23 * CURVATURE_DISTORTION);
|
uniform float uBleeding; // 0 = sin sangrado NTSC, 1 = máximo
|
||||||
coord *= vScreenScale;
|
|
||||||
coord -= vec2(0.5);
|
// Conversores YCbCr para efecto NTSC
|
||||||
float rsq = coord.x * coord.x + coord.y * coord.y;
|
vec3 rgb_to_ycc(vec3 rgb) {
|
||||||
coord += coord * (CURVATURE_DISTORTION * rsq);
|
return vec3(
|
||||||
coord *= barrelScale;
|
0.299*rgb.r + 0.587*rgb.g + 0.114*rgb.b,
|
||||||
if (abs(coord.x) >= 0.5 || abs(coord.y) >= 0.5)
|
-0.169*rgb.r - 0.331*rgb.g + 0.500*rgb.b + 0.5,
|
||||||
coord = vec2(-1.0);
|
0.500*rgb.r - 0.419*rgb.g - 0.081*rgb.b + 0.5
|
||||||
else
|
);
|
||||||
{
|
}
|
||||||
coord += vec2(0.5);
|
vec3 ycc_to_rgb(vec3 ycc) {
|
||||||
coord /= vScreenScale;
|
float y = ycc.x;
|
||||||
}
|
float cb = ycc.y - 0.5;
|
||||||
return coord;
|
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)
|
float CalcScanLineWeight(float dist)
|
||||||
{
|
{
|
||||||
return max(1.0 - dist * dist * SCANLINE_WEIGHT, SCANLINE_GAP_BRIGHTNESS);
|
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()
|
void main()
|
||||||
{
|
{
|
||||||
#if defined(CURVATURE)
|
|
||||||
vec2 texcoord = Distort(vTexCoord);
|
|
||||||
if (texcoord.x < 0.0) {
|
|
||||||
FragColor = vec4(0.0);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
vec2 texcoord = vTexCoord;
|
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;
|
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 tempY = floor(texcoordInPixels.y) + 0.5;
|
||||||
float yCoord = tempY / TextureSize.y;
|
float yCoord = tempY / TextureSize.y;
|
||||||
|
|
||||||
// Scanline en espacio de pantalla (subpíxel)
|
// Scanline en espacio de pantalla (subpíxel)
|
||||||
float scaleY = uOutputHeight / TextureSize.y;
|
float scaleY = uOutputHeight / TextureSize.y;
|
||||||
float screenY = vTexCoord.y * uOutputHeight;
|
float screenY = texcoord.y * uOutputHeight;
|
||||||
float posInRow = mod(screenY, scaleY);
|
float posInRow = mod(screenY, scaleY);
|
||||||
float scanLineDY = posInRow / scaleY - 0.5;
|
float scanLineDY = posInRow / scaleY - 0.5;
|
||||||
float localFilterWidth = 1.0 / scaleY;
|
float localFilterWidth = 1.0 / scaleY;
|
||||||
@@ -116,7 +88,7 @@ void main()
|
|||||||
scanLineWeight += CalcScanLineWeight(scanLineDY + localFilterWidth);
|
scanLineWeight += CalcScanLineWeight(scanLineDY + localFilterWidth);
|
||||||
scanLineWeight *= 0.3333333;
|
scanLineWeight *= 0.3333333;
|
||||||
|
|
||||||
// Phosphor blur en espacio textura (sin cambios)
|
// Phosphor blur en espacio textura
|
||||||
float dy = texcoordInPixels.y - tempY;
|
float dy = texcoordInPixels.y - tempY;
|
||||||
float signY = sign(dy);
|
float signY = sign(dy);
|
||||||
dy = dy * dy;
|
dy = dy * dy;
|
||||||
@@ -125,59 +97,64 @@ void main()
|
|||||||
dy /= TextureSize.y;
|
dy /= TextureSize.y;
|
||||||
dy *= signY;
|
dy *= signY;
|
||||||
vec2 tc = vec2(texcoord.x, yCoord + dy);
|
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;
|
vec3 colour;
|
||||||
colour.r = texture(Texture, tc + vec2(ca, 0.0)).r;
|
if (uBleeding > 0.0) {
|
||||||
colour.g = texture(Texture, tc).g;
|
float tw = TextureSize.x;
|
||||||
colour.b = texture(Texture, tc - vec2(ca, 0.0)).b;
|
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)
|
// Aberración cromática
|
||||||
#if defined(GAMMA)
|
float ca = uChroma * 0.005;
|
||||||
#if defined(FAKE_GAMMA)
|
colour.r = texture(Texture, tc + vec2(ca, 0.0)).r;
|
||||||
colour = colour * colour;
|
colour.b = texture(Texture, tc - vec2(ca, 0.0)).b;
|
||||||
#else
|
|
||||||
colour = pow(colour, vec3(INPUT_GAMMA));
|
// Corrección gamma (linealizar antes de scanlines, codificar después)
|
||||||
#endif
|
if (uGamma > 0.0) {
|
||||||
#endif
|
vec3 lin = pow(colour, vec3(2.4));
|
||||||
|
colour = mix(colour, lin, uGamma);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scanlines
|
||||||
scanLineWeight *= BLOOM_FACTOR;
|
scanLineWeight *= BLOOM_FACTOR;
|
||||||
colour *= mix(1.0, scanLineWeight, uScanlines);
|
colour *= mix(1.0, scanLineWeight, uScanlines);
|
||||||
|
|
||||||
#if defined(GAMMA)
|
if (uGamma > 0.0) {
|
||||||
#if defined(FAKE_GAMMA)
|
vec3 enc = pow(colour, vec3(1.0 / 2.2));
|
||||||
colour = sqrt(colour);
|
colour = mix(colour, enc, uGamma);
|
||||||
#else
|
}
|
||||||
colour = pow(colour, vec3(1.0 / OUTPUT_GAMMA));
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
// Viñeta
|
||||||
if (uVignette > 0.0) {
|
if (uVignette > 0.0) {
|
||||||
vec2 uv = texcoord - vec2(0.5);
|
vec2 uv = texcoord - vec2(0.5);
|
||||||
float vig = 1.0 - dot(uv, uv) * uVignette * 4.0;
|
float vig = 1.0 - dot(uv, uv) * uVignette * 4.0;
|
||||||
colour *= clamp(vig, 0.0, 1.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);
|
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
|
// Configuración
|
||||||
#define SCANLINES
|
#define SCANLINES
|
||||||
#define MULTISAMPLE
|
#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_WEIGHT 6.0
|
||||||
#define SCANLINE_GAP_BRIGHTNESS 0.12
|
#define SCANLINE_GAP_BRIGHTNESS 0.12
|
||||||
#define BLOOM_FACTOR 3.5
|
#define BLOOM_FACTOR 3.5
|
||||||
#define INPUT_GAMMA 2.4
|
|
||||||
#define OUTPUT_GAMMA 2.2
|
|
||||||
|
|
||||||
// Inputs desde vertex shader
|
// Inputs desde vertex shader
|
||||||
in vec2 vTexCoord;
|
in vec2 vTexCoord;
|
||||||
in float vFilterWidth;
|
in float vFilterWidth;
|
||||||
#if defined(CURVATURE)
|
|
||||||
in vec2 vScreenScale;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Output
|
// Output
|
||||||
out vec4 FragColor;
|
out vec4 FragColor;
|
||||||
|
|
||||||
// Uniforms
|
// Uniforms existentes
|
||||||
uniform sampler2D Texture;
|
uniform sampler2D Texture;
|
||||||
uniform vec2 TextureSize;
|
uniform vec2 TextureSize;
|
||||||
uniform float uVignette; // 0 = sin viñeta, 1 = máxima
|
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 uChroma; // 0 = sin aberración, 1 = máxima
|
||||||
uniform float uOutputHeight; // altura del viewport en pixels de pantalla
|
uniform float uOutputHeight; // altura del viewport en pixels de pantalla
|
||||||
|
|
||||||
#if defined(CURVATURE)
|
// Nuevos uniforms
|
||||||
vec2 Distort(vec2 coord)
|
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
|
||||||
vec2 CURVATURE_DISTORTION = vec2(CURVATURE_X, CURVATURE_Y);
|
uniform float uCurvature; // 0 = plana, 1 = curvatura barrel máxima
|
||||||
vec2 barrelScale = vec2(1.0) - (0.23 * CURVATURE_DISTORTION);
|
uniform float uBleeding; // 0 = sin sangrado NTSC, 1 = máximo
|
||||||
coord *= vScreenScale;
|
|
||||||
coord -= vec2(0.5);
|
// Conversores YCbCr para efecto NTSC
|
||||||
float rsq = coord.x * coord.x + coord.y * coord.y;
|
vec3 rgb_to_ycc(vec3 rgb) {
|
||||||
coord += coord * (CURVATURE_DISTORTION * rsq);
|
return vec3(
|
||||||
coord *= barrelScale;
|
0.299*rgb.r + 0.587*rgb.g + 0.114*rgb.b,
|
||||||
if (abs(coord.x) >= 0.5 || abs(coord.y) >= 0.5)
|
-0.169*rgb.r - 0.331*rgb.g + 0.500*rgb.b + 0.5,
|
||||||
coord = vec2(-1.0);
|
0.500*rgb.r - 0.419*rgb.g - 0.081*rgb.b + 0.5
|
||||||
else
|
);
|
||||||
{
|
}
|
||||||
coord += vec2(0.5);
|
vec3 ycc_to_rgb(vec3 ycc) {
|
||||||
coord /= vScreenScale;
|
float y = ycc.x;
|
||||||
}
|
float cb = ycc.y - 0.5;
|
||||||
return coord;
|
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)
|
float CalcScanLineWeight(float dist)
|
||||||
{
|
{
|
||||||
return max(1.0 - dist * dist * SCANLINE_WEIGHT, SCANLINE_GAP_BRIGHTNESS);
|
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()
|
void main()
|
||||||
{
|
{
|
||||||
#if defined(CURVATURE)
|
|
||||||
vec2 texcoord = Distort(vTexCoord);
|
|
||||||
if (texcoord.x < 0.0) {
|
|
||||||
FragColor = vec4(0.0);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
vec2 texcoord = vTexCoord;
|
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;
|
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 tempY = floor(texcoordInPixels.y) + 0.5;
|
||||||
float yCoord = tempY / TextureSize.y;
|
float yCoord = tempY / TextureSize.y;
|
||||||
|
|
||||||
// Scanline en espacio de pantalla (subpíxel)
|
// Scanline en espacio de pantalla (subpíxel)
|
||||||
float scaleY = uOutputHeight / TextureSize.y;
|
float scaleY = uOutputHeight / TextureSize.y;
|
||||||
float screenY = vTexCoord.y * uOutputHeight;
|
float screenY = texcoord.y * uOutputHeight;
|
||||||
float posInRow = mod(screenY, scaleY);
|
float posInRow = mod(screenY, scaleY);
|
||||||
float scanLineDY = posInRow / scaleY - 0.5;
|
float scanLineDY = posInRow / scaleY - 0.5;
|
||||||
float localFilterWidth = 1.0 / scaleY;
|
float localFilterWidth = 1.0 / scaleY;
|
||||||
@@ -119,7 +91,7 @@ void main()
|
|||||||
scanLineWeight += CalcScanLineWeight(scanLineDY + localFilterWidth);
|
scanLineWeight += CalcScanLineWeight(scanLineDY + localFilterWidth);
|
||||||
scanLineWeight *= 0.3333333;
|
scanLineWeight *= 0.3333333;
|
||||||
|
|
||||||
// Phosphor blur en espacio textura (sin cambios)
|
// Phosphor blur en espacio textura
|
||||||
float dy = texcoordInPixels.y - tempY;
|
float dy = texcoordInPixels.y - tempY;
|
||||||
float signY = sign(dy);
|
float signY = sign(dy);
|
||||||
dy = dy * dy;
|
dy = dy * dy;
|
||||||
@@ -128,59 +100,64 @@ void main()
|
|||||||
dy /= TextureSize.y;
|
dy /= TextureSize.y;
|
||||||
dy *= signY;
|
dy *= signY;
|
||||||
vec2 tc = vec2(texcoord.x, yCoord + dy);
|
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;
|
vec3 colour;
|
||||||
colour.r = texture(Texture, tc + vec2(ca, 0.0)).r;
|
if (uBleeding > 0.0) {
|
||||||
colour.g = texture(Texture, tc).g;
|
float tw = TextureSize.x;
|
||||||
colour.b = texture(Texture, tc - vec2(ca, 0.0)).b;
|
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)
|
// Aberración cromática
|
||||||
#if defined(GAMMA)
|
float ca = uChroma * 0.005;
|
||||||
#if defined(FAKE_GAMMA)
|
colour.r = texture(Texture, tc + vec2(ca, 0.0)).r;
|
||||||
colour = colour * colour;
|
colour.b = texture(Texture, tc - vec2(ca, 0.0)).b;
|
||||||
#else
|
|
||||||
colour = pow(colour, vec3(INPUT_GAMMA));
|
// Corrección gamma (linealizar antes de scanlines, codificar después)
|
||||||
#endif
|
if (uGamma > 0.0) {
|
||||||
#endif
|
vec3 lin = pow(colour, vec3(2.4));
|
||||||
|
colour = mix(colour, lin, uGamma);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scanlines
|
||||||
scanLineWeight *= BLOOM_FACTOR;
|
scanLineWeight *= BLOOM_FACTOR;
|
||||||
colour *= mix(1.0, scanLineWeight, uScanlines);
|
colour *= mix(1.0, scanLineWeight, uScanlines);
|
||||||
|
|
||||||
#if defined(GAMMA)
|
if (uGamma > 0.0) {
|
||||||
#if defined(FAKE_GAMMA)
|
vec3 enc = pow(colour, vec3(1.0 / 2.2));
|
||||||
colour = sqrt(colour);
|
colour = mix(colour, enc, uGamma);
|
||||||
#else
|
}
|
||||||
colour = pow(colour, vec3(1.0 / OUTPUT_GAMMA));
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
// Viñeta
|
||||||
if (uVignette > 0.0) {
|
if (uVignette > 0.0) {
|
||||||
vec2 uv = texcoord - vec2(0.5);
|
vec2 uv = texcoord - vec2(0.5);
|
||||||
float vig = 1.0 - dot(uv, uv) * uVignette * 4.0;
|
float vig = 1.0 - dot(uv, uv) * uVignette * 4.0;
|
||||||
colour *= clamp(vig, 0.0, 1.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);
|
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];
|
||||||
|
}
|
||||||
@@ -342,9 +342,17 @@ auto OpenGLShader::init(SDL_Window* window,
|
|||||||
scanlines_location_ = glGetUniformLocation(program_id_, "uScanlines");
|
scanlines_location_ = glGetUniformLocation(program_id_, "uScanlines");
|
||||||
chroma_location_ = glGetUniformLocation(program_id_, "uChroma");
|
chroma_location_ = glGetUniformLocation(program_id_, "uChroma");
|
||||||
output_height_location_ = glGetUniformLocation(program_id_, "uOutputHeight");
|
output_height_location_ = glGetUniformLocation(program_id_, "uOutputHeight");
|
||||||
if (vignette_location_ != -1) { glUniform1f(vignette_location_, postfx_vignette_); }
|
mask_location_ = glGetUniformLocation(program_id_, "uMask");
|
||||||
if (scanlines_location_ != -1) { glUniform1f(scanlines_location_, postfx_scanlines_); }
|
gamma_location_ = glGetUniformLocation(program_id_, "uGamma");
|
||||||
if (chroma_location_ != -1) { glUniform1f(chroma_location_, postfx_chroma_); }
|
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);
|
glUseProgram(0);
|
||||||
|
|
||||||
is_initialized_ = true;
|
is_initialized_ = true;
|
||||||
@@ -400,9 +408,13 @@ void OpenGLShader::render() {
|
|||||||
checkGLError("glUseProgram");
|
checkGLError("glUseProgram");
|
||||||
|
|
||||||
// Pasar uniforms PostFX
|
// Pasar uniforms PostFX
|
||||||
if (vignette_location_ != -1) { glUniform1f(vignette_location_, postfx_vignette_); }
|
if (vignette_location_ != -1) { glUniform1f(vignette_location_, postfx_vignette_); }
|
||||||
if (scanlines_location_ != -1) { glUniform1f(scanlines_location_, postfx_scanlines_); }
|
if (scanlines_location_ != -1) { glUniform1f(scanlines_location_, postfx_scanlines_); }
|
||||||
if (chroma_location_ != -1) { glUniform1f(chroma_location_, postfx_chroma_); }
|
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)
|
// Configurar viewport (obtener tamaño lógico de SDL)
|
||||||
int logical_w;
|
int logical_w;
|
||||||
@@ -469,10 +481,14 @@ void OpenGLShader::render() {
|
|||||||
glViewport(old_viewport[0], old_viewport[1], old_viewport[2], old_viewport[3]);
|
glViewport(old_viewport[0], old_viewport[1], old_viewport[2], old_viewport[3]);
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenGLShader::setPostFXParams(float vignette, float scanlines, float chroma) {
|
void OpenGLShader::setPostFXParams(const PostFXParams& p) {
|
||||||
postfx_vignette_ = vignette;
|
postfx_vignette_ = p.vignette;
|
||||||
postfx_scanlines_ = scanlines;
|
postfx_scanlines_ = p.scanlines;
|
||||||
postfx_chroma_ = chroma;
|
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) {
|
void OpenGLShader::setTextureSize(float width, float height) {
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ class OpenGLShader : public ShaderBackend {
|
|||||||
|
|
||||||
void render() override;
|
void render() override;
|
||||||
void setTextureSize(float width, float height) override;
|
void setTextureSize(float width, float height) override;
|
||||||
void setPostFXParams(float vignette, float scanlines, float chroma) override;
|
void setPostFXParams(const PostFXParams& p) override;
|
||||||
void cleanup() final;
|
void cleanup() final;
|
||||||
[[nodiscard]] auto isHardwareAccelerated() const -> bool override { return is_initialized_; }
|
[[nodiscard]] auto isHardwareAccelerated() const -> bool override { return is_initialized_; }
|
||||||
|
|
||||||
@@ -60,11 +60,19 @@ class OpenGLShader : public ShaderBackend {
|
|||||||
GLint scanlines_location_ = -1;
|
GLint scanlines_location_ = -1;
|
||||||
GLint chroma_location_ = -1;
|
GLint chroma_location_ = -1;
|
||||||
GLint output_height_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
|
// Valores cacheados de PostFX
|
||||||
float postfx_vignette_ = 0.6F;
|
float postfx_vignette_ = 0.6F;
|
||||||
float postfx_scanlines_ = 0.7F;
|
float postfx_scanlines_ = 0.7F;
|
||||||
float postfx_chroma_ = 0.15F;
|
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
|
// Tamaños
|
||||||
int window_width_ = 0;
|
int window_width_ = 0;
|
||||||
|
|||||||
@@ -482,7 +482,9 @@ void Screen::loadShaders() {
|
|||||||
void Screen::applyCurrentPostFXPreset() {
|
void Screen::applyCurrentPostFXPreset() {
|
||||||
if (shader_backend_ && !Options::postfx_presets.empty()) {
|
if (shader_backend_ && !Options::postfx_presets.empty()) {
|
||||||
const auto& p = Options::postfx_presets[static_cast<size_t>(Options::current_postfx_preset)];
|
const auto& p = Options::postfx_presets[static_cast<size_t>(Options::current_postfx_preset)];
|
||||||
shader_backend_->setPostFXParams(p.vignette, p.scanlines, p.chroma);
|
Rendering::PostFXParams params{p.vignette, p.scanlines, p.chroma,
|
||||||
|
p.mask, p.gamma, p.curvature, p.bleeding};
|
||||||
|
shader_backend_->setPostFXParams(params);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -48,32 +48,110 @@ struct PostFXUniforms {
|
|||||||
float chroma_strength;
|
float chroma_strength;
|
||||||
float scanline_strength;
|
float scanline_strength;
|
||||||
float screen_height;
|
float screen_height;
|
||||||
|
float mask_strength;
|
||||||
|
float gamma_strength;
|
||||||
|
float curvature;
|
||||||
|
float bleeding;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// YCbCr helpers for NTSC bleeding
|
||||||
|
static float3 rgb_to_ycc(float3 rgb) {
|
||||||
|
return float3(
|
||||||
|
0.299f*rgb.r + 0.587f*rgb.g + 0.114f*rgb.b,
|
||||||
|
-0.169f*rgb.r - 0.331f*rgb.g + 0.500f*rgb.b + 0.5f,
|
||||||
|
0.500f*rgb.r - 0.419f*rgb.g - 0.081f*rgb.b + 0.5f
|
||||||
|
);
|
||||||
|
}
|
||||||
|
static float3 ycc_to_rgb(float3 ycc) {
|
||||||
|
float y = ycc.x;
|
||||||
|
float cb = ycc.y - 0.5f;
|
||||||
|
float cr = ycc.z - 0.5f;
|
||||||
|
return clamp(float3(
|
||||||
|
y + 1.402f*cr,
|
||||||
|
y - 0.344f*cb - 0.714f*cr,
|
||||||
|
y + 1.772f*cb
|
||||||
|
), 0.0f, 1.0f);
|
||||||
|
}
|
||||||
|
|
||||||
fragment float4 postfx_fs(PostVOut in [[stage_in]],
|
fragment float4 postfx_fs(PostVOut in [[stage_in]],
|
||||||
texture2d<float> scene [[texture(0)]],
|
texture2d<float> scene [[texture(0)]],
|
||||||
sampler samp [[sampler(0)]],
|
sampler samp [[sampler(0)]],
|
||||||
constant PostFXUniforms& u [[buffer(0)]]) {
|
constant PostFXUniforms& u [[buffer(0)]]) {
|
||||||
float ca = u.chroma_strength * 0.005;
|
float2 uv = in.uv;
|
||||||
float4 color;
|
|
||||||
color.r = scene.sample(samp, in.uv + float2( ca, 0.0)).r;
|
|
||||||
color.g = scene.sample(samp, in.uv).g;
|
|
||||||
color.b = scene.sample(samp, in.uv - float2( ca, 0.0)).b;
|
|
||||||
color.a = scene.sample(samp, in.uv).a;
|
|
||||||
|
|
||||||
|
// Curvatura barrel CRT
|
||||||
|
if (u.curvature > 0.0f) {
|
||||||
|
float2 c = uv - 0.5f;
|
||||||
|
float rsq = dot(c, c);
|
||||||
|
float2 dist = float2(0.05f, 0.1f) * u.curvature;
|
||||||
|
float2 barrelScale = 1.0f - 0.23f * dist;
|
||||||
|
c += c * (dist * rsq);
|
||||||
|
c *= barrelScale;
|
||||||
|
if (abs(c.x) >= 0.5f || abs(c.y) >= 0.5f) {
|
||||||
|
return float4(0.0f, 0.0f, 0.0f, 1.0f);
|
||||||
|
}
|
||||||
|
uv = c + 0.5f;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Muestra base
|
||||||
|
float3 base = scene.sample(samp, uv).rgb;
|
||||||
|
|
||||||
|
// Sangrado NTSC — difuminado horizontal de crominancia
|
||||||
|
float3 colour;
|
||||||
|
if (u.bleeding > 0.0f) {
|
||||||
|
float tw = float(scene.get_width());
|
||||||
|
float3 ycc = rgb_to_ycc(base);
|
||||||
|
float3 ycc_l2 = rgb_to_ycc(scene.sample(samp, uv - float2(2.0f/tw, 0.0f)).rgb);
|
||||||
|
float3 ycc_l1 = rgb_to_ycc(scene.sample(samp, uv - float2(1.0f/tw, 0.0f)).rgb);
|
||||||
|
float3 ycc_r1 = rgb_to_ycc(scene.sample(samp, uv + float2(1.0f/tw, 0.0f)).rgb);
|
||||||
|
float3 ycc_r2 = rgb_to_ycc(scene.sample(samp, uv + float2(2.0f/tw, 0.0f)).rgb);
|
||||||
|
ycc.yz = (ycc_l2.yz + ycc_l1.yz*2.0f + ycc.yz*2.0f + ycc_r1.yz*2.0f + ycc_r2.yz) / 8.0f;
|
||||||
|
colour = mix(base, ycc_to_rgb(ycc), u.bleeding);
|
||||||
|
} else {
|
||||||
|
colour = base;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Aberración cromática
|
||||||
|
float ca = u.chroma_strength * 0.005f;
|
||||||
|
colour.r = scene.sample(samp, uv + float2(ca, 0.0f)).r;
|
||||||
|
colour.b = scene.sample(samp, uv - float2(ca, 0.0f)).b;
|
||||||
|
|
||||||
|
// Corrección gamma (linealizar antes de scanlines, codificar después)
|
||||||
|
if (u.gamma_strength > 0.0f) {
|
||||||
|
float3 lin = pow(colour, float3(2.4f));
|
||||||
|
colour = mix(colour, lin, u.gamma_strength);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scanlines
|
||||||
float texHeight = float(scene.get_height());
|
float texHeight = float(scene.get_height());
|
||||||
float scaleY = u.screen_height / texHeight;
|
float scaleY = u.screen_height / texHeight;
|
||||||
float screenY = in.uv.y * u.screen_height;
|
float screenY = uv.y * u.screen_height;
|
||||||
float posInRow = fmod(screenY, scaleY);
|
float posInRow = fmod(screenY, scaleY);
|
||||||
float scanLineDY = posInRow / scaleY - 0.5;
|
float scanLineDY = posInRow / scaleY - 0.5f;
|
||||||
float scan = max(1.0 - scanLineDY * scanLineDY * 6.0, 0.12) * 3.5;
|
float scan = max(1.0f - scanLineDY * scanLineDY * 6.0f, 0.12f) * 3.5f;
|
||||||
color.rgb *= mix(1.0, scan, u.scanline_strength);
|
colour *= mix(1.0f, scan, u.scanline_strength);
|
||||||
|
|
||||||
float2 d = in.uv - float2(0.5, 0.5);
|
if (u.gamma_strength > 0.0f) {
|
||||||
float vignette = 1.0 - dot(d, d) * u.vignette_strength;
|
float3 enc = pow(colour, float3(1.0f/2.2f));
|
||||||
color.rgb *= clamp(vignette, 0.0, 1.0);
|
colour = mix(colour, enc, u.gamma_strength);
|
||||||
|
}
|
||||||
|
|
||||||
return color;
|
// Viñeta
|
||||||
|
float2 d = uv - 0.5f;
|
||||||
|
float vignette = 1.0f - dot(d, d) * u.vignette_strength;
|
||||||
|
colour *= clamp(vignette, 0.0f, 1.0f);
|
||||||
|
|
||||||
|
// Máscara de fósforo RGB
|
||||||
|
if (u.mask_strength > 0.0f) {
|
||||||
|
float whichMask = fract(in.pos.x * 0.3333333f);
|
||||||
|
float3 mask = float3(0.80f);
|
||||||
|
if (whichMask < 0.3333333f) mask.x = 1.0f;
|
||||||
|
else if (whichMask < 0.6666667f) mask.y = 1.0f;
|
||||||
|
else mask.z = 1.0f;
|
||||||
|
colour = mix(colour, colour * mask, u.mask_strength);
|
||||||
|
}
|
||||||
|
|
||||||
|
return float4(colour, 1.0f);
|
||||||
}
|
}
|
||||||
)";
|
)";
|
||||||
// NOLINTEND(readability-identifier-naming)
|
// NOLINTEND(readability-identifier-naming)
|
||||||
@@ -423,10 +501,14 @@ auto SDL3GPUShader::createShaderSPIRV(SDL_GPUDevice* device,
|
|||||||
return shader;
|
return shader;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SDL3GPUShader::setPostFXParams(float vignette, float scanlines, float chroma) {
|
void SDL3GPUShader::setPostFXParams(const PostFXParams& p) {
|
||||||
uniforms_.vignette_strength = vignette;
|
uniforms_.vignette_strength = p.vignette;
|
||||||
uniforms_.scanline_strength = scanlines;
|
uniforms_.scanline_strength = p.scanlines;
|
||||||
uniforms_.chroma_strength = chroma;
|
uniforms_.chroma_strength = p.chroma;
|
||||||
|
uniforms_.mask_strength = p.mask;
|
||||||
|
uniforms_.gamma_strength = p.gamma;
|
||||||
|
uniforms_.curvature = p.curvature;
|
||||||
|
uniforms_.bleeding = p.bleeding;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Rendering
|
} // namespace Rendering
|
||||||
|
|||||||
@@ -7,11 +7,16 @@
|
|||||||
|
|
||||||
// PostFX uniforms pushed to fragment stage each frame.
|
// PostFX uniforms pushed to fragment stage each frame.
|
||||||
// Must match the MSL struct and GLSL uniform block layout.
|
// Must match the MSL struct and GLSL uniform block layout.
|
||||||
|
// 8 floats = 32 bytes — meets Metal/Vulkan 16-byte alignment requirement.
|
||||||
struct PostFXUniforms {
|
struct PostFXUniforms {
|
||||||
float vignette_strength; // 0 = none, ~0.8 = subtle
|
float vignette_strength; // 0 = none, ~0.8 = subtle
|
||||||
float chroma_strength; // 0 = off, ~0.2 = subtle chromatic aberration
|
float chroma_strength; // 0 = off, ~0.2 = subtle chromatic aberration
|
||||||
float scanline_strength; // 0 = off, 1 = full
|
float scanline_strength; // 0 = off, 1 = full
|
||||||
float screen_height; // logical height in pixels (for resolution-independent scanlines)
|
float screen_height; // logical height in pixels (for resolution-independent scanlines)
|
||||||
|
float mask_strength; // 0 = off, 1 = full phosphor dot mask
|
||||||
|
float gamma_strength; // 0 = off, 1 = full gamma 2.4/2.2 correction
|
||||||
|
float curvature; // 0 = flat, 1 = max barrel distortion
|
||||||
|
float bleeding; // 0 = off, 1 = max NTSC chrominance bleeding
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace Rendering {
|
namespace Rendering {
|
||||||
@@ -42,7 +47,7 @@ class SDL3GPUShader : public ShaderBackend {
|
|||||||
void uploadPixels(const Uint32* pixels, int width, int height) override;
|
void uploadPixels(const Uint32* pixels, int width, int height) override;
|
||||||
|
|
||||||
// Actualiza los parámetros de intensidad de los efectos PostFX
|
// Actualiza los parámetros de intensidad de los efectos PostFX
|
||||||
void setPostFXParams(float vignette, float scanlines, float chroma) override;
|
void setPostFXParams(const PostFXParams& p) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static auto createShaderMSL(SDL_GPUDevice* device,
|
static auto createShaderMSL(SDL_GPUDevice* device,
|
||||||
|
|||||||
@@ -6,6 +6,20 @@
|
|||||||
|
|
||||||
namespace Rendering {
|
namespace Rendering {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Parámetros de intensidad de los efectos PostFX
|
||||||
|
* Definido a nivel de namespace para facilitar el uso desde subclases y screen.cpp
|
||||||
|
*/
|
||||||
|
struct PostFXParams {
|
||||||
|
float vignette = 0.0F; // Intensidad de la viñeta
|
||||||
|
float scanlines = 0.0F; // Intensidad de las scanlines
|
||||||
|
float chroma = 0.0F; // Aberración cromática
|
||||||
|
float mask = 0.0F; // Máscara de fósforo RGB
|
||||||
|
float gamma = 0.0F; // Corrección gamma (blend 0=off, 1=full)
|
||||||
|
float curvature = 0.0F; // Curvatura barrel CRT
|
||||||
|
float bleeding = 0.0F; // Sangrado de color NTSC
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Interfaz abstracta para backends de renderizado con shaders
|
* @brief Interfaz abstracta para backends de renderizado con shaders
|
||||||
*
|
*
|
||||||
@@ -54,11 +68,9 @@ class ShaderBackend {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Establece los parámetros de intensidad de los efectos PostFX
|
* @brief Establece los parámetros de intensidad de los efectos PostFX
|
||||||
* @param vignette Intensidad de la viñeta (0.0 = ninguna, 1.0 = máxima)
|
* @param p Struct con todos los parámetros PostFX
|
||||||
* @param scanlines Intensidad de las scanlines (0.0 = desactivadas, 1.0 = máximas)
|
|
||||||
* @param chroma Intensidad de la aberración cromática (0.0 = ninguna, 1.0 = máxima)
|
|
||||||
*/
|
*/
|
||||||
virtual void setPostFXParams(float /*vignette*/, float /*scanlines*/, float /*chroma*/) {}
|
virtual void setPostFXParams(const PostFXParams& /*p*/) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Verifica si el backend está usando aceleración por hardware
|
* @brief Verifica si el backend está usando aceleración por hardware
|
||||||
|
|||||||
@@ -690,6 +690,18 @@ auto loadPostFXFromFile() -> bool {
|
|||||||
if (p.contains("chroma")) {
|
if (p.contains("chroma")) {
|
||||||
try { preset.chroma = p["chroma"].get_value<float>(); } catch (...) {}
|
try { preset.chroma = p["chroma"].get_value<float>(); } catch (...) {}
|
||||||
}
|
}
|
||||||
|
if (p.contains("mask")) {
|
||||||
|
try { preset.mask = p["mask"].get_value<float>(); } catch (...) {}
|
||||||
|
}
|
||||||
|
if (p.contains("gamma")) {
|
||||||
|
try { preset.gamma = p["gamma"].get_value<float>(); } catch (...) {}
|
||||||
|
}
|
||||||
|
if (p.contains("curvature")) {
|
||||||
|
try { preset.curvature = p["curvature"].get_value<float>(); } catch (...) {}
|
||||||
|
}
|
||||||
|
if (p.contains("bleeding")) {
|
||||||
|
try { preset.bleeding = p["bleeding"].get_value<float>(); } catch (...) {}
|
||||||
|
}
|
||||||
postfx_presets.push_back(preset);
|
postfx_presets.push_back(preset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -727,20 +739,52 @@ auto savePostFXToFile() -> bool {
|
|||||||
file << "# vignette: screen darkening at the edges\n";
|
file << "# vignette: screen darkening at the edges\n";
|
||||||
file << "# scanlines: horizontal scanline effect\n";
|
file << "# scanlines: horizontal scanline effect\n";
|
||||||
file << "# chroma: chromatic aberration (RGB color fringing)\n";
|
file << "# chroma: chromatic aberration (RGB color fringing)\n";
|
||||||
|
file << "# mask: phosphor dot mask (RGB subpixel pattern)\n";
|
||||||
|
file << "# gamma: gamma correction input 2.4 / output 2.2\n";
|
||||||
|
file << "# curvature: CRT barrel distortion\n";
|
||||||
|
file << "# bleeding: NTSC horizontal colour bleeding\n";
|
||||||
file << "\n";
|
file << "\n";
|
||||||
file << "presets:\n";
|
file << "presets:\n";
|
||||||
file << " - name: \"CRT\"\n";
|
file << " - name: \"CRT\"\n";
|
||||||
file << " vignette: 0.6\n";
|
file << " vignette: 0.6\n";
|
||||||
file << " scanlines: 0.7\n";
|
file << " scanlines: 0.7\n";
|
||||||
file << " chroma: 0.15\n";
|
file << " chroma: 0.15\n";
|
||||||
|
file << " mask: 0.6\n";
|
||||||
|
file << " gamma: 0.8\n";
|
||||||
|
file << " curvature: 0.0\n";
|
||||||
|
file << " bleeding: 0.0\n";
|
||||||
|
file << " - name: \"NTSC\"\n";
|
||||||
|
file << " vignette: 0.4\n";
|
||||||
|
file << " scanlines: 0.5\n";
|
||||||
|
file << " chroma: 0.2\n";
|
||||||
|
file << " mask: 0.4\n";
|
||||||
|
file << " gamma: 0.5\n";
|
||||||
|
file << " curvature: 0.0\n";
|
||||||
|
file << " bleeding: 0.6\n";
|
||||||
|
file << " - name: \"CURVED\"\n";
|
||||||
|
file << " vignette: 0.5\n";
|
||||||
|
file << " scanlines: 0.6\n";
|
||||||
|
file << " chroma: 0.1\n";
|
||||||
|
file << " mask: 0.5\n";
|
||||||
|
file << " gamma: 0.7\n";
|
||||||
|
file << " curvature: 0.8\n";
|
||||||
|
file << " bleeding: 0.0\n";
|
||||||
file << " - name: \"SCANLINES\"\n";
|
file << " - name: \"SCANLINES\"\n";
|
||||||
file << " vignette: 0.0\n";
|
file << " vignette: 0.0\n";
|
||||||
file << " scanlines: 0.8\n";
|
file << " scanlines: 0.8\n";
|
||||||
file << " chroma: 0.0\n";
|
file << " chroma: 0.0\n";
|
||||||
|
file << " mask: 0.0\n";
|
||||||
|
file << " gamma: 0.0\n";
|
||||||
|
file << " curvature: 0.0\n";
|
||||||
|
file << " bleeding: 0.0\n";
|
||||||
file << " - name: \"SUBTLE\"\n";
|
file << " - name: \"SUBTLE\"\n";
|
||||||
file << " vignette: 0.3\n";
|
file << " vignette: 0.3\n";
|
||||||
file << " scanlines: 0.4\n";
|
file << " scanlines: 0.4\n";
|
||||||
file << " chroma: 0.05\n";
|
file << " chroma: 0.05\n";
|
||||||
|
file << " mask: 0.0\n";
|
||||||
|
file << " gamma: 0.3\n";
|
||||||
|
file << " curvature: 0.0\n";
|
||||||
|
file << " bleeding: 0.0\n";
|
||||||
|
|
||||||
file.close();
|
file.close();
|
||||||
|
|
||||||
@@ -750,9 +794,11 @@ auto savePostFXToFile() -> bool {
|
|||||||
|
|
||||||
// Cargar los presets recién creados
|
// Cargar los presets recién creados
|
||||||
postfx_presets.clear();
|
postfx_presets.clear();
|
||||||
postfx_presets.push_back({"CRT", 0.6F, 0.7F, 0.15F});
|
postfx_presets.push_back({"CRT", 0.6F, 0.7F, 0.15F, 0.6F, 0.8F, 0.0F, 0.0F});
|
||||||
postfx_presets.push_back({"SCANLINES", 0.0F, 0.8F, 0.0F});
|
postfx_presets.push_back({"NTSC", 0.4F, 0.5F, 0.2F, 0.4F, 0.5F, 0.0F, 0.6F});
|
||||||
postfx_presets.push_back({"SUBTLE", 0.3F, 0.4F, 0.05F});
|
postfx_presets.push_back({"CURVED", 0.5F, 0.6F, 0.1F, 0.5F, 0.7F, 0.8F, 0.0F});
|
||||||
|
postfx_presets.push_back({"SCANLINES",0.0F, 0.8F, 0.0F, 0.0F, 0.0F, 0.0F, 0.0F});
|
||||||
|
postfx_presets.push_back({"SUBTLE", 0.3F, 0.4F, 0.05F, 0.0F, 0.3F, 0.0F, 0.0F});
|
||||||
current_postfx_preset = 0;
|
current_postfx_preset = 0;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -120,6 +120,10 @@ struct PostFXPreset {
|
|||||||
float vignette{0.6F}; // Intensidad de la viñeta (0.0 = ninguna, 1.0 = máxima)
|
float vignette{0.6F}; // Intensidad de la viñeta (0.0 = ninguna, 1.0 = máxima)
|
||||||
float scanlines{0.7F}; // Intensidad de las scanlines (0.0 = desactivadas, 1.0 = máximas)
|
float scanlines{0.7F}; // Intensidad de las scanlines (0.0 = desactivadas, 1.0 = máximas)
|
||||||
float chroma{0.15F}; // Intensidad de la aberración cromática (0.0 = ninguna, 1.0 = máxima)
|
float chroma{0.15F}; // Intensidad de la aberración cromática (0.0 = ninguna, 1.0 = máxima)
|
||||||
|
float mask{0.0F}; // Intensidad de la máscara de fósforo RGB (0.0 = desactivada, 1.0 = máxima)
|
||||||
|
float gamma{0.0F}; // Corrección gamma input 2.4 / output 2.2 (0.0 = off, 1.0 = plena)
|
||||||
|
float curvature{0.0F}; // Distorsión barrel CRT (0.0 = plana, 1.0 = máxima curvatura)
|
||||||
|
float bleeding{0.0F}; // Sangrado de color NTSC horizontal Y/C (0.0 = off, 1.0 = máximo)
|
||||||
};
|
};
|
||||||
|
|
||||||
// --- Variables globales ---
|
// --- Variables globales ---
|
||||||
|
|||||||
46
tools/shaders/compile_spirv.sh
Executable file
46
tools/shaders/compile_spirv.sh
Executable file
@@ -0,0 +1,46 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Compile Vulkan GLSL shaders to SPIR-V and update the C++ headers used by SDL3GPUShader.
|
||||||
|
# Required: glslc (from Vulkan SDK or: brew install glslang / apt install glslang-tools)
|
||||||
|
#
|
||||||
|
# Run from the project root: tools/shaders/compile_spirv.sh
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
SHADERS_DIR="data/shaders"
|
||||||
|
HEADERS_DIR="source/core/rendering/sdl3gpu"
|
||||||
|
|
||||||
|
if ! command -v glslc &> /dev/null; then
|
||||||
|
echo "ERROR: glslc not found. Install Vulkan SDK or run:"
|
||||||
|
echo " macOS: brew install glslang"
|
||||||
|
echo " Linux: sudo apt install glslang-tools"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Compiling SPIR-V shaders..."
|
||||||
|
|
||||||
|
glslc "${SHADERS_DIR}/postfx.vert" -o /tmp/postfx.vert.spv
|
||||||
|
glslc "${SHADERS_DIR}/postfx.frag" -o /tmp/postfx.frag.spv
|
||||||
|
|
||||||
|
echo "Generating C++ headers..."
|
||||||
|
|
||||||
|
xxd -i /tmp/postfx.vert.spv | \
|
||||||
|
sed 's/unsigned char __tmp_postfx_vert_spv\[\]/static const uint8_t kpostfx_vert_spv[]/' | \
|
||||||
|
sed 's/unsigned int __tmp_postfx_vert_spv_len/static const size_t kpostfx_vert_spv_size/' | \
|
||||||
|
sed 's/= [0-9]*/\0/' \
|
||||||
|
> "${HEADERS_DIR}/postfx_vert_spv.h"
|
||||||
|
|
||||||
|
xxd -i /tmp/postfx.frag.spv | \
|
||||||
|
sed 's/unsigned char __tmp_postfx_frag_spv\[\]/static const uint8_t kpostfx_frag_spv[]/' | \
|
||||||
|
sed 's/unsigned int __tmp_postfx_frag_spv_len/static const size_t kpostfx_frag_spv_size/' | \
|
||||||
|
sed 's/= [0-9]*/\0/' \
|
||||||
|
> "${HEADERS_DIR}/postfx_frag_spv.h"
|
||||||
|
|
||||||
|
# Prepend required includes to the headers
|
||||||
|
for f in "${HEADERS_DIR}/postfx_vert_spv.h" "${HEADERS_DIR}/postfx_frag_spv.h"; do
|
||||||
|
echo -e "#pragma once\n#include <cstdint>\n#include <cstddef>\n$(cat "$f")" > "$f"
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "Done. Headers updated in ${HEADERS_DIR}/"
|
||||||
|
echo " postfx_vert_spv.h"
|
||||||
|
echo " postfx_frag_spv.h"
|
||||||
|
echo "Rebuild the project to use the new shaders."
|
||||||
Reference in New Issue
Block a user