Skip to content

Instantly share code, notes, and snippets.

@realmonster
Last active July 8, 2019 23:45
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save realmonster/b504593c970a45c0d1ba9ec2c8a13fcf to your computer and use it in GitHub Desktop.
Save realmonster/b504593c970a45c0d1ba9ec2c8a13fcf to your computer and use it in GitHub Desktop.
NOT Ideal NTSC composite cable CRT simulation for RetroArch
// NOT Ideal NTSC composite cable CRT simulation for RetroArch <- fix of title.
// by r57shell
// thanks to feos & HardWareMan
// ideal in terms of ideal encoding/decoding,
// also, without any passbands.
// also TV subpixels and scanlines
void main_vertex
(
float4 position : POSITION,
out float4 oPosition : POSITION,
uniform float4x4 modelViewProj,
float2 tex : TEXCOORD,
out float2 oTex : TEXCOORD
)
{
oPosition = mul(modelViewProj, position);
oTex = tex;
}
struct input
{
float2 video_size;
float2 texture_size;
float2 output_size;
float frame_count;
float frame_direction;
float frame_rotation;
};
// TWEAKS start
#define ANIMATE_PHASE
#define ANIMATE_SCANLINE
#define USE_SCANLINES
#define USE_SUBPIXELS
#define USE_GAMMA
//default NTSC gamma = 2.2
const float gamma = 2.2;
const float pi = 3.142;
const float phase_x = 5./6.*2.;
//0.83333334899648563972123297929094*2.;
//16.52723/10.0; // 4./2.0
const float phase_y = 1./6.*2.;
//0.16667200780160314494044593822*2.;
//12.33362/10.0; // 1./3.0
const float phase_move = 2./6.*2.;
const int samples_count = 20;
const int tv_pixels = 400;
// screen size, scanlines = y*2; y one field, and y other field.
vec2 size = vec2(256,224);
const float dark_scanline = 0.5; // half
// TWEAKS end
vec3 monitor(uniform sampler2D tex : TEXUNIT0, vec2 p, uniform input IN)
{
vec2 size2 = IN.texture_size;
vec2 uv = p;
float alpha = (p.x*IN.texture_size.x/IN.video_size.x*size.x*phase_x
+floor(p.y*IN.texture_size.y/IN.video_size.y*size.y)*phase_y
#ifdef ANIMATE_PHASE
+IN.frame_count*phase_move
#endif
)*pi;
float step = 2.*pi/samples_count;
float ustep = 2./phase_x/samples_count*IN.video_size.x/IN.texture_size.x/size.x;
float utotal = 0.;
float vtotal = 0.;
float sig = 0.;
alpha -= step*samples_count;
uv.x -= ustep*samples_count;
for (int i=0; i<samples_count; ++i)
{
#ifdef USE_GAMMA
vec4 res = pow(tex2D(tex, uv),1./gamma);
#else
vec4 res = tex2D(tex, uv);
#endif
vec3 yuv = mul(float3x3(
0.299,0.587,0.114,
-0.299,-0.587,1.-0.114,
1.-0.299,-0.587,-0.114), res.xyz);
sig = yuv.x+dot(yuv.yz*vec2(0.492111,0.877283),vec2(sin(alpha),cos(alpha)));
utotal += sig*sin(alpha);
vtotal += sig*cos(alpha);
alpha += step;
uv.x += ustep;
}
vec3 yuv = vec3(-utotal*sin(alpha-step)-vtotal*cos(alpha-step),
utotal/0.492111,vtotal/0.877283)*(2./samples_count);
yuv.x += sig;
vec3 rgb =
#ifdef USE_GAMMA
pow(
#endif
mul(float3x3(
1., 0., 1.,
1., -0.1942078364565588, -0.5093696763202726,
1., 1., 0.), yuv)
#ifdef USE_GAMMA
,gamma)
#endif
;
#if (defined(USE_SUBPIXELS) || defined(USE_SCANLINES))
vec2 q = (p*IN.texture_size/IN.video_size)*vec2(tv_pixels*3,size.y*2);
#endif
#ifdef USE_SCANLINES
float z =
#ifdef ANIMATE_SCANLINE
mod(IN.frame_count,2.0)+
#endif
0.5;
if (abs(mod(q.y,2)-z)<0.5)
rgb *= dark_scanline;
#endif
// size of pixel screen in texture coords:
//float output_pixel_size = IN.video_size.x/(IN.output_size.x*IN.texture_size.x);
// correctness check
//if (mod(p.x*output_pixel_size,2.0) < 1.0)
// rgb = vec3(0);
#ifdef USE_SUBPIXELS
float left = mod(q.x-0.5*tv_pixels*3./IN.output_size.x,3);
float right = left+tv_pixels*3./IN.output_size.x;
vec3 w = min(max(vec3(0.,1.,2.),vec3(left)),vec3(1.,2.,3.))
-max(min(vec3(1.,2.,3.),vec3(right)),vec3(0.,1.,2.))
+min(max(vec3(3.,4.,5.),vec3(left)),vec3(4.,5.,6.))
-max(min(vec3(4.,5.,6.),vec3(right)),vec3(3.,4.,5.));
rgb = rgb*3.*w/(w.x+w.y+w.z);
#endif
return rgb;
}
// pos (left corner, sample size)
vec4 monitor_sample(uniform sampler2D tex : TEXUNIT0, vec2 p, vec2 sample, uniform input IN)
{
// linear interpolation was...
// now other thing.
// http://imgur.com/m8Z8trV
// AT LAST IT WORKS!!!!
// going to check in retroarch...
float2 size = IN.texture_size;
vec2 next = vec2(.25,1.)/size;
vec2 f = fract(vec2(4.,1.)*size*p);
sample *= vec2(4.,1.)*size;
vec2 l;
vec2 r;
if (f.x+sample.x < 1.)
{
l.x = f.x+sample.x;
r.x = 0.;
}
else
{
l.x = 1.-f.x;
r.x = min(1.,f.x+sample.x-1.);
}
if (f.y+sample.y < 1.)
{
l.y = f.y+sample.y;
r.y = 0.;
}
else
{
l.y = 1.-f.y;
r.y = min(1.,f.y+sample.y-1.);
}
vec3 top = mix(monitor(tex, p, IN), monitor(tex, p+vec2(next.x,0.), IN), r.x/(l.x+r.x));
vec3 bottom = mix(monitor(tex, p+vec2(0.,next.y), IN), monitor(tex, p+next, IN), r.x/(l.x+r.x));
return vec4(mix(top,bottom, r.y/(l.y+r.y)),1.0);
}
float4 main_fragment(uniform sampler2D tex : TEXUNIT0, float2 coords : TEXCOORD0, uniform input IN) : COLOR
{
return vec4(monitor(tex, (coords+vec2(0.0,0.0)), IN),1.); //monitor_sample(tex, (coords+vec2(0.0,0.0))/2., 1./2./IN.output_size, IN);
// difference
//float zoom = 8;
//vec4 sampled = monitor_sample(tex, coords, 1./zoom/IN.output_size, IN);
//vec4 simple = vec4(monitor(tex, coords, IN),1.);
//return vec4(length(sampled - simple)*5*sin(IN.frame_count/30.))+simple;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment