Free H O R R I - F I | A lo-fi horror shader!

Gizmo199

Member

Horri-Fi is a simple and easy-to-use Shader for Game Maker Studio 2! Setup is as simple as calling:
Code:
horrifi_enable(true);
and then applying it to your surface:
Code:
horrifi_set();
draw_surface(application_surface, 0, 0);
horrifi_reset();
That's it!

This shader includes:
  • Bloom
  • Chromatic Abberation
  • Scanlines
  • VHS Distortion
  • CRT TV Curve
  • Noise
  • Vignette
You can dynamically set any of the shader values using the
Code:
horrifi_*element_set()
functions such as:
Code:
horrifi_bloom_set()
horrifi_chromaticab_set()
horrifi_scanlines_set()
horrifi_vhs_set()
horrifi_vignette_set()
horrifi_crt_set()
horrifi_noise_set()
The demo above is also included as an EXE file that you can run, adjust settings, and press the "Copy GML code to clipboard" button and paste in your game!!

Don't wanna download anything? Below is the script library, fragment, and vertex shader you can easily copy/paste!
GML:
global.__horriFi =
{
    #region Main System
   
        enabled        : true,
        time        : 0,
       
    #endregion
    #region Parameter Structs
        // Vignette settings
        vignette :
        {
            enabled : false,
            strength : .25,
            intensity : .5
        },
       
        // Noise settings
        noise :
        {
            enabled : false,
            strength : .1
        },
       
        // Chromatic abberation settings
        chromatic_abberation :
        {
            enabled : false,
            strength : .3
        },
       
        // Bloom
        bloom :
        {
            enabled : false,
            radius : 8,
            intensity : 1,
            threshold : 0.8
        },
   
        // VHS Distortion
        vhs :
        {
            enabled : false,
            strength : .5
        },
   
        // Scanlines
        scanlines :
        {
            enabled : false,
            strength : .25
        },
       
        // CRT Curve
        crt :
        {
            enabled : false,
            curve : 2
        },
    #endregion
    #region Shader Uniforms
   
        uniEnable    : shader_get_uniform(shd_horrifi, "enable"),
        uniTime        : shader_get_uniform(shd_horrifi, "time"),
        uniSeed        : shader_get_uniform(shd_horrifi, "seed"),
        uniTexel    : shader_get_uniform(shd_horrifi, "texel"),
        uniVig        : shader_get_uniform(shd_horrifi, "vignette"),
        uniNstr        : shader_get_uniform(shd_horrifi, "noise_strength"),
        uniChab        : shader_get_uniform(shd_horrifi, "chab_intensity"),
        uniBloom    : shader_get_uniform(shd_horrifi, "bloom"),
        uniScanStr    : shader_get_uniform(shd_horrifi, "scan_strength"),
        uniVhs        : shader_get_uniform(shd_horrifi, "vhs_strength"),
        uniCurve    : shader_get_uniform(shd_horrifi, "curve"),
       
    #endregion
    #region Functions
   
        render : function()
        {
            var _w, _h, _s;
            _s = surface_get_texture(application_surface);
            _w = texture_get_texel_width(_s);
            _h = texture_get_texel_height(_s);
           
            if ( !enabled ) exit;
            // Set shader
            shader_set(shd_horrifi);
       
            // Enable effects
            shader_set_uniform_f_array( uniEnable,
                [
                    bloom.enabled,
                    chromatic_abberation.enabled,
                    noise.enabled,
                    vignette.enabled,
                    scanlines.enabled,
                    vhs.enabled,
                    crt.enabled
                ]
            );
       
            // Set effects parameters
            shader_set_uniform_f(uniTexel, _w, _h);
            shader_set_uniform_f(uniTime, time++);
            shader_set_uniform_f(uniSeed, 1+random(100));
            shader_set_uniform_f(uniVig, vignette.intensity, vignette.strength);
            shader_set_uniform_f(uniNstr, noise.strength);
            shader_set_uniform_f(uniChab, chromatic_abberation.strength);
            shader_set_uniform_f(uniBloom, bloom.radius, bloom.intensity, bloom.threshold);
            shader_set_uniform_f(uniScanStr, scanlines.strength);
            shader_set_uniform_f(uniVhs, vhs.strength);
            shader_set_uniform_f(uniCurve, crt.curve, crt.curve);
        },
       
        // Resetting
        reset : function()
        {
            if ( enabled ) shader_reset();
        }
       
    #endregion
}

