add some BFI shaders and blargg's NTSC port (#653)

* Create bfi-simple.slang

* Create bfi-simple.slangp

* add blargg's NTSC port

* Create blargg.slangp

* add edge-blended 240 hz rolling scan BFI

* Add files via upload

* add subframe motionblur test

* Create subframe-motionblur-test.slangp

* Create checkerboard.slang

* Create checkerboard.slangp

* start moving motionblur_test to bfi directory

* add motionblur_test preset

* Delete bfi/shaders/checkerboard.slang

doesn't work anyway

* Delete bfi/checkerboard.slangp

doesn't work anyway

* finish moving motionblur_test

* finish moving motionblur_test

* move some stuff around

* optimize pngs

* add some missing licenses
This commit is contained in:
hunterk 2024-11-21 22:27:33 -06:00 committed by GitHub
parent 235f49722d
commit 8731861b22
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
23 changed files with 528 additions and 0 deletions

14
ntsc/blargg.slangp Normal file
View File

@ -0,0 +1,14 @@
shaders = "3"
shader0 = "../stock.slang"
filter_linear0 = "false"
scale_type_x0 = "absolute"
scale_x0 = "640.000000"
scale_type_y0 = "source"
scale_y0 = "1.000000"
shader1 = "shaders/blargg/blargg-0.slang"
filter_linear1 = false
shader2 = "shaders/blargg/blargg-1.slang"
filter_linear2 = false

View File

@ -0,0 +1,48 @@
#version 450
// NewRisingSun and blargg's NTSC filter
// simplified and ported to glsl by metallic77
// no license given, but I would expect it to inherit the LGPL license from the C version
#include "blargg_params.inc"
#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 * 1.0001;
}
#pragma stage fragment
layout(location = 0) in vec2 vTexCoord;
layout(location = 0) out vec4 FragColor;
layout(set = 0, binding = 2) uniform sampler2D Source;
#define PI 3.1415926
// Colorspace conversion matrix for RGB-to-YIQ
const mat3 RGBYIQ = mat3(
0.2989, 0.5870, 0.1140,
0.5959, -0.2744, -0.3216,
0.2115, -0.5229, 0.3114
);
#define onedeg 0.017453
void main()
{
float modulo = 3.0; if (global.OriginalSize.x > 300.0) modulo = 2.0;
float phase = floor(vTexCoord.x*params.SourceSize.x)*pi_mod*onedeg + mod(floor(vTexCoord.y*params.SourceSize.y),modulo)*PI*vert_scal;
phase += ntsc_hue;
if (stat_ph == 1.0) phase += sin(mod(float(params.FrameCount/2),2.0))*PI;
vec3 YUV = texture(Source,vTexCoord).rgb;
YUV = YUV*RGBYIQ;
YUV *= vec3(ntsc_bri, cos(phase), sin(phase));
float signal = YUV.x + YUV.y + YUV.z;
FragColor = vec4(vec3(signal), 1.0);
}

View File

