Last active
July 8, 2019 23:45
-
-
Save realmonster/b504593c970a45c0d1ba9ec2c8a13fcf to your computer and use it in GitHub Desktop.
NOT Ideal NTSC composite cable CRT simulation for RetroArch
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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