// Main functions
function horrifi_enable(onoff)
{
    ///@func horrifi_enable(enable)
    global.__horriFi.enabled = onoff;
}
function horrifi_is_enabled()
{
    return global.__horriFi.enabled;
}   
function horrifi_set()
{
    global.__horriFi.render();   
}
function horrifi_reset()
{   
    global.__horriFi.reset();
}

// Bloom functions
function horrifi_bloom_enable(onoff)
{
    ///@func horrifi_bloom_enable(enable)
    global.__horriFi.bloom.enabled = onoff;
}
function horrifi_bloom_radius(rad)
{
    ///@func horrifi_bloom_radius(radius)
    global.__horriFi.bloom.radius = rad;
}
function horrifi_bloom_intensity(int)
{
    ///@func horrifi_bloom_intensity(intensity)
    global.__horriFi.bloom.intensity = int;
}   
function horrifi_bloom_threshold(thresh)
{
    ///@func horrifi_bloom_threshold(threshold)
    global.__horriFi.bloom.threshold = thresh;
}
function horrifi_bloom_is_enabled()
{
    return global.__horriFi.bloom.enabled;   
}
function horrifi_bloom_get_radius()
{
    return global.__horriFi.bloom.radius;   
}
function horrifi_bloom_get_intensity()
{
    return global.__horriFi.bloom.intensity;   
}
function horrifi_bloom_get_threshold()
{
    return global.__horriFi.bloom.threshold;   
}
function horrifi_bloom_set(e,r,i,t)
{
    ///@func horrifi_bloom_set(enabled, radius, intensity, threshold)
    horrifi_bloom_enable(e);
    horrifi_bloom_radius(r);
    horrifi_bloom_intensity(i);
    horrifi_bloom_threshold(t);
}
   
// Chromatic Abberation functions
function horrifi_chromaticab_enable(onoff)
{
    ///@func horrifi_chromaticab_enable(enable)
    global.__horriFi.chromatic_abberation.enabled = onoff;
}   
function horrifi_chromaticab_strength(str)
{
    ///@func horrifi_chromaticab_strength(strength)
    global.__horriFi.chromatic_abberation.strength = str;
}
function horrifi_chromaticab_is_enabled()
{
    return global.__horriFi.chromatic_abberation.enabled;
}
function horrifi_chromaticab_get_strength()
{
    return global.__horriFi.chromatic_abberation.strength;   
}
function horrifi_chromaticab_set(e,s)
{
    ///@func horrifi_chromaticab_set(enable, strength)
    horrifi_chromaticab_enable(e);
    horrifi_chromaticab_strength(s);
}

// Noise functions
function horrifi_noise_enable(onoff)
{
    ///@func horrifi_noise_enable(enable)
    global.__horriFi.noise.enabled = onoff;
}   
function horrifi_noise_strength(str)
{
    ///@func horrifi_noise_strength(strength)
    global.__horriFi.noise.strength = str;
}
function horrifi_noise_is_enabled()
{
    return global.__horriFi.noise.enabled;
}
function horrifi_noise_get_strength()
{   
    return global.__horriFi.noise.strength;
}
function horrifi_noise_set(e, s)
{
    ///@func horrifi_noise_set(enable, strength)
    horrifi_noise_enable(e);
    horrifi_noise_strength(s);
}