@ -0,0 +1,136 @@
#version 450
// NewRisingSun and blargg's NTSC filter
// simplified and ported to glsl by metallic77
// no license given, but I would expect it to inherit the LGPL license from the C version
#include "blargg_params.inc"
#define PI 3.1415926
#define fringing_mid 0.8
#define fringing_max 1.6
#define artifacts_mid 0.4
#define artifacts_max 0.6
#define onedeg 0.017453
// Colorspace conversion matrix for YIQ-to-RGB
const mat3 YIQ2RGB = mat3(
1.0, 0.956, 0.6210,
1.0, -0.2720, -0.6474,
1.0, -1.1060, 1.7046);
float blackman (float x)
{
float b = 0.42 - 0.5 * cos(x) + 0.08 * cos( x * 2.0 );
return b;
}
#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 * 1.0001;
}
#pragma stage fragment
layout(location = 0) in vec2 vTexCoord;
layout(location = 0) out vec4 FragColor;
layout(set = 0, binding = 2) uniform sampler2D Source;
void main()
{
vec2 size = params.SourceSize.xy;
vec2 uv = vTexCoord;
int i = int(kernel_half);
float cutoff_factor = -0.03125;
float cutoff = ntsc_bleed;
if ( cutoff < 0.0 )
{
// keep extreme value accessible only near upper end of scale (1.0)
cutoff *= cutoff;
cutoff *= cutoff;
cutoff *= cutoff;
cutoff *= -30.0 / 0.65;
}
cutoff = cutoff_factor - 0.65 * cutoff_factor * cutoff;
// Sample composite signal and decode to YUV
vec3 YUV = vec3(0);
float sum = 0.0;
float to_angle = ntsc_res + 1.0;
float rolloff = 1.0 + ntsc_sharp * 0.032;
float maxh = kernel_half*2.0;
float pow_a_n = pow( rolloff, maxh );
to_angle = PI / maxh * LUMA_CUTOFF * (to_angle * to_angle + 1.0);
for (int n=0; n<i*2+1; n++) { // 2*maxh + 1
// blargg-ntsc
// generate luma (y) filter using sinc kernel
float a = PI * 2.0 / (kernel_half * 2.0) * float(n);
float w = blackman(a);
vec2 pos = uv - vec2(kernel_half/size.x,0.0) + vec2(float(n) / size.x, 0.0);
float x = float(n) - kernel_half; // maxh/2
float angle = x * to_angle;
float kernel = 0.0;
float fringing = 0.0;
if (fract(float(n+2)/4.0) == 0.0)
{
if(fring >0.0)
fringing = -fring*(fringing_max-fringing_mid);
}
//instability occurs at center point with rolloff very close to 1.0
if ( x > 1.056 || pow_a_n > 1.056 || pow_a_n < 0.981 )
{
float rolloff_cos_a = rolloff * cos( angle );
float num = 1.0 - rolloff_cos_a -
pow_a_n * cos( maxh * angle ) +
pow_a_n * rolloff * cos( (maxh - 1.0) * angle );
float den = 1.0 - rolloff_cos_a - rolloff_cos_a + rolloff * rolloff;
float dsf = num / den;
kernel = dsf - 0.5;
}
YUV.x += texture(Source, pos).r*w*kernel*(1.0+fringing);
sum += w*kernel;
}
YUV.x /= sum;
float sumc = 0.0;
// blargg ntsc-chroma
// generate chroma (iq) filter using gaussian kernel
for (int n=-i; n<i; n++) {
vec2 pos = uv + vec2(float(n) / size.x, 0.0);
float modulo = 3.0; if (global.OriginalSize.x > 300.0) modulo = 2.0;
float phase = (floor(vTexCoord.x*params.SourceSize.x)+float(n))*pi_mod*onedeg + mod(floor(vTexCoord.y*params.SourceSize.y),modulo)*PI*vert_scal;
if (stat_ph == 1.0) phase += sin(mod(float(params.FrameCount/2),2.0))*PI;
float r = exp(cutoff*float(n)*float(n));
float artifacts = 0.0;
if (fract(float(n+i+2)/4.0) == 0.0)
{
if(afacts>0.0)
artifacts= -afacts*(artifacts_max-artifacts_mid);
}
vec2 carrier = ntsc_sat*vec2(cos(phase), sin(phase));
YUV.yz += r*texture(Source, pos).gb * carrier*(1.0+artifacts);
sumc += r;
}
YUV.yz /= sumc;
// Convert signal to RGB
YUV = YUV*YIQ2RGB;
FragColor = vec4(YUV, 1.0);
}

View File

