Stencyl 3.4.0 is now out. Get it now!

Some Custom Shaders

rob1221

  • *
  • Posts: 9036
I've started getting into shaders recently and over time I'll be sharing some of the ones that I make.  For each one I'll include a Shadertoy version, Stencyl code, and a global custom block that lets you easily use the shader and also sets some default values.  Shadertoy is what I've been using to create my shaders and then I port them to Stencyl.

To use the Shadertoy versions of the shaders, go to https://www.shadertoy.com/new and paste the code there.  Also select an image by clicking on iChannel0 on the bottom of the screen.

To use the Stencyl code (if not using the custom block), paste it into the "shader from code" block and set your shader attribute to it.  Then set all of the shader properties which you must do before using as I can't set default values from within the shader.  For more details: http://www.stencyl.com/help/view/shaders/

To use the global custom block, just import the attached scene behavior and apply the shader using the custom block.  You don't need to modify the behavior or attach it to any scenes.  The shader is automatically created when using the block and default values are set.

Note: if you meet all of the following three conditions:
1. Using any shader that uses x/y positions (such as Circle Light or Color Rings)
2. Your game is running in full screen
3. Your Stencyl build is older than b9287
Then you'll need to open plaf/haxe/lib/stencyl/1,00/com/stencyl/graphics/shaders/PostProcess.hx
Find the line that starts with this: GL.uniform2f(resolutionUsUniform
Replace the whole line with this: GL.uniform2f(resolutionUsUniform, Std.int(openfl.Lib.current.stage.stageWidth / (Engine.SCALE * Engine.screenScaleX)), Std.int(openfl.Lib.current.stage.stageHeight / (Engine.SCALE * Engine.screenScaleY)));

« Last Edit: April 08, 2017, 10:26:08 am by rob1221 »

rob1221

  • *
  • Posts: 9036
The first shader I have to share is a simple circle light shader.
https://gfycat.com/EsteemedRemarkableGreathornedowl

Here are the properties of the shader that you can set:
enabled (bool) -- This is used to control whether most of the shader code is run.  It's useful if you want to disable the shader without needing to reapply shaders.  If not using the custom block it defaults to false so you'll need to set it to true first.
centerX (float) -- This is the X center of the light circle.
centerY (float) -- This is the Y center of the light circle.
minDarkAmount (float) -- This is how dark the screen becomes just outside of the light radius (range: 0-1)
maxDarkAmount (float) -- This is the maximum darkness of the screen and is reached at the dark radius (range: 0-1)
lightRadius (float) -- This is the inner radius of the light that has no darkness at all.
darkRadius (float) -- This is the outer radius of the light at which point the max darkness is reached.
red (float) -- This is the red value of the light (range: 0-1)
green (float) -- This is the green value of the light (range: 0-1)
blue (float) -- This is the blue value of the light (range: 0-1)

Shadertoy Code
Code: [Select]
bool enabled = true;
float minDarkAmount = 0.4;
float maxDarkAmount = 0.8;
float lightRadius = 0.2;
float darkRadius = 0.5;
float red = 1.0;
float green = 1.0;
float blue = 1.0;

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{   
    float centerX = iMouse.x / iResolution.x;
float centerY = iMouse.y / iResolution.y;
   
    vec2 uv = vec2(fragCoord.xy / iResolution.xy);

if (!enabled)
{
fragColor = texture(iChannel0, uv);
return;
}
   
    vec2 res = iResolution.xy;
    float lightRadius2 = lightRadius * (res.x + res.y) * 0.5;
    float darkRadius2 = darkRadius * (res.x + res.y) * 0.5;
   
float xDiff = (centerX - uv.x) * res.x;
float yDiff = (centerY - uv.y) * res.y;
float dist = sqrt((xDiff * xDiff) + (yDiff * yDiff));
    float darkRange = maxDarkAmount - minDarkAmount;
    float darkAmount = minDarkAmount + min(darkRange * ((dist - lightRadius2) / (darkRadius2 - lightRadius2)), darkRange);
vec3 darkness = vec3(texture(iChannel0, uv).rgb * (1.0 - darkAmount));

if (dist < lightRadius2)
{
        vec3 colorMod = vec3(red, green, blue);
    fragColor = vec4(texture(iChannel0, uv).rgb * colorMod, 1.0);
}
else
    {
        if (red == 1.0 && green == 1.0 && blue == 1.0)
        {
            fragColor = vec4(darkness, 1.0);
        }
        else
        {
float darkRatio = (darkAmount - minDarkAmount) / darkRange;
    float newRed = red + ((1.0 - red) * darkRatio);
    float newGreen = green + ((1.0 - green) * darkRatio);
    float newBlue = blue + ((1.0 - blue) * darkRatio);
        vec3 colorMod = vec3(newRed, newGreen, newBlue);
        fragColor = vec4(darkness * colorMod, 1.0);
        }
    }
}

Stencyl Code
Code: [Select]
#ifdef GL_ES
precision mediump float;
#endif

varying vec2 vTexCoord;
uniform vec2 uResolution;
uniform vec2 uResolutionUs;
uniform sampler2D uImage0;

uniform bool enabled;
uniform float centerX;
uniform float centerY;
uniform float minDarkAmount;
uniform float maxDarkAmount;
uniform float lightRadius;
uniform float darkRadius;
uniform float red;
uniform float green;
uniform float blue;

void main()
{   
vec2 uv = vTexCoord;
vec4 tex = texture2D(uImage0, uv);

if (!enabled)
{
gl_FragColor = tex;
return;
}
   
vec2 res = uResolution.xy;
float scale = res.x / uResolutionUs.x;
float xDiff = (centerX / uResolutionUs.x - uv.x) * res.x;
float yDiff = ((1.0 - centerY / uResolutionUs.y) - uv.y) * res.y;
float dist = sqrt((xDiff * xDiff) + (yDiff * yDiff));
float darkRange = maxDarkAmount - minDarkAmount;
float lightRad = lightRadius * scale;
float darkRad = darkRadius * scale;
float darkAmount = minDarkAmount + min(darkRange * ((dist - lightRad) / (darkRad - lightRad)), darkRange);
vec3 darkness = vec3(tex.rgb * (1.0 - darkAmount));

if (dist < lightRad)
{
vec3 colorMod = vec3(red, green, blue);
gl_FragColor = vec4(tex.rgb * colorMod, 1.0);
}
else
{
if (red == 1.0 && green == 1.0 && blue == 1.0)
{
gl_FragColor = vec4(darkness, 1.0);
}
else
{
float darkRatio = (darkAmount - minDarkAmount) / darkRange;
float newRed = red + ((1.0 - red) * darkRatio);
float newGreen = green + ((1.0 - green) * darkRatio);
float newBlue = blue + ((1.0 - blue) * darkRatio);
vec3 colorMod = vec3(newRed, newGreen, newBlue);
gl_FragColor = vec4(darkness * colorMod, 1.0);
}
}
}

I know multiple lights is something that will be requested but I don't know how to do that at this time.

Jan 15 update: This shader now uses standard Stencyl x/y blocks for some properties, but some users may need to edit the engine to get it to work properly.  Check the first post for more details.

« Last Edit: April 08, 2017, 08:26:15 am by rob1221 »

rob1221

  • *
  • Posts: 9036
This shader wipes the RGB channels off or back on the screen from any direction.  It could be used with scene transitions.
https://gfycat.com/ShamelessSeparateIsopod

Here are the properties of the shader that you can set:
enabled (bool) -- This is used to control whether most of the shader code is run.  It's useful if you want to disable the shader without needing to reapply shaders.  If not using the custom block it defaults to false so you'll need to set it to true first.
counter (float) -- This controls the current position of the shader and it must be 0 or higher.  Set this under the always/updating event or in a timer.  Increase to add color channels and decrease to remove color channels.  The example GIF changes the counter by + or - 20 in the always/updating event.
direction (float) -- This is the direction (in degrees) that the shader will move when adding color channels.  The direction is reversed when removing color channels. (range: 0-360)

Shadertoy Code
Code: [Select]
bool enabled = true;
float counter = 0.0;
float direction = 0.0;

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
vec2 uv = fragCoord.xy / iResolution.xy;
    vec4 tex = texture(iChannel0, uv);
   
if (!enabled)
{
fragColor = tex;
return;
}

    vec2 res = vec2(iResolution.xy);
    float rad = radians(direction);
    float s = sin(rad + radians(180.0));
    float c = cos(rad);
    float start = res.x * abs(c) * 3.0 + res.y * abs(s) * 3.0;
   
    if (false) // for testing: true = transition out and false = transition in
    {
        counter = start - iGlobalTime * 500.0;
    }
    else
    {
        counter = iGlobalTime * 500.0;
    }
   
    float x = fragCoord.x;
    float y = fragCoord.y;
   
    if (direction >= 0.0 && direction < 90.0)
    {
        y = y - res.y;
    }
    else if (direction >= 90.0 && direction < 180.0)
    {
        x = x - res.x;
        y = y - res.y;
    }
else if (direction >= 180.0 && direction < 270.0)
    {
        x = x - res.x;
    }
    // no changes between 270.0 and 360.0

    float red = 0.0;
    float green = 0.0;
    float blue = 0.0;
   
    if (y * s + x * c < counter)
    {
        red = tex.r;
    }

    if (y * s + x * c < (counter - start / 3.0))
    {
        green = tex.g;
    }

if (y * s + x * c < (counter - start * 2.0 / 3.0))
    {
        blue = tex.b;
    }

     fragColor = vec4(red, green, blue, 1.0);
}