// Vignette functions
function horrifi_vignette_enable(onoff)       
{
    ///@func horrifi_vignette_enable(enable)   
    global.__horriFi.vignette.enabled=onoff
}
function horrifi_vignette_strength(str)       
{
    ///@func horrifi_vignette_strength(strength)   
    global.__horriFi.vignette.strength = str;
}
function horrifi_vignette_intensity(int)   
{
    ///@func horrifi_vignette_intensity(intensity)
    global.__horriFi.vignette.intensity = int;
}
function horrifi_vignette_is_enabled()       
{
    return global.__horriFi.vignette.enabled;
}
function horrifi_vignette_get_strength()   
{
    return global.__horriFi.vignette.strength;
}
function horrifi_vignette_get_intensity()   
{
    return global.__horriFi.vignette.intensity;
}
function horrifi_vignette_set(e,s,i)
{
    ///@func horrifi_vignette_set(enable, strength, intensity)
    horrifi_vignette_enable(e);
    horrifi_vignette_strength(s);
    horrifi_vignette_intensity(i);
}

// VHS functions
function horrifi_vhs_enable(onoff)
{
    ///@func horrifi_vhs_enable(enable)
    global.__horriFi.vhs.enabled = onoff;
}   
function horrifi_vhs_strength(str)
{
    ///@func horrifi_vhs_strength(strength)
    global.__horriFi.vhs.strength = str;
}
function horrifi_vhs_is_enabled()
{
    return global.__horriFi.vhs.enabled;   
}
function horrifi_vhs_get_strength()
{
    return global.__horriFi.vhs.strength;   
}
function horrifi_vhs_set(e,s)
{
    ///@func horrifi_vhs_set(enable, strength)
    horrifi_vhs_enable(e);
    horrifi_vhs_strength(s);
}

// Scanlines functions
function horrifi_scanlines_enable(onoff)
{
    ///@func horrifi_scanlines_enable(enable)
    global.__horriFi.scanlines.enabled = onoff;
}   
function horrifi_scanlines_strength(str)
{
    ///@func horrifi_scanlines_strength(strength)
    global.__horriFi.scanlines.strength = str;
}
function horrifi_scanlines_is_enabled()
{
    return global.__horriFi.scanlines.enabled;   
}
function horrifi_scanlines_get_strength()
{
    return global.__horriFi.scanlines.strength;   
}
function horrifi_scanlines_set(e,s)
{
    ///@func horrifi_scanlines_set(enable, strength)
    horrifi_scanlines_enable(e);
    horrifi_scanlines_strength(s);
}

// CRT functions
function horrifi_crt_enable(onoff)
{
    ///@func horrifi_crt_enable(enable)
    global.__horriFi.crt.enabled = onoff;
}   
function horrifi_crt_curve(str1)
{
    ///@func horrifi_crt_curve(strength)
    global.__horriFi.crt.curve = str1;
}
function horrifi_crt_is_enabled()
{
    return global.__horriFi.crt.enabled;   
}
function horrifi_crt_get_curve()
{
    return global.__horriFi.crt.curve;   
}
function horrifi_crt_set(e,s1)
{
    ///@func horrifi_crt_set(enable, curve)
    horrifi_crt_enable(e);
    horrifi_crt_curve(s1);
}
Code:
attribute vec3 in_Position;                  // (x,y,z)
attribute vec4 in_Colour;                    // (r,g,b,a)
attribute vec2 in_TextureCoord;              // (u,v)

varying vec2 v_vTexcoord;
varying vec4 v_vColour;

void main()
{
    vec4 object_space_pos = vec4( in_Position.x, in_Position.y, in_Position.z, 1.0);
    gl_Position = gm_Matrices[MATRIX_WORLD_VIEW_PROJECTION] * object_space_pos;
   
    v_vColour = in_Colour;
    v_vTexcoord = in_TextureCoord;
}
Code:
#define SAMPLES 32.