@ -0,0 +1,43 @@
layout(push_constant) uniform Push
{
vec4 SourceSize;
uint FrameCount;
float kernel_half, ntsc_sat, ntsc_res, ntsc_bri, ntsc_hue, ntsc_sharp, fring, afacts, ntsc_bleed, LUMA_CUTOFF, stat_ph, dummy, pi_mod, vert_scal;
} params;
#pragma parameter kernel_half "Kernel Half-Size (speed-up)" 16.0 1.0 16.0 1.0
#pragma parameter ntsc_sat "Saturation" 2.0 0.0 6.0 0.05
#pragma parameter ntsc_res "Resolution" 0.0 -1.0 1.0 0.05
#pragma parameter ntsc_sharp "Sharpness" 0.1 -1.0 1.0 0.05
#pragma parameter ntsc_bri "Brightness" 1.0 0.0 2.0 0.01
#pragma parameter ntsc_hue "Hue" 0.0 -1.0 6.3 0.05
#pragma parameter fring "Fringing" 0.0 0.0 1.0 0.05
#pragma parameter afacts "Artifacts" 0.0 0.0 1.0 0.05
#pragma parameter ntsc_bleed "Chroma Bleed" 0.0 -0.75 2.0 0.05
#pragma parameter LUMA_CUTOFF "Luma Cutoff" 0.2 0.0 1.0 0.005
#pragma parameter stat_ph "Dot Crawl On/Off" 0.0 0.0 1.0 1.0
#pragma parameter dummy " [ System Specific Tweaks] " 0.0 0.0 0.0 0.0
#pragma parameter pi_mod "Phase-Horiz. Angle" 96.0 1.0 360.0 1.0
#pragma parameter vert_scal "Phase-Vertical Scale" 0.6667 0.0 2.0 0.05555
#define kernel_half params.kernel_half
#define ntsc_sat params.ntsc_sat
#define ntsc_res params.ntsc_res
#define ntsc_sharp params.ntsc_sharp
#define fring params.fring
#define afacts params.afacts
#define ntsc_bleed params.ntsc_bleed
#define LUMA_CUTOFF params.LUMA_CUTOFF
#define stat_ph params.stat_ph
#define dummy params.dummy
#define pi_mod params.pi_mod
#define vert_scal params.vert_scal
#define ntsc_bri params.ntsc_bri
#define ntsc_hue params.ntsc_hue
layout(std140, set = 0, binding = 0) uniform UBO
{
mat4 MVP;
vec4 OriginalSize;
vec4 OutputSize;
} global;

View File

@ -0,0 +1,3 @@
shaders = 1
shader0 = shaders/bfi-simple.slang

View File

@ -0,0 +1,12 @@
shaders = 1
shader0 = shaders/edge-blended/edge-blended-240hz-bfi.slang
textures = "top;bot;midtop;midbot"
top = shaders/edge-blended/resources/240hzTop.png
top_wrap_mode = mirrored_repeat
bot = shaders/edge-blended/resources/240hzBot.png
bot_wrap_mode = mirrored_repeat
midtop = shaders/edge-blended/resources/240hzMidTop.png
midbot = shaders/edge-blended/resources/240hzMidBot.png

View File

@ -0,0 +1,9 @@
shaders = 1
shader0 = shaders/motionblur_test/shaders/motionblur_test.slang
textures = "base;twoSub;threeSub;fourSub"
base = shaders/motionblur_test/resources/60.png
twoSub = shaders/motionblur_test/resources/120.png
threeSub = shaders/motionblur_test/resources/180.png
fourSub = shaders/motionblur_test/resources/240.png

View File

@ -0,0 +1,74 @@
#version 450
// BFI Simple
// several different implementations of software BFI
// license: public domain
layout(push_constant) uniform Push
{
vec4 SourceSize;
vec4 OriginalSize;
vec4 OutputSize;
uint FrameCount, CurrentSubFrame, TotalSubFrames;
float bfi_mode, bfidummy0, bfidummy1, bfidummy2, bfidummy3, bfidummy4, bfidummy5;
} params;
#pragma parameter bfi_mode "Sub-Frame BFI Mode" 0.0 0.0 5.0 1.0
int mode = int(params.bfi_mode);
#pragma parameter bfidummy0 "|| Mode 0 -> Disable BFI" 0.00001 0.00001 0.00001 0.00001
#pragma parameter bfidummy1 "|| Mode 1 -> Cycle on each sub-frame" 0.00001 0.00001 0.00001 0.00001
#pragma parameter bfidummy2 "|| Mode 2 -> Show only the first sub-frame" 0.00001 0.00001 0.00001 0.00001
#pragma parameter bfidummy3 "|| Mode 3 -> Show all but the last sub-frame" 0.00001 0.00001 0.00001 0.00001
#pragma parameter bfidummy4 "|| Mode 4 -> Fade out" 0.00001 0.00001 0.00001 0.00001
#pragma parameter bfidummy5 "|| Mode 5 -> Half blank" 0.00001 0.00001 0.00001 0.00001
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;
layout(location = 1) out vec4 bfi;
void main()
{
gl_Position = global.MVP * Position;
vTexCoord = TexCoord;
switch(mode){
case 0: // passthru
bfi = vec4(1.0);
break;
case 1: // classic oscillator
bfi = vec4(mod(float(params.FrameCount * params.TotalSubFrames + params.CurrentSubFrame), 2.));
break;
case 2: // blank all subframes after the first (maximize blur reduction)
bfi = (params.CurrentSubFrame == 1) ? vec4(1.0) : vec4(0.0);
break;
case 3: // blank the last subframe (maximize brightness)
bfi = (params.CurrentSubFrame == params.TotalSubFrames) ? vec4(0.0) : vec4(1.0);
break;
case 4: // fade out
bfi = vec4(1. - float(params.CurrentSubFrame - 1) / float(params.TotalSubFrames));
break;
case 5: // half on, half off
bfi = (params.CurrentSubFrame > 0.5 * params.TotalSubFrames) ? vec4(1.0) : vec4(0.0);
break;
}
}
#pragma stage fragment
layout(location = 0) in vec2 vTexCoord;
layout(location = 1) in vec4 bfi;
layout(location = 0) out vec4 FragColor;
layout(set = 0, binding = 2) uniform sampler2D Source;
void main()
{
FragColor = vec4(texture(Source, vTexCoord).rgb, 1.0);
if(params.TotalSubFrames < 2) return;
else FragColor *= bfi;
}

