I took the liberty to mod your ntsc shader to add some of my tests and suggestions, hopefully I didn’t make a mess. Also commented the color correction block to compare signal bandwidth only. I’m not sure if I want to add YCbCr to it. By the way I read that composite has a brightness roll off of 3 dB, I think that along the pedestal (black level) could be the origin of TV levels and PC levels. I think I should multiply it by 0.707.
#version 450
layout(push_constant) uniform Push
{
vec4 SourceSize;
vec4 OriginalSize;
vec4 OutputSize;
uint FrameCount;
float SPLIT;
float I_RES;
float Q_RES;
float I_SHIFT;
float Q_SHIFT;
float Y_MUL;
float I_MUL;
float Q_MUL;
} params;
// Suggestions:
// I: 1.3 Q: 0.4 (for FCC NTSC analogue standard -old-)
// I: 1.0 Q: 1.0 (for FCC NTSC standard 4:1:1)
// I: 2.0 Q: 2.0 (for FCC NTSC S-Video 4:2:2)
#pragma parameter SPLIT "Split" 0.0 -1.0 1.0 0.1
#pragma parameter I_RES "I Mhz" 1.3 0.4 4.0 0.05
#pragma parameter Q_RES "Q Mhz" 0.4 0.4 4.0 0.05
#pragma parameter I_SHIFT "I Shift" 0.0 -1.0 1.0 0.02
#pragma parameter Q_SHIFT "Q Shift" 0.0 -1.0 1.0 0.02
#pragma parameter Y_MUL "Y Multiplier" 1.0 0.0 2.0 0.1
#pragma parameter I_MUL "I Multiplier" 1.0 0.0 2.0 0.1
#pragma parameter Q_MUL "Q Multiplier" 1.0 0.0 2.0 0.1
layout(std140, set = 0, binding = 0) uniform UBO
{
mat4 MVP;
} global;
#pragma stage vertex
layout(location = 0) in vec4 Position;
layout(location = 1) in vec2 TexCoord;
layout(location = 0) out vec2 vTexCoord;
void main()
{
gl_Position = global.MVP * Position;
vTexCoord = TexCoord;
}
#pragma stage fragment
layout(location = 0) in vec2 vTexCoord;
layout(location = 0) out vec4 FragColor;
layout(set = 0, binding = 2) uniform sampler2D Source;
vec4 RGB_YIQ(vec4 col)
{
mat3 conv_mat = mat3(
0.299996928307425, 0.590001575542717, 0.110001496149858,
0.599002392519453, -0.277301256521204, -0.321701135998249,
0.213001700342824, -0.52510120528935, 0.312099504946526);
col.rgb *= conv_mat;
return col;
}
vec4 YIQ_RGB(vec4 col)
{
mat3 conv_mat = mat3(
1.0, 0.946882217090069, 0.623556581986143,
1.0, -0.274787646298978, -0.635691079187380,
1.0, -1.108545034642030, 1.709006928406470);
col.rgb *= conv_mat;
return col;
}
// to Studio Swing (in YIQ space) (for footroom and headroom)
vec4 PCtoTV(vec4 col)
{
col *= 255;
col.x = ((col.x * 219) / 255) + 16;
col.y = (((col.y - 128) * 224) / 255) + 112;
col.z = (((col.z - 128) * 224) / 255) + 112;
return vec4(col.xyz, 1.0) / 255;
}
// to Full Swing (in YIQ space)
vec4 TVtoPC(vec4 col)
{
col *= 255;
float colx = ((col.x - 16) / 219) * 255;
float coly = (((col.y - 112) / 224) * 255) + 128;
float colz = (((col.z - 112) / 224) * 255) + 128;
return vec4(colx,coly,colz, 1.0) / 255;
}
void main()
{
#define ms *pow(10.0, -9.0)
#define MHz *pow(10.0, 9.0);
const float max_col_res_I = (params.I_RES / 2.0) * 52.6 ms * 315.0/88.0 MHz;
const float max_col_res_Q = (params.Q_RES / 2.0) * 52.6 ms * 315.0/88.0 MHz;
const float max_lum_res = 52.6 ms * 315.0/88.0 MHz;
const int viewport_col_resy = int(ceil((params.OutputSize.x / params.OriginalSize.x) * (params.OriginalSize.x / max_col_res_I)));
const int viewport_col_resz = int(ceil((params.OutputSize.x / params.OriginalSize.x) * (params.OriginalSize.x / max_col_res_Q)));
const int viewport_lum_res = int(ceil((params.OutputSize.x / params.OriginalSize.x) * (params.OriginalSize.x / max_lum_res)));
if(vTexCoord.x - params.SPLIT - 1.0 > 0.0 || vTexCoord.x - params.SPLIT < 0.0)
{
FragColor = vec4(texture(Source, vTexCoord).rgb, 1.0);
}
else
{
vec4 col = vec4(0.0, 0.0, 0.0, 1.0);
col += RGB_YIQ(texture(Source, vTexCoord));
for(int i = 1; i < viewport_col_resy; i++)
{
col.y += RGB_YIQ(texture(Source, vTexCoord - vec2((i - viewport_col_resy/2) * params.OutputSize.z, 0.0))).y;
}
for(int i = 1; i < viewport_col_resz; i++)
{
col.z += RGB_YIQ(texture(Source, vTexCoord - vec2((i - viewport_col_resz/2) * params.OutputSize.z, 0.0))).z;
}
for(int i = 1; i < viewport_lum_res; i++)
{
col.x += RGB_YIQ(texture(Source, vTexCoord - vec2((i - viewport_col_resy/2) * params.OutputSize.z, 0.0))).x;
}
col.y /= viewport_col_resy;
col.z /= viewport_col_resz;
col.x /= viewport_lum_res;
col = PCtoTV(col);
// col.y = mod((col.y + 1.0) + params.I_SHIFT, 2.0) - 1.0;
// col.y = 0.9 * col.y + 0.1 * col.y * col.x;
//
// col.z = mod((col.z + 1.0) + params.Q_SHIFT, 2.0) - 1.0;
// col.z = 0.4 * col.z + 0.6 * col.z * col.x;
// col.x += 0.5*col.y;
//
// col.z *= params.Q_MUL;
// col.y *= params.I_MUL;
// col.x *= params.Y_MUL;
col = clamp(col,vec4(0.0,-0.5957,-0.5226,0.0),vec4(1.0,0.5957,0.5226,1.0));
FragColor = YIQ_RGB(TVtoPC(col));
}
}