varying vec2 v_vTexcoord;
varying vec4 v_vColour;

uniform float enable[7];

uniform float time;
uniform float seed;
uniform float noise_strength;
uniform float chab_intensity;
uniform float vhs_strength;
uniform float scan_strength;

uniform vec2 curve;
uniform vec2 texel;
uniform vec2 vignette;
uniform vec3 bloom;

// Noise
float noise(vec2 uv)
{
    return fract(sin(uv.x*12.9898+uv.y*78.233)*437.585453*seed);
}

// VHS
vec4 vhs(vec2 uv)
{
    vec2 tcoord = uv;
    tcoord.x += sin(time*noise(uv));
    return texture2D( gm_BaseTexture, tcoord)*vhs_strength;    
}

// Vignette
float vig(vec2 uv)
{
    uv *= 1. - uv;
    return ( pow(uv.x*uv.y*vignette.x*10.,vignette.y) );
}

// Chromatic abberation
vec3 chromatic(vec2 uv, float offset)
{
    float _r = texture2D( gm_BaseTexture, vec2(uv.x+offset, uv.y)).r;
    float _g = texture2D( gm_BaseTexture, uv).g;
    float _b = texture2D( gm_BaseTexture, vec2(uv.x-offset, uv.y)).b;
    return vec3(_r,_g,_b);
}

// Bloom
vec4 blur(vec2 uv)
{
    float total = 0.;
    float rad = 1.;
    mat2 ang = mat2(.73736882209777832,-.67549037933349609,.67549037933349609,.73736882209777832);
    vec2 point = normalize(fract(cos(uv*mat2(195,174,286,183))*742.)-.5)*(bloom.x/sqrt(SAMPLES));
    vec4 amount = vec4(0);
    
    for ( float i=0.; i<SAMPLES; i++ )
    {
        point*=ang;
        rad+=1./rad;
        vec4 samp = texture2D(gm_BaseTexture, uv + point * (rad-1.) * texel);
        
        float mul = 1.;
        float lum = ( samp.r+samp.g+samp.b )/3.;
        if ( lum < bloom.z ){ mul = 0.; }
        
        amount += samp*(1./rad)*mul;
        total+=(1./rad);
    }
    amount /= total;
    return amount*bloom.y;
}

// TV Curve
vec2 crt_curve( vec2 uv )
{
    uv = uv*2.-1.;
    vec2 uvoff = abs(uv.xy) / vec2(curve.x, curve.y);
    uv = uv + uv * uvoff * uvoff;
    uv = uv * .5 + .5;
    return uv;
}

void main()
{    
    // CRT 
    vec2 mainUv = v_vTexcoord;
    if ( enable[6] > .5 ) mainUv = crt_curve(v_vTexcoord);
    
    // Base coloring
    vec4 color = v_vColour * texture2D( gm_BaseTexture, mainUv);
    
    // Chromatic abberation
    if ( enable[1] > .5 ) color.rgb = v_vColour.rgb * chromatic(mainUv, chab_intensity * 0.01);
    
    // Scanlines
    if ( enable[4] > .5 ) color.rgb *= (1.-scan_strength)+(sin(mainUv.y*1024.)*scan_strength);
    
    // Bloom
    if ( enable[0] > .5 ) color.rgb += blur(mainUv).rgb;
    
    // Noise
    if ( enable[2] > .5 ) color.rgb += noise(mainUv)*noise_strength;
    
    // VHS
    if ( enable[5] > .5 ) color += vhs(mainUv);
    
    // Vignette
    if ( enable[3] > .5) color.rgb *= vig(mainUv);
    
    // Cutoff edges
    if ( enable[6] > .5)
    {
        if ( mainUv.x<0.||mainUv.y<0.||mainUv.x>1.||mainUv.y>1. )
        {
            color.rgb *= 0.;    
        }
    }
    
    gl_FragColor = color;
}
 
Last edited:
Top