Stencyl Code
Code: [Select]
#ifdef GL_ES
precision mediump float;
#endif

varying vec2 vTexCoord;
uniform vec2 uResolution;
uniform sampler2D uImage0;

uniform bool enabled;
uniform float counter;
uniform float direction;

void main()
{
vec2 uv = vTexCoord;
    vec4 tex = texture2D(uImage0, uv);
   
if (!enabled)
{
gl_FragColor = tex;
return;
}

    vec2 res = uResolution;
    float rad = radians(direction);
    float s = sin(rad + radians(180.0));
    float c = cos(rad);
    float start = res.x * abs(c) * 3.0 + res.y * abs(s) * 3.0;
    float x = uv.x * res.x;
    float y = uv.y * res.y;
   
    if (direction >= 0.0 && direction < 90.0)
    {
        y = y - res.y;
    }
    else if (direction >= 90.0 && direction < 180.0)
    {
        x = x - res.x;
        y = y - res.y;
    }
else if (direction >= 180.0 && direction < 270.0)
    {
        x = x - res.x;
    }
    // no changes between 270.0 and 360.0

    float red = 0.0;
    float green = 0.0;
    float blue = 0.0;
   
    if (y * s + x * c < counter)
    {
        red = tex.r;
    }

    if (y * s + x * c < (counter - start / 3.0))
    {
        green = tex.g;
    }

if (y * s + x * c < (counter - start * 2.0 / 3.0))
    {
        blue = tex.b;
    }

gl_FragColor = vec4(red, green, blue, 1.0);
}

