Shaders Creating a shadow shader

Vintendi

Member
I'm not new to game maker but definetly new to shaders. I am trying to create a shadow effect for my top down game. Currently I just have two sprites, one for the world and one for the shadow. I don't want to create 2 sprites per game objects.

What I want the shader to do:
Turn the current sprite black and reduce the alpha to 65%, apply a blur effect and fade the top of the sprite.

sprite example:


shadow example:


Overall example:

 

sp202

Member
Decided to write a shader myself too, here's what I've got:

upload_2019-1-14_21-32-15.png

The fragment shader:
Code:
varying vec2 v_vTexcoord;
varying vec4 v_vColour;
uniform vec3 size;//width,height,radius
const int Quality = 8;
const int Directions = 16;
const float Pi = 6.28318530718;//pi * 2
void main()
{
    vec2 radius = size.z/size.xy;
    radius+=radius*(1.-v_vTexcoord.y);
    vec4 Color = texture2D( gm_BaseTexture, v_vTexcoord).a*vec4(0.,0.,0.,1.);
    for( float d=0.0;d<Pi;d+=Pi/float(Directions) )
    {
        for( float i=1.0/float(Quality);i<=1.0;i+=1.0/float(Quality) )
        {
                Color += texture2D( gm_BaseTexture, v_vTexcoord+vec2(cos(d),sin(d))*radius*i).a*vec4(0.,0.,0.,1.);
        }
    }
    Color /= float(Quality)*float(Directions)+1.0;
    gl_FragColor =  Color *  v_vColour;
    gl_FragColor.a*=(v_vTexcoord.y);
}
What I suggest you do is to crank up quality as high as you can, pre-render sprites using the shader, then use those sprite for shadows.
 
Last edited:

Vintendi

Member
I made an attempt at it by modifying a blur shader and mixing it with a script to draw sprites skewed and some surfaces....

View attachment 22810

https://drive.google.com/open?id=1fHUJnglnRGl2YbBKKYp4lKhaNKvPUO9A

I don't know anything about shaders either so this isn't great but the grass texture helps hide the imperfections. Set the background color to a solid to see what I mean. But overall this might help you get started.
Thanks, it's better than anything i could achieve
 

Vintendi

Member
Decided to write a shader myself too, here's what I've got:

View attachment 22811

The fragment shader:
Code:
varying vec2 v_vTexcoord;
varying vec4 v_vColour;
uniform vec3 size;//width,height,radius
const int Quality = 8;
const int Directions = 16;
const float Pi = 6.28318530718;//pi * 2
void main()
{
    vec2 radius = size.z/size.xy;
    radius+=radius*(1.-v_vTexcoord.y);
    vec4 Color = texture2D( gm_BaseTexture, v_vTexcoord).a*vec4(0.,0.,0.,1.);
    for( float d=0.0;d<Pi;d+=Pi/float(Directions) )
    {
        for( float i=1.0/float(Quality);i<=1.0;i+=1.0/float(Quality) )
        {
                Color += texture2D( gm_BaseTexture, v_vTexcoord+vec2(cos(d),sin(d))*radius*i).a*vec4(0.,0.,0.,1.);
        }
    }
    Color /= float(Quality)*float(Directions)+1.0;
    gl_FragColor =  Color *  v_vColour;
    gl_FragColor.a*=(v_vTexcoord.y);
}
What I suggest you do is to crank up quality as high as you can, pre-render sprites using the shader, then use those sprite for shadows.
That's pretty much perfect thanks!
 

Vintendi

Member
W
Decided to write a shader myself too, here's what I've got:

View attachment 22811

The fragment shader:
Code:
varying vec2 v_vTexcoord;
varying vec4 v_vColour;
uniform vec3 size;//width,height,radius
const int Quality = 8;
const int Directions = 16;
const float Pi = 6.28318530718;//pi * 2
void main()
{
    vec2 radius = size.z/size.xy;
    radius+=radius*(1.-v_vTexcoord.y);
    vec4 Color = texture2D( gm_BaseTexture, v_vTexcoord).a*vec4(0.,0.,0.,1.);
    for( float d=0.0;d<Pi;d+=Pi/float(Directions) )
    {
        for( float i=1.0/float(Quality);i<=1.0;i+=1.0/float(Quality) )
        {
                Color += texture2D( gm_BaseTexture, v_vTexcoord+vec2(cos(d),sin(d))*radius*i).a*vec4(0.,0.,0.,1.);
        }
    }
    Color /= float(Quality)*float(Directions)+1.0;
    gl_FragColor =  Color *  v_vColour;
    gl_FragColor.a*=(v_vTexcoord.y);
}
What I suggest you do is to crank up quality as high as you can, pre-render sprites using the shader, then use those sprite for shadows.
what value did you use for the radius, anything other than 0 renders the sprite invisible
 

sp202

Member
This is what I'm doing in GML:
Code:
//CREATE EVENT

surf=-1

//DRAW EVENT

border=10

if !surface_exists(surf)
{
    surf=surface_create(sprite_width+border,sprite_height+border)
}

surface_set_target(surf)
draw_sprite(sprite_index,0,(sprite_width+border)/2,(sprite_height+border)/2)
surface_reset_target()
shader_set(shdShadows)
shader_set_uniform_f(shader_get_uniform(shdShadows,"size"),sprite_width+border,sprite_height+border,10)
draw_surface(surf,x-surface_get_width(surf)/2,y-surface_get_height(surf)/2)
shader_reset()
I suggest you draw the shader output to another surface, use sprite_create_from_surface and then use that sprite.
 
Top