• Hey Guest! Ever feel like entering a Game Jam, but the time limit is always too much pressure? We get it... You lead a hectic life and dedicating 3 whole days to make a game just doesn't work for you! So, why not enter the GMC SLOW JAM? Take your time! Kick back and make your game over 4 months! Interested? Then just click here!

GameMaker 【SOLVED】How to realize a gradient shader

Zhanghua

Member
I want to deal the sprite image by the effect as following:
Top is dark, and Bottom is bright, and Middle is linear change.

So I did that by this way out of my prediction.

shd_Ice.VSH:

Code:
//
attribute vec3 in_Position;                  // (x,y,z)
//attribute vec3 in_Normal;                  // (x,y,z)     unused in this shader.
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;
    gl_Position = gm_Matrices[MATRIX_WORLD_VIEW_PROJECTION] * vec4(in_Position, 1.0);
   
    v_vColour = in_Colour;
    v_vTexcoord = in_TextureCoord;
}

shd_Ice.FSH

Code:
//
varying vec2 v_vTexcoord;
varying vec4 v_vColour;


void main()
{
    vec2 uv = v_vTexcoord;
    vec4 colour = texture2D( gm_BaseTexture, v_vTexcoord );
    colour.r = (colour.r + uv.y )/2.0;
    colour.g = (colour.g + uv.y )/2.0;
    colour.b = (colour.b + uv.y )/2.0;
    gl_FragColor = vec4(colour.rgb, texture2D(gm_BaseTexture,uv).a);

}


Draw Event

Code:
shader_set(shd_Ice);
draw_self();
shader_reset();

So, I'm confused by the relative position to draw.....
 
I hope I understood your question or all this work is for nothing :)

If you shade a surface or a sprite on a seperate texture page you can use a passthrough vertex shader and this as a fragment shader:

Code:
varying vec2 v_vTexcoord;
varying vec4 v_vColour;

void main()
{
    vec4 col = v_vColour * texture2D( gm_BaseTexture, v_vTexcoord );
 
   col.rgb = col.rgb * v_vTexcoord.y;

   gl_FragColor = col;
}
Because v_vTexcoord will interpolate from 0 to 1.

-----------------------------------------

However as soon as you use this with a sprite on a mixed texture page you need to compensate the uv's.

There's at least two ways of doing this. Version 1:

Create Event:
Code:
sprite = spr_foto;

shader = shd_dark_top;

u_v = shader_get_uniform(shader, "v");
uvs = sprite_get_uvs(sprite, 0);
Draw Event:
Code:
shader_set(shader);
shader_set_uniform_f(u_v, uvs[1], uvs[3]);
draw_sprite(sprite, 0, x, y);
shader_reset();
Vertex Shader:
Code:
//
// Simple darken top shader - vertex
//
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;

varying float v_normalized;
uniform vec2 v;


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;
 
   v_normalized = (v_vTexcoord.y - v[0]) / (v[1] - v[0]);
}
Fragment Shader:
Code:
//
// Simple darken top shader - fragment
//
varying vec2 v_vTexcoord;
varying vec4 v_vColour;

varying float v_normalized;

void main()
{
    vec4 col = v_vColour * texture2D( gm_BaseTexture, v_vTexcoord );
 
   col.rgb = col.rgb * v_normalized;
 
   gl_FragColor = col;
}
Here we passed in the uvs of the sprite and normalized them to 0-1 and pass them as interpolated values into the fragment shader. In the fragment shader we use the normalized v instead of the v_vTexoord because v_vTexcoord will not range from 0-1.
-----------------------------------------

And Version 2:

Create Event:
Code:
sprite = spr_foto;

shader = shd_dark_top;

shader_enable_corner_id(true);
Draw Event:
Code:
shader_set(shader);
draw_sprite(sprite, 0, x, y);
shader_reset();
Vertex Shader:
Code:
//
// Simple darken top shader - vertex
//
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;