« Last Edit: April 08, 2017, 08:23:24 am by rob1221 »

Eriko

  • Posts: 151
Thanks, man. The collection keeps growing.  :D

rob1221

  • *
  • Posts: 9036
This shader creates colored rings that move away from a given center.  It's similar to the shockwave shader by laserhosen but with colors rather than distortion.
https://gfycat.com/SpryPhonyGossamerwingedbutterfly

Here are the properties of the shader that you can set:
enabled (bool) -- This is used to control whether most of the shader code is run.  It's useful if you want to disable the shader without needing to reapply shaders.  If not using the custom block it defaults to false so you'll need to set it to true first.
centerX (float) -- This is the X center of the rings.
centerY (float) -- This is the Y center of the rings.
counter (float) -- This controls the current position of the shader and it must be 0 or higher.  Set this under the always/updating event or in a timer.  Increase to expand the rings, but you can also decrease to reverse the ring movement.  The example GIF changes the counter by +6 in the always/updating event.
thickness (float) -- This is the thickness of each ring.
ringSpacing (float) -- This is the empty space between each ring if you have more than 1.
ringCount (float) -- This is the number of rings that will be created in total if you keep increasing the counter.
red (float) -- This is the red value and is either added to or multiplied with the current screen. (range: 0-2)
green (float) -- This is the red value and is either added to or multiplied with the current screen. (range: 0-2)
blue (float) -- This is the red value and is either added to or multiplied with the current screen. (range: 0-2)
multiply (bool) -- If true, the color values are multiplied with the screen values.  If false, the color values are added to the screen values.

