I want Space Mines III to look like it is playing on a 24x24 text screen displayed on the ten inch black and white CRT I played on as a kid. That is a little tricky because that CRT is long gone. Finding reference pictures of old CRT's was surprisingly difficult, they were either blurry or modern image editor reproductions. Luckily I found several good photos on vintage-computer.com.
CRT shaders are tricky beasts because LCD's don't really have the resolution to simulate a CRT properly. There are lots of CRT shaders on the internet, but they are designed to simulate visual aberrations and the color masks of various types of color monitors. I couldn't find a single monochrome CRT shader.
The code for rendering the CRT is pretty simple, it is rendered in three passes of a fullscreen quad with an orthographic projection. Hardcore render people would get that down to a single triangle, I couldn't be bothered. The shader for the first pass is included here, the other two shaders discussed below can be found easily on the internet.
The game sends all the info about the characters to render on the screen as a single bitmap (crttexture) split into three sections. The left third is the foreground color for the letter (fontForeground), the middle third is the background color (fontBackground), the right third stores the character's index into the font in the red channel (fontCharacter). I didn't bother to make the bitmap a power-of-two size (which video cards prefer) the bitmap is a simple multiple of the CRT dimension in characters (crtSize).
#version 150
uniform sampler2D crttexture;
uniform sampler2D font;
uniform vec2 crtSize;
in vec2 texCoord0;
out vec4 fragColor;
void main (void)
{
float s = texCoord0.s / 3.0;
vec4 fontForeground = texture( crttexture, vec2( s, texCoord0.t ) );
vec4 fontBackground = texture( crttexture, vec2( s + ( 1.0 / 3.0 ), texCoord0.t ) );
float fontCharacter = texture( crttexture, vec2( s + ( 2.0 / 3.0 ), texCoord0.t ) ).r * 255.0;
vec2 crtCellCoords = mod( texCoord0 * crtSize, 1.0 );
vec2 fontCellCoords0 = vec2( floor( mod( fontCharacter, 16.0 ) ) / 16.0, floor( fontCharacter / 16.0 ) / 16.0 );
vec2 fontCellCoords1 = fontCellCoords0 + vec2( 1.0 / 16.0, 1.0 / 16.0 );
vec2 fontCoords = mix( fontCellCoords0, fontCellCoords1, crtCellCoords );
float fontTexel = texture( font, fontCoords ).r;
fragColor = fontTexel < 0.5 ? fontForeground : fontBackground;
if( fontTexel >= 0.5 )
{
fragColor = fontBackground;
}
else
{
vec2 pixelResolution = crtSize * 8;
vec2 pixel = mod( texCoord0, 1.0 / pixelResolution ) * pixelResolution;
vec2 m = pixel - vec2( 0.5, 0.5 );
float radius = sqrt(m.x * m.x + m.y * m.y);
const float MIN_RADIUS = 0.3;
const float MAX_RADIUS = 0.5;
if( radius < MIN_RADIUS )
{
fragColor = fontForeground;
}
else if (radius > MAX_RADIUS )
{
fragColor = fontBackground;
}
else
{
fragColor = mix( fontForeground, fontBackground, ( radius - MIN_RADIUS ) / ( MAX_RADIUS - MIN_RADIUS ) );
}
}
if( fontTexel >= 0.5 )
{
fragColor = fontBackground;
}
else
{
// Radius of ellipse. Values greater than .5 blend to next pixel.
const float RADIUS_X = 0.55;
const float RADIUS_Y = 0.45;
vec2 pixelResolution = crtSize * 8;
vec2 pixel = mod( texCoord0, 1.0 / pixelResolution ) * pixelResolution;
vec2 m = pixel - vec2( 0.5, 0.5 );
float radius = ( ( m.x * m.x ) / ( RADIUS_X * RADIUS_X ) ) + ( ( m.y * m.y ) / ( RADIUS_Y * RADIUS_Y ) );
// Blend from foreground to background color to blur edges of ellipse.
const float BLEND_MIN = 0.7;
const float BLEND_MAX = 1.0;
if ( radius < BLEND_MIN )
{
fragColor = fontForeground;
}
else if ( radius > BLEND_MAX )
{
fragColor = fontBackground;
}
else
{
fragColor = mix( fontForeground, fontBackground, ( radius - BLEND_MIN ) / ( BLEND_MAX - BLEND_MIN ) );
}
}
We were unable to retrieve our cookie from your web browser. If pressing F5 once to reload this page does not get rid of this message, please read this to learn more.
You will not be able to post until you resolve this problem.