varying float v_normalized;

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;
 
   v_normalized = abs(mod(in_Colour.b * 255.0, 2.0));

}
Fragment Shader: Same as in Version 1

Here we use corner id's instead of uvs.
abs(mod(in_Colour.b * 255.0, 2.0)); will return an interpolated value from 0 to 1 on the y axis as well. I can't tell you why because I simply don't understand why. II just know it works (credits to flyingsaucerinvasion for this btw).

I re-wrote some of the code in the forum text editor instead of GMS so I hope I didn't make a mistake. Feel free to ask if you got any questions about the code or if something isn't working.
 

Zhanghua

Member
I hope I understood your question or all this work is for nothing :)

If you shade a surface or a sprite on a seperate texture page you can use a passthrough vertex shader and this as a fragment shader:

Code:
varying vec2 v_vTexcoord;
varying vec4 v_vColour;

void main()
{
    vec4 col = v_vColour * texture2D( gm_BaseTexture, v_vTexcoord );
 
   col.rgb = col.rgb * v_vTexcoord.y;

   gl_FragColor = col;
}
Because v_vTexcoord will interpolate from 0 to 1.

-----------------------------------------

However as soon as you use this with a sprite on a mixed texture page you need to compensate the uv's.

There's at least two ways of doing this. Version 1:

Create Event:
Code:
sprite = spr_foto;

shader = shd_dark_top;

u_v = shader_get_uniform(shader, "v");
uvs = sprite_get_uvs(sprite, 0);
Draw Event:
Code:
shader_set(shader);
shader_set_uniform_f(u_v, uvs[1], uvs[3]);
draw_sprite(sprite, 0, x, y);
shader_reset();
Vertex Shader:
Code:
//
// Simple darken top shader - vertex
//
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;

varying float v_normalized;
uniform vec2 v;


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;
 
   v_normalized = (v_vTexcoord.y - v[0]) / (v[1] - v[0]);
}
Fragment Shader:
Code:
//
// Simple darken top shader - fragment
//
varying vec2 v_vTexcoord;
varying vec4 v_vColour;

varying float v_normalized;

void main()
{
    vec4 col = v_vColour * texture2D( gm_BaseTexture, v_vTexcoord );
 
   col.rgb = col.rgb * v_normalized;
 
   gl_FragColor = col;
}
Here we passed in the uvs of the sprite and normalized them to 0-1 and pass them as interpolated values into the fragment shader. In the fragment shader we use the normalized v instead of the v_vTexoord because v_vTexcoord will not range from 0-1.
-----------------------------------------

And Version 2:

Create Event:
Code:
sprite = spr_foto;

shader = shd_dark_top;

shader_enable_corner_id(true);
Draw Event:
Code:
shader_set(shader);
draw_sprite(sprite, 0, x, y);
shader_reset();
Vertex Shader:
Code:
//
// Simple darken top shader - vertex
//
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;

varying float v_normalized;

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;
 
   v_normalized = abs(mod(in_Colour.b * 255.0, 2.0));

}
Fragment Shader: Same as in Version 1

Here we use corner id's instead of uvs.
abs(mod(in_Colour.b * 255.0, 2.0)); will return an interpolated value from 0 to 1 on the y axis as well. I can't tell you why because I simply don't understand why. II just know it works (credits to flyingsaucerinvasion for this btw).

I re-wrote some of the code in the forum text editor instead of GMS so I hope I didn't make a mistake. Feel free to ask if you got any questions about the code or if something isn't working.

God Like U!!!
I don't reckon that it need to set the sprite "seperate texture page", Tkank you very much.

If not check the "seperate texture page", the all sub-sprite will be regarded as "a whole image", coordinate will be error yet....

Tks a lot to U!
 
Last edited:
you're welcome.

The other two examples have the advantage that the sprite does not need to be on a seperate texture page and thus might not need a texture swap depening on how you control your drawing order. Especially on mobile devices I wouldn't se the sprite on a seperate texture unless I absolutely have to..
 
Top