Shadertoy Code
Code: [Select]
bool enabled = true;
float counter = 0.0;
float thickness = 25.0;
float ringSpacing = 100.0;
const float ringCount = 6.0;
float red = 1.6;
float green = 1.4;
float blue = 0.5;
bool multiply = false;

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    float centerX = iMouse.x;
float centerY = iMouse.y;
   
vec2 uv = fragCoord.xy / iResolution.xy;
vec4 tex = texture(iChannel0, uv);
   
if (!enabled)
{
fragColor = tex;
return;
}
   
vec2 fragPt = fragCoord;
counter = iGlobalTime;
float outerRad = counter * 300.0;
float innerRad = outerRad - thickness;
float xDiff = centerX - fragPt.x;
float yDiff = centerY - fragPt.y;
float dist = sqrt((xDiff * xDiff) + (yDiff * yDiff));
   
fragColor = tex;
   
for(float i = 0.0; i < ringCount; i++)
{
if (dist < outerRad - i * ringSpacing && dist > innerRad - i * ringSpacing)
{
if (multiply)
{
fragColor = vec4(tex.r * red, tex.g * green, tex.b * blue, 1.0);
}
else
{
fragColor = vec4(tex.r + red, tex.g + green, tex.b + blue, 1.0);
}
}
}
}

Stencyl Code
Code: [Select]
#ifdef GL_ES
precision mediump float;
#endif

varying vec2 vTexCoord;
uniform vec2 uResolution;
uniform vec2 uResolutionUs;
uniform sampler2D uImage0;

uniform bool enabled;
uniform float centerX;
uniform float centerY;
uniform float counter;
uniform float thickness;
uniform float ringSpacing;
uniform float ringCount;
uniform float red;
uniform float green;
uniform float blue;
uniform bool multiply;

void main()
{
vec2 uv = vTexCoord;
vec4 tex = texture2D(uImage0, uv);
   
if (!enabled)
{
gl_FragColor = tex;
return;
}
   
vec2 scale = uResolution.xy / uResolutionUs.xy;
float scaleAvg = (scale.x + scale.y) / 2.0;
vec2 fragPt = uv * uResolution;
float outerRad = counter * scaleAvg;
float innerRad = outerRad - thickness * scaleAvg;
float xDiff = centerX * scale.x - fragPt.x;
float yDiff = (uResolutionUs.y - centerY) * scale.y - fragPt.y;
float dist = sqrt((xDiff * xDiff) + (yDiff * yDiff));
   
gl_FragColor = tex;
   
for(float i = 0.0; i < ringCount; i++)
{
if (dist < outerRad - i * ringSpacing * scaleAvg && dist > innerRad - i * ringSpacing * scaleAvg)
{
if (multiply)
{
gl_FragColor = vec4(tex.r * red, tex.g * green, tex.b * blue, 1.0);
}
else
{
gl_FragColor = vec4(tex.r + red, tex.g + green, tex.b + blue, 1.0);
}
}
}
}

« Last Edit: April 08, 2017, 08:27:42 am by rob1221 »

rob1221

  • *
  • Posts: 9036
This shader displays two sets of bars that come in from opposite sides of the screen and cross each other to cover the screen.  It could be used as a scene transition.
https://gfycat.com/SimplePoliteHummingbird

Here are the properties of the shader that you can set:
enabled (bool) -- This is used to control whether most of the shader code is run.  It's useful if you want to disable the shader without needing to reapply shaders.  If not using the custom block it defaults to false so you'll need to set it to true first.
counter (float) -- This controls the current position of the shader and it must be 0 or higher.  Set this under the always/updating event or in a timer.  Increase to move the bars in and decrease to move the bars out.  The example GIF changes the counter by + or - 8 every 0.01 seconds.
direction (float) -- This is the direction (in degrees) that the first set of bars will move.  The second set moves in the opposite direction. (range: 0-360)
barWidth(float) -- This is the width of each bar.
red(float) -- This is the red value of the bars. (range: 0-1)
green(float) -- This is the green value of the bars. (range: 0-1)
blue(float) -- This is the blue value of the bars. (range: 0-1)