View File

@ -0,0 +1,101 @@
#version 450
// edge-blended 240Hz BFI
// by hunterk
// license: public domain
layout(push_constant) uniform Push
{
vec4 SourceSize;
vec4 OriginalSize;
vec4 OutputSize;
uint FrameCount;
uint CurrentSubFrame;
uint TotalSubFrames;
float favor_black;
} params;
// setting this to 1 makes 75% of the screen black at all times
// while setting it to 0 makes it only 25% black
#pragma parameter favor_black "Favor Black" 1.0 0.0 1.0 1.0
// set a macro here for debugging purposes since there's no way to advance subframes
#define debugFrame params.CurrentSubFrame
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;
layout(location = 1) out vec4 check;
layout(location = 2) out vec2 lutCoord;
void main()
{
gl_Position = global.MVP * Position;
vTexCoord = TexCoord;
// TODO/FIXME: figure out some good way to make the seams roll
lutCoord = TexCoord;// + vec2(0., mod(float(params.FrameCount), 250.)/1000.);
// set up a bool to check which sub-frame we're currently on
bvec4 checkbool = bvec4(debugFrame == 1,
debugFrame == 2,
debugFrame == 3,
debugFrame == 4);
// flip the check bool if we want to blackout on all but one sub-frame
bvec4 boolflip = bvec4(params.favor_black);
check.x = boolflip.x ? float(!checkbool.x) : float(checkbool.x);
check.y = boolflip.y ? float(!checkbool.y) : float(checkbool.y);
check.z = boolflip.z ? float(!checkbool.z) : float(checkbool.z);
check.w = boolflip.w ? float(!checkbool.w) : float(checkbool.w);
}
#pragma stage fragment
layout(location = 0) in vec2 vTexCoord;
layout(location = 1) in vec4 check;
layout(location = 2) in vec2 lutCoord;
layout(location = 0) out vec4 FragColor;
layout(set = 0, binding = 2) uniform sampler2D Source;
layout(set = 0, binding = 3) uniform sampler2D top;
layout(set = 0, binding = 4) uniform sampler2D bot;
layout(set = 0, binding = 5) uniform sampler2D midtop;
layout(set = 0, binding = 6) uniform sampler2D midbot;
void main()
{
FragColor = vec4(texture(Source, vTexCoord).rgb, 1.0);
// early return if we don't have subframes enabled
if(params.TotalSubFrames == 1) return;
// use a nonstandard approximation of linear gamma to blend transitions better
// going higher makes the transitions blend better but creates harsh lines elsewhere
FragColor = pow(FragColor, vec4(2.5));
// sample the LUTs, "linearize" them
vec4 Top = texture(top, lutCoord);
Top = pow(Top, vec4(2.5));
vec4 Bot = texture(bot, lutCoord);
Bot = pow(Bot, vec4(2.5));
vec4 MidTop = texture(midtop, lutCoord);
MidTop = pow(MidTop, vec4(2.5));
vec4 MidBot = texture(midbot, lutCoord);
MidBot = pow(MidBot, vec4(2.5));
// based on the subframe, apply the blackout LUTs
FragColor = (bool(check.x)) ? mix(FragColor, Top, Top.a) : FragColor;
FragColor = (bool(check.y)) ? mix(FragColor, MidTop, MidTop.a) : FragColor;
FragColor = (bool(check.z)) ? mix(FragColor, MidBot, MidBot.a) : FragColor;
FragColor = (bool(check.w)) ? mix(FragColor, Bot, Bot.a) : FragColor;
// black-out the other transitions when preferring black
FragColor *= (params.favor_black > 0.5 && debugFrame == 1 && vTexCoord.y > 0.3333) ? vec4(0.0) : vec4(1.0);
FragColor *= (params.favor_black > 0.5 && debugFrame == 3 && (vTexCoord.y > 0.8 || vTexCoord.y < 0.3333)) ? vec4(0.0) : vec4(1.0);
FragColor *= (params.favor_black > 0.5 && debugFrame == 2 && (vTexCoord.y > 0.6667 || vTexCoord.y < 0.2)) ? vec4(0.0) : vec4(1.0);
FragColor *= (params.favor_black > 0.5 && debugFrame == 4 && vTexCoord.y < 0.6667) ? vec4(0.0) : vec4(1.0);
// de-"linearize"
FragColor = pow(FragColor, vec4(1./2.5));
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@ -0,0 +1,88 @@
#version 450
// motionblur test
// by hunterk
// license: public domain
layout(push_constant) uniform Push
{
vec4 SourceSize;
vec4 OriginalSize;
vec4 OutputSize;
uint FrameCount;
uint TotalSubFrames;
uint CurrentSubFrame;
vec4 FinalViewportSize;
float inv_speed;
float OriginalAspect;
} params;
#pragma parameter inv_speed "Invader Speed" 1.0 1.0 20.0 1.0
float speed = params.FrameCount * params.inv_speed;
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;
layout(location = 1) out vec2 noSubCoord;
layout(location = 2) out vec2 fullSubCoord;
layout(location = 3) out vec2 halfSubCoord;
void main()
{
gl_Position = global.MVP * Position;
vTexCoord = TexCoord;
noSubCoord = vec2(vTexCoord.xy * params.OutputSize.xy) - ivec2(mod(speed * params.TotalSubFrames, params.OutputSize.x),100);
fullSubCoord = vec2(vTexCoord.xy * params.OutputSize.xy) - ivec2(mod(speed * params.TotalSubFrames + params.CurrentSubFrame - 1., params.OutputSize.x),400);
halfSubCoord = vec2(vTexCoord.xy * params.OutputSize.xy) - ivec2(mod(speed * params.TotalSubFrames + int((params.CurrentSubFrame - 1.) / 2.), params.OutputSize.x),250);
}
#pragma stage fragment
layout(location = 0) in vec2 vTexCoord;
layout(location = 1) in vec2 noSubCoord;
layout(location = 2) in vec2 fullSubCoord;
layout(location = 3) in vec2 halfSubCoord;
layout(location = 0) out vec4 FragColor;
layout(set = 0, binding = 2) uniform sampler2D Source;
layout(set = 0, binding = 3) uniform sampler2D base;
layout(set = 0, binding = 4) uniform sampler2D twoSub;
layout(set = 0, binding = 5) uniform sampler2D threeSub;
layout(set = 0, binding = 6) uniform sampler2D fourSub;
void main()
{
FragColor = texture(Source, vTexCoord);
if(params.TotalSubFrames == 2)
{
FragColor = vec4(0.0);
vec4 invader60 = texelFetch(base, ivec2(noSubCoord), 0);
FragColor = mix(FragColor, invader60, invader60.a);
vec4 invader120 = texelFetch(twoSub, ivec2(fullSubCoord), 0);
FragColor = mix(FragColor, invader120, invader120.a);
}
else if(params.TotalSubFrames == 3)
{
FragColor = vec4(0.0);
vec4 invader60 = texelFetch(base, ivec2(noSubCoord), 0);
FragColor = mix(FragColor, invader60, invader60.a);
vec4 invader180 = texelFetch(threeSub, ivec2(fullSubCoord), 0);
FragColor = mix(FragColor, invader180, invader180.a);
}
else if(params.TotalSubFrames == 4)
{
FragColor = vec4(0.0);
vec4 invader60 = texelFetch(base, ivec2(noSubCoord), 0);
FragColor = mix(FragColor, invader60, invader60.a);
vec4 invader120 = texelFetch(twoSub, ivec2(halfSubCoord), 0);
FragColor = mix(FragColor, invader120, invader120.a);
vec4 invader240 = texelFetch(fourSub, ivec2(fullSubCoord), 0);
FragColor = mix(FragColor, invader240, invader240.a);
}
else return;
}