Thursday, April 10, 2014

Interlacing Shader for CRTs

In the comments of my TVs and Retro Gaming post, GPDP and Monroe88 mentioned the need for a shader that would take a 480p signal from an emulator and either add 100% black scanlines on "240p" content or blank alternating fields each frame. In other words, they needed a shader that would interlace the default progressive signal, which would allow them to run their displays at 480p for all retro console emulation instead of having to switch back and forth between 320x240 (most games for NES, SNES, Genesis/Mega Drive etc.) and 640x480 (for games that utilized the added lines of resolution that interlacing provided, such as 2-player mode in Sonic 2, SNES' R.P.M. Racing, and tons of PSX games).

So, here's the shader in RetroArch-compatible Cg format:
- HLSL compilers
- Cg compilers
Author: hunterk
License: Public domain
struct input
float2 video_size;
float2 texture_size;
float2 output_size;
float frame_count;
float frame_direction;
float frame_rotation;
sampler2D texture : TEXUNIT0;
void main_vertex
float4 position : POSITION,
out float4 oPosition : POSITION,
uniform float4x4 modelViewProj,
float4 color : COLOR,
out float4 oColor : COLOR,
float2 texCoord : TEXCOORD,
out float2 oTexCoord : TEXCOORD,
uniform input IN
oPosition = mul(modelViewProj, position);
oColor = color;
oTexCoord = texCoord;
float4 main_fragment (in float2 texCoord : TEXCOORD, uniform input IN) : COLOR
float4 res = tex2D(IN.texture, texCoord);
float y = 0.0;
// assume anything with a vertical resolution greater than 400 lines is interlaced
if (IN.video_size.y > 400.0) y = IN.texture_size.y * texCoord.y + IN.frame_count;
y = 2.0 * IN.texture_size.y * texCoord.y;
if (fmod(y, 2.0) > 0.99999) return res;
return float4(0.0);
It has two branching 'if' statements, which is horrible for performance in shaders, but this one is simple enough--and only needs to run at 2x--that it shouldn't matter.

UPDATE: In the comments, Monroe88 mentioned another very useful shader for use with 31 kHz CRT monitors: aliaspider's TVoutTweaks, which lets you add some nice effects, such as composite color correction and horizontal bandwidth (blends things like SNES' pseudo-hires transparency and other dithering effects), which will make the image a little closer to a 15 kHz TV. See Monroe88's comment below for more info and a pic.


Monroe88 said...

Here are some presets I came up with for this shader

The first pass is scaled horizontally to viewport, so it only applies horizontally. You can change the filter to linear to soften the image a bit or use a different shader like xBR or aliaspider's TVouTweaks.

Threw on image-adjustment for gamma correction and color control for good measure.

If you find your 31khz CRT looking too sterile because of the sharpness, TVoutTweaks is a good way to give it a more TV-like look, and it also blends dithering and SNES hires output. The blur is adjustable by editing the RESOLUTION define.

Here's a pic of my CRT monitor displaying 3840x480 in RetroArch with and

Hunter K. said...

That looks really good, man. I updated the post with a note about your comment. :)

iphone said...

Thanks for sharing your info. I truly appreciate your efforts and I will be waiting for your further write ups thank you once again.

Anonymous said...

More presets, this time with Themaister's NTSC shaders. Used Hyllian's Jinc shaders to smooth it out so it looks natural. High horizontal resolution recommended.

Analytics Tracking Footer