Shadertoy Code
Code: [Select]
bool enabled = true;
float counter = 0.0;
float direction = 45.0;
float barWidth = 20.0;
float red = 0.1;
float green = 0.2;
float blue = 0.4;

void mainImage(out vec4 fragColor, in vec2 fragCoord)
{
vec2 uv = fragCoord.xy / iResolution.xy;
    vec4 tex = texture(iChannel0, uv);
   
if (!enabled)
{
fragColor = tex;
return;
}

    vec2 res = vec2(iResolution.xy);
    float rad = radians(direction);
    float s = sin(rad + radians(180.0));
    float c = cos(rad);
    float start = res.x * abs(c) + res.y * abs(s);
   
    if (false) // for testing: false = transition out and true = transition in
    {
        counter = start - iGlobalTime * 500.0;
    }
    else
    {
        counter = iGlobalTime * 500.0;
    }
   
    float x = fragCoord.x;
    float y = fragCoord.y;
    float startX = x;
    float startY = y;
   
    if (direction >= 0.0 && direction < 90.0)
    {
        startY = y - res.y;
    }
    else if (direction >= 90.0 && direction < 180.0)
    {
        startX = x - res.x;
        startY = y - res.y;
    }
else if (direction >= 180.0 && direction < 270.0)
    {
        startX = x - res.x;
    }
    else
    {
        // no changes between 270.0 and 360.0
    }

    float offset = res.x * 2.0; // avoids inconsistent bars
    float rowFloat = abs((x + offset) * s - y * c) / barWidth;
    int row = int(floor(rowFloat));
   
    if (counter > abs(startX * c) + abs(startY * s) && row % 2 == 0)
    {
        fragColor = vec4(red, green, blue, 1.0);
    }
    else if (abs(res.x * c) + abs(res.y * s) - counter < abs(startX * c) + abs(startY * s) && row % 2 == 1)
    {
        fragColor = vec4(red, green, blue, 1.0);
    }
    else
    {
        fragColor = vec4(tex);
    }
}

Stencyl Code
Code: [Select]
#ifdef GL_ES
precision mediump float;
#endif

varying vec2 vTexCoord;
uniform vec2 uResolution;
uniform vec2 uResolutionUs;
uniform sampler2D uImage0;

uniform bool enabled;
uniform float counter;
uniform float direction;
uniform float barWidth;
uniform float red;
uniform float green;
uniform float blue;

void main()
{
vec2 uv = vTexCoord;
    vec4 tex = texture2D(uImage0, uv);
   
if (!enabled)
{
gl_FragColor = tex;
return;
}

    vec2 res = uResolution;
float scale = res.x / uResolutionUs.x;
    float rad = radians(direction);
    float s = sin(rad + radians(180.0));
    float c = cos(rad);
    float start = res.x * abs(c) + res.y * abs(s);
    float x = uv.x * res.x;
    float y = uv.y * res.y;
    float startX = x;
    float startY = y;
   
    if (direction >= 0.0 && direction < 90.0)
    {
        startY = y - res.y;
    }
    else if (direction >= 90.0 && direction < 180.0)
    {
        startX = x - res.x;
        startY = y - res.y;
    }
else if (direction >= 180.0 && direction < 270.0)
    {
        startX = x - res.x;
    }
    else
    {
        // no changes between 270.0 and 360.0
    }

    float offset = res.x * 2.0; // avoids inconsistent bars
    float rowFloat = abs((x + offset) * s - y * c) / (barWidth * scale);
    int row = int(floor(rowFloat));
   
    if (counter * scale > abs(startX * c) + abs(startY * s) && row % 2 == 0)
    {
        gl_FragColor = vec4(red, green, blue, 1.0);
    }
    else if (abs(res.x * c) + abs(res.y * s) - counter * scale < abs(startX * c) + abs(startY * s) && row % 2 == 1)
    {
        gl_FragColor = vec4(red, green, blue, 1.0);
    }
    else
    {
        gl_FragColor = vec4(tex);
    }
}