Old CRT display shader (Fallout’s Pipboy screen)

Posted on

Okey, let’s try to make something cool. Do you remember the Fallout’s Pipboy? I’m loving it and always wanted to have one.

Fallout's Pipboy
Fallout’s Pipboy

So, I want to make it similar, from scratch where I have one plane (2 triangles) and one texture. And FLSL of course )

Our future result:

What do we need?

  1. Slightly bend texture because it is CRT.

  2. Tint it green

  3. Place interlace effect

  4. And show frequency waves

The first is pure math. I need kind of a function, that changes UV from one point to another.

graph

Offset of every point depends on distance from it to the center (AO). The more this distance is, the more is offset.

Function shouldn’t be linear: density in the center is less than near the borders.

So, I built a function that meets the given criteria:

non-linear offset function

The complete transformation is:

   float2 ruv = iUV0;
    ruv -= 0.5;
    ruv*=screenSize;

ruv is our new UV, then we move UV coordinate system from 0..1 to -0.5..0.5 – because points need to be moved in direction of center.

   float d = length(ruv.xy);
    const float max_radius = 0.70711;

max_radius – is a distance to corner. It is sqrt of 0.5.

   float modified = sin((d/max_radius/2.5)*PI)/1.55;
    ruv += ruv*(d-modified);

Here is our function. Then we move the point by adding UV.

   ruv*=(2.2-distort);
    ruv += 0.5;

Bend value and we going back to normal coordinates from 0 to 1.

So, these are all transformations.

basic transformations

Let’s color it.

I can remove repeated texture by setting it to black. It is not a very good way, but it works )

   float modifier = (ruv.x>=0)*(ruv.y>=0)*(ruv.x<=1)*(ruv.y<=1);
    output fragment = color*modifier;

cutted edges

Grayscale it

   float modifier = (ruv.x>=0)*(ruv.y>=0)*(ruv.x<=1)*(ruv.y<=1);
    float4 finalColor = grayscaleLightnessColor(color)*modifier
    
    output fragment = finalColor*modifier;

Grayscale

Tint it to given color

   output fragment = finalColor*modifier*tint;

tinted

Place interlace effect

   float interlace = 1+clamp(sin(ruv.y*PI*inerlaceFrequency)*0.25,0.0,0.25)*interlaceIntensity;
    output fragment = finalColor*modifier*tint*interlace;

interlaced

And make flicks – one big with low frequency and small flicks with high frequency

   float flicks = 1;
    flicks += sin((ruv.y+time.x*5)*PI*3.5)*0.2;
    flicks += sin((ruv.y+time.x)*PI*2)*0.2;
    flicks *= flickIntensity;
    
    output fragment = finalColor*modifier*tint*interlace*flicks;

with flicks

That’s it. The complete shader:

use namespace flare;
use namespace flare.transforms;
use namespace flare.filters;

param float4 tint <ui = "color"> = float4(0.7,1,0.7,1);
param float4 backColor <ui = "color"> = float4(0.7,1,0.7,1);

sampler2D texture2;

param float screenSize = 1.1;
param float distort = 1.1;
param float flickIntensity = 1;

param float interlaceIntensity = 1;
param float inerlaceFrequency = 200;


param TIME time;

float grayscaleLightness(float4 color) {
    return max(max(color.r,color.g),color.b)+min(min(color.r,color.g),color.b)/2;
}
float4 grayscaleLightnessColor(float4 color) {
    return float4(grayscaleLightness(color));
}

technique main
{
    output vertex = transform();

    float2 ruv = iUV0;

    ruv -= 0.5;
    ruv*=screenSize;
    float d = length(ruv.xy);
    const float max_radius = 0.70711;
    float modified = sin((d/max_radius/2.5)*PI)/1.55;
    ruv += ruv*(d-modified);
    
    ruv*=(2.2-distort);

    
    ruv += 0.5;
    
    float darklevel = clamp((1-d)*1.4,0,1);
    
    
    float4 color =  sampler2D( texture2, ruv);
    
    float modifier = (ruv.x>=0)*(ruv.y>=0)*(ruv.x<=1)*(ruv.y<=1);
    float4 finalColor = grayscaleLightnessColor(color)*modifier
    
    float interlace = 1+clamp(sin(ruv.y*PI*inerlaceFrequency)*0.25,0.0,0.25)*interlaceIntensity;
    
    float flicks = 1;
    flicks += sin((ruv.y+time.x*5)*PI*3.5)*0.2;
    flicks += sin((ruv.y+time.x)*PI*2)*0.2;
    flicks *= flickIntensity;
    
    output fragment = finalColor*modifier*tint*interlace*flicks;
    
}

Leave a Reply