It's been a while since I've updated this blog, but here's a short update. I recently created a newsletter that you should subscribe to! I'll be sending out updates on my future tutorials via the newsletter!

Thanks for patience! I think it will be worth while!

You can find sneak peeks on my social media:

]]>

It's been too since I posted here and I think it's time for an update. I've been busy working on many Game Maker projects (I sometimes post about them on Twitter) which is why I stopped posting new tutorials. In 2019, I would like to change that and reboot my tutorial series. I've learned a lot in these past 5 years which will help me bring better content to you all.

These are my tentative feature plans for the new series:

I don't know for certain that I'll get to all of this, but if I'm able, then I intend to.

- New website domain and hosting.
- Fundamental tutorials for covering shader syntax.
- Example tutorials showing how to apply the fundamentals.
- Shader gallery with source code.
- Shader glossary with explanations of functions and variables.
- And perhaps HLSL tutorials. Let me know if you're interested!

I don't know for certain that I'll get to all of this, but if I'm able, then I intend to.

I would love to hear your thoughts and input on this project. If you have any ideas for tutorials or features, you can send them below and I'll see what I can do.

I've been writing these tutorials and examples in my spare time and I've made these tutorials for free without using ads. I will continue to create ad-free resources however I need help now to bring the new series. Please donate using the link below. Any little bit will help.

https://paypal.me/XDev

The new website will be shared on all my social media and on this website when it's ready.

In the meantime, feel free to join my Discord server. Let's talk about shaders and GM.

Thanks for reading!

]]>https://paypal.me/XDev

The new website will be shared on all my social media and on this website when it's ready.

In the meantime, feel free to join my Discord server. Let's talk about shaders and GM.

Thanks for reading!

To make our wind, we need a simple ripple effect. We will create a vec2 and call it "Coord". This will be used for the wind distortion. We will set it to "v_vTexcoord". Now you need to change the texture2D coordinates to our Coord vector. It should look like this:

*vec2 Coord = v_vTexcoord;*

gl_FragColor = v_vColour * texture2D( gm_BaseTexture, Coord);

This is a pass-through shader. If you run the shader applied to a sprite, it shouldn't change anything. To make a ripple we will use a cosine wave. We could do it like this:

* vec2 Coord = v_vTexcoord + vec2(cos(v_vTexcoord.y*30.0)/30.0,0);*

If you run this you will see this (before and after):

gl_FragColor = v_vColour * texture2D( gm_BaseTexture, Coord);

This is a pass-through shader. If you run the shader applied to a sprite, it shouldn't change anything. To make a ripple we will use a cosine wave. We could do it like this:

If you run this you will see this (before and after):

This is closer to what we want, but we need it to be animated. This works by shifting the X coordinates in a cosine wave shape. This makes a nice ripple. You can adjust the frequency by changing the first 30.0 and adjust the amplitude by changing the second 30.0. We'll come back to this at the end of the tutorial

We need to add a uniform float and we'll call it "Time" and we'll add it in the cosine like this:

*vec2 Coord = v_vTexcoord + vec2(cos(v_vTexcoord.y*30.0+Time*6.2831)/30.0,0);*

It is multiplied by 6.2831 (pi times two) because cosine uses angles in radians so that it does a full cycle every second.

We'll set the time uniform in GameMaker like this in the draw event:

* var uTime = shader_get_uniform(shdr_wind,"Time");*

shader_set(shdr_wind)

shader_set_uniform_f(uTime,current_time/1000)

draw_self()

* shader_reset()*

This will set "Time" to the number of seconds since the game started.

If you run this it will be animated. You may notice a problem where the base of the grass moves

just as much as the top.

It is multiplied by 6.2831 (pi times two) because cosine uses angles in radians so that it does a full cycle every second.

We'll set the time uniform in GameMaker like this in the draw event:

shader_set(shdr_wind)

shader_set_uniform_f(uTime,current_time/1000)

draw_self()

This will set "Time" to the number of seconds since the game started.

If you run this it will be animated. You may notice a problem where the base of the grass moves

just as much as the top.

So to fix the problem I just mentioned we will multiply cosine by the" (1.0-v_vTexcoord.y)". This will make the distortion decrease as it approaches the bottom of the sprite. Here is what I did:

*vec2 Coord = v_vTexcoord + vec2(cos(v_vTexcoord.y*30.0 + Time*6.2831)/30.0,0)*(1.0-v_vTexcoord.y);*

It looks much better now. This shader is currently proportion to the sprite size. If it's long it will stretch the waves, but if it's short it will shrink the waves. That is because the waves are based off of the v_vTexcoord which is between 0 and 1. To fix this we can use the position attribute from the vertex shader because it uses the vertex coordinates rather than the texture coordinates.

We need to add a varying vec2 called "v_vPosition" in the vertex and fragment shaders by the other varying vectors. Set it in the vertex shader like this:

*v_vPosition = in_Position.xy;*

And back to the fragment shader use this for the coordinates:

*vec2 Size = vec2(256,128);*

vec2 Wave = vec2(48,5);

vec2 Coord = v_vTexcoord +vec2(cos((v_vPosition.y/Wave.x+Time)*6.2831)*Wave.y,0)/Size*(1.0-v_vTexcoord.y);

**"Size" should be the size of the texture. Use a uniform if necessary. "Wave" is for adjusting the wave shape. The first number is the height of the wave and the second number is the amplitude.

This works by making a cosine wave of the chosen size with time added (multiplied by 6.2831 because of radians) and then multiplied by the amplitude; then divided by "Size" to get it between 0 and 1 and finally multiplied by the code I mentioned above to fix the bottom.

Thanks for reading.

DOWNLOAD EXAMPLE

Basic tutorials used:

#4 Vertex Shader

#2 Black and White Shader

#1 Shader Basics

]]>

It looks much better now. This shader is currently proportion to the sprite size. If it's long it will stretch the waves, but if it's short it will shrink the waves. That is because the waves are based off of the v_vTexcoord which is between 0 and 1. To fix this we can use the position attribute from the vertex shader because it uses the vertex coordinates rather than the texture coordinates.

We need to add a varying vec2 called "v_vPosition" in the vertex and fragment shaders by the other varying vectors. Set it in the vertex shader like this:

And back to the fragment shader use this for the coordinates:

vec2 Wave = vec2(48,5);

vec2 Coord = v_vTexcoord +

This works by making a cosine wave of the chosen size with time added (multiplied by 6.2831 because of radians) and then multiplied by the amplitude; then divided by "Size" to get it between 0 and 1 and finally multiplied by the code I mentioned above to fix the bottom.

Thanks for reading.

DOWNLOAD EXAMPLE

Basic tutorials used:

#4 Vertex Shader

#2 Black and White Shader

#1 Shader Basics

We will first try making a shader that flattens the 3d shapes. To do this we will simply set the z position to 0 for all vertices while leaving x and y positions unchanged. Make sure to comment out the normal attribute and use the default pass through fragment shader.

An important variable is "object_space_pos". It is the variable we can use for setting the position.

We will use the x and y components but will use 0 instead of z.

Here is the result:

An important variable is "object_space_pos". It is the variable we can use for setting the position.

We will use the x and y components but will use 0 instead of z.

Here is the result:

As you can see the shapes are properly flattened. Because you now know how to change the vertex position we'll try making water.

Before we begin with the water shader, we will need something to apply the shader to. We will make a plane instead of our basic shapes. In GameMaker, this may be a little tricky, because we need to generate one with enough vertices. Here is the code I used:

**water = d3d_model_create();**

cells = 64;//Number of cells

cell = 8;//Cell width

height = 32;//Water max height

for(var X = -cells/2;X<cells/2;X++)

{

d3d_model_primitive_begin(water,pr_trianglestrip)

for(var Y = -cells/2;Y<cells/2-1;Y++)

{

d3d_model_vertex(water,X*cell,Y*cell,height)

d3d_model_vertex(water,(X+1)*cell,Y*cell,height)

}

d3d_model_primitive_end(water)

}

This generates a plane model with many vertices to create higher quality water.

Apply your shader to this model instead of the basic shapes using "d3d_model_draw()".

cells = 64;//Number of cells

cell = 8;//Cell width

height = 32;//Water max height

for(var X = -cells/2;X<cells/2;X++)

{

d3d_model_primitive_begin(water,pr_trianglestrip)

for(var Y = -cells/2;Y<cells/2-1;Y++)

{

d3d_model_vertex(water,X*cell,Y*cell,height)

d3d_model_vertex(water,(X+1)*cell,Y*cell,height)

}

d3d_model_primitive_end(water)

}

This generates a plane model with many vertices to create higher quality water.

Apply your shader to this model instead of the basic shapes using "d3d_model_draw()".

We will use our knowledge to make detailed 3d water using perlin noise and height displacement. You should see the Vertex shader tutorial. We will be using the rand and noise functions to determine the height of the water for each vertex so put them in the vertex shader. Now we'll create a variable called height. This will be used to set the height of the water per vertex. Use this code:

**float height = noise(in_Position.xy/64.0)*0.4+noise(in_Position.xy/32.0)*0.3+noise(in_Position.xy/16.0)*0.2+noise(in_Position.xy/8.0)*0.1;**

That code generates a perlin noise pattern to make waves in the water.

Instead of setting the z position to 0 we'll set it to "height * in_Position.z". This makes it so we can determine the wave height by setting z position. And now finally we'll make a varying float called "v_vHeight" and pass our height variable through it. Now set the color in the fragment shader:

**gl_FragColor = vec4(mix(vec3(0.1,0.2,0.4),vec3(0.1,0.1,0.3),v_vHeight),1.0);**

This is the result:

]]>That code generates a perlin noise pattern to make waves in the water.

Instead of setting the z position to 0 we'll set it to "height * in_Position.z". This makes it so we can determine the wave height by setting z position. And now finally we'll make a varying float called "v_vHeight" and pass our height variable through it. Now set the color in the fragment shader:

This is the result:

Lighting shader usually use normals. As you may remember from the last tutorial, normals are the vectors which are perpendicular to each triangle. I will explain them in better detail.

Normal vector information can be received using the "in_Normal" attribute. This is only processed for each vertex (because it's in the vertex shader). So we'll create a varying vector called "v_vNormal" and we'll add it to both shaders (fragment and vertex). This way the normal can processed per pixel rather than per vertex. To test this shader we'll set the color to the normals so we can see what the normals look like. This is the code I used:

**gl_FragColor = vec4(v_vNormal*0.5+0.5,1.0);**

Because normals are in the range of -1 and 1 we'll multiply it be 0.5 and add 0.5 to put it in the range of 0 to 1 (since we can't see negative colors).

Here is what it looks like (downloads at the bottom):

Because normals are in the range of -1 and 1 we'll multiply it be 0.5 and add 0.5 to put it in the range of 0 to 1 (since we can't see negative colors).

Here is what it looks like (downloads at the bottom):

That is a simple scene with a sphere, cube and cone. You can see the normals with our shader. When you make your own models, you'll want to make sure the normals are correct.

Now we'll move on to a simple lighting shader.

Now we'll move on to a simple lighting shader.

Now that we get normals we'll make some simple directional lighting. To do this we will need to create a few vectors. We'll create a vec3 called "dir" and set it to something like "vec3( 0.6, 0.1, -1.0)".

This will be the direction of our light. Next create another vec3 called "col". This will be for the light color. We'll set this to a nice blue color (I used "vec3( 0.5, 0.8, 1.0)").

Now create a float called "lighting". Since this will be for the brightness of the pixel we'll need to calculate how much light it is receiving. We'll use the dot product function. This function finds the difference between two vectors in the range of -1 to 1 (if used properly). The vectors should be normalized. When you normalize a vector it sets the length of the vector to 1 while keeping it in proportion. If you do not do this to the vectors of a dot product, then it will return something in a range larger (or smaller) than -1 to 1 and in this case, ruin the lighting.

So now we'll set lighting to the dot product of "v_vNormal" and "dir" (make sure their both normalized). This will find the difference of the normal and the light (between -1 and 1).

To convert this to a visible value we multiply it by 0.5 and add 0.5. Now mulitply the "lighting" float and "col" vector to the texture color (making sure it is a vec4). Your code should look like this:

**vec3 dir = vec3( 0.6, 0.1, -1.0);//Light direction**

vec3 col = vec3( 0.5, 0.8, 1.0);//Light color

float lighting = (dot( normalize(v_vNormal), normalize(dir)) * 0.5 + 0.5);//Calculate

gl_FragColor = v_vColour * texture2D( gm_BaseTexture, v_vTexcoord) * vec4( lighting * col, 1.0);

This gets the desired light direction and color, then calculates it with "lighting" and finally applies it to the texture. This is the result (downloads below):

This will be the direction of our light. Next create another vec3 called "col". This will be for the light color. We'll set this to a nice blue color (I used "vec3( 0.5, 0.8, 1.0)").

Now create a float called "lighting". Since this will be for the brightness of the pixel we'll need to calculate how much light it is receiving. We'll use the dot product function. This function finds the difference between two vectors in the range of -1 to 1 (if used properly). The vectors should be normalized. When you normalize a vector it sets the length of the vector to 1 while keeping it in proportion. If you do not do this to the vectors of a dot product, then it will return something in a range larger (or smaller) than -1 to 1 and in this case, ruin the lighting.

So now we'll set lighting to the dot product of "v_vNormal" and "dir" (make sure their both normalized). This will find the difference of the normal and the light (between -1 and 1).

To convert this to a visible value we multiply it by 0.5 and add 0.5. Now mulitply the "lighting" float and "col" vector to the texture color (making sure it is a vec4). Your code should look like this:

vec3 col = vec3( 0.5, 0.8, 1.0);//Light color

float lighting = (dot( normalize(v_vNormal), normalize(dir)) * 0.5 + 0.5);//Calculate

gl_FragColor = v_vColour * texture2D( gm_BaseTexture, v_vTexcoord) * vec4( lighting * col, 1.0);

This gets the desired light direction and color, then calculates it with "lighting" and finally applies it to the texture. This is the result (downloads below):

As you can see, our nice scene now has lighting. Lets take this farther by creating a point light.

Now that you have a basic understanding of 3D lighting we can create point lights.

We want the point lights to have a radius. So we can add a float called "rad".

Since these lights cast shine in all directions, we'll need to change the "dir" vector.

This is actually simple to set, but we'll need the position of each pixel, so we'll add another varying vec3 and call it "v_vPosition". In the vertex shader, set it to "in_Position".

With the position we can calculate the direction from the light to each pixel. We simple put in the desired position for the point light (I used "vec3( 0.0, 32.0, -96.0)") and then subtract the pixel position ("v_vPoisiton"). That gets the direction for the light.

Now the last thing we'll add is a radius. This will make the light fade the farther the it is from the surface of each object. This is called Attenuation so we'll make a variable called "attenuation". Set this variable to the length of "dir" (so we get the distance from the light) and divide it by "rad" (so the brightness equals one when it is at the radius distance and 0 when at the light). This is opposite of what we want so we'll have it 1 minus are current code. To keep this from going negative we'll put the max function around our code with the second argument to 0 (which keeps it from going below 0).

The fragment code should look like this:

**vec3 dir = vec3( 0.0, 32.0, -96.0) - v_vPosition;//Light direction**

float rad = 128.0;//Light radius

vec3 col = vec3( 0.5, 0.8, 1.0);//Light color

float attenuation = max( 1.0 - length(dir)/rad, 0.0);//Calculate brightness

float lighting = (dot( normalize(v_vNormal), normalize(dir)) * 0.5 + 0.5) * attenuation;//Calculate lighting

gl_FragColor = v_vColour * texture2D( gm_BaseTexture, v_vTexcoord) * vec4( lighting * col, 1.0);

When running the code, the result should look like this (downloads at the bottom):

We want the point lights to have a radius. So we can add a float called "rad".

Since these lights cast shine in all directions, we'll need to change the "dir" vector.

This is actually simple to set, but we'll need the position of each pixel, so we'll add another varying vec3 and call it "v_vPosition". In the vertex shader, set it to "in_Position".

With the position we can calculate the direction from the light to each pixel. We simple put in the desired position for the point light (I used "vec3( 0.0, 32.0, -96.0)") and then subtract the pixel position ("v_vPoisiton"). That gets the direction for the light.

Now the last thing we'll add is a radius. This will make the light fade the farther the it is from the surface of each object. This is called Attenuation so we'll make a variable called "attenuation". Set this variable to the length of "dir" (so we get the distance from the light) and divide it by "rad" (so the brightness equals one when it is at the radius distance and 0 when at the light). This is opposite of what we want so we'll have it 1 minus are current code. To keep this from going negative we'll put the max function around our code with the second argument to 0 (which keeps it from going below 0).

The fragment code should look like this:

float rad = 128.0;//Light radius

vec3 col = vec3( 0.5, 0.8, 1.0);//Light color

float attenuation = max( 1.0 - length(dir)/rad, 0.0);//Calculate brightness

float lighting = (dot( normalize(v_vNormal), normalize(dir)) * 0.5 + 0.5) * attenuation;//Calculate lighting

gl_FragColor = v_vColour * texture2D( gm_BaseTexture, v_vTexcoord) * vec4( lighting * col, 1.0);

When running the code, the result should look like this (downloads at the bottom):

There's a simple light in the center of the screen which properly fades. You can easily add a uniform for changing light position, radius, direction, or color if you want, but that is up to the developer.

DOWNLOAD NORMALS EXAMPLE

DOWNLOAD DIRECTIONAL LIGHT EXAMPLE

DOWNLOAD POINT LIGHT EXAMPLE

]]>DOWNLOAD NORMALS EXAMPLE

DOWNLOAD DIRECTIONAL LIGHT EXAMPLE

DOWNLOAD POINT LIGHT EXAMPLE

In order to make a shader for 3D you will most likely need to no 3d information such as position and normals. In the Vertex Shader Tutorial I explained in briefly how to get the position with the "in_Position" vector (only in the vertex shader). In order to add this to the fragment shader you'll need a varying vec3 for position. Create one and call it "v_vPosition". Make sure it's in the fragment shader also.

To get the position in 3D we will simply use the Z component along with the X and Y components. You can also get the normals (which is essentially a vector perpendicular to the currently processed triangle) of the triangles as they are set (d3d_draw functions have the normals already). When you apply the shader to a 3D object then you can retrieve the normal which is useful for lighting.

To get the position in 3D we will simply use the Z component along with the X and Y components. You can also get the normals (which is essentially a vector perpendicular to the currently processed triangle) of the triangles as they are set (d3d_draw functions have the normals already). When you apply the shader to a 3D object then you can retrieve the normal which is useful for lighting.

For our first example we'll make a simple shader which makes all 3D objects below a certain height turn black. This is rather simple to do. In the fragment shader we can check if "v_vPosition.z" is less than your desired height, then set it to black if it is. Because the vertex shader is only run for every vertex it's not precise enough for this shader. That is why we passed it to the fragment shader so that we can check each pixel rather than each vertex. For my example I used:

**if (v_vPosition.z>0.0)**

{

gl_FragColor = vec4(0.0,0.0,0.0,1.0);

}

Here is a screenshot of a simple 3D demo scene:

{

gl_FragColor = vec4(0.0,0.0,0.0,1.0);

}

Here is a screenshot of a simple 3D demo scene:

As you can see the parts below 0 Z are black.

DOWNLOAD EXAMPLE

]]>DOWNLOAD EXAMPLE

Shaders on GLSL Sandbox have a few differences from shaders in GameMaker. Like on ShaderToy there are no textures most of the time. However GLSL Sandbox only supports frame buffers. If a shader uses theses, there will be a uniform sampler2D with the other uniforms. It can be named anything, but it is most often called "backbuffer" or "bb". Frame buffers must be made in GameMaker outside of the shader. These can be done with surfaces in GameMaker, but because these are very rarely used I will not get in too much detail.

Check out tutorial 4 if you haven't already for information about varying vectors. You will need to create a varying vec2 called "fragCoord". Then set it in the vertex shader to "in_Position.xy". You can also remove v_vColour and v_vTexcoord because they won' be used in this shader.

Some shaders also have a varying vec2 called "surfacePosition" (although it is not in this shader). This is used to make shaders where you can scroll and zoom. To make this in GameMaker you should another varying vector in vertex shader called "surfacePosition". Then you should be set like this:

**surfacePosition = in_Position.xy*Scroll.zw+Scroll.xy;**

"Scroll" will be a vec4 uniform that we'll add next. That code will zoom with z and w components and offset with the x and y.

Some shaders also have a varying vec2 called "surfacePosition" (although it is not in this shader). This is used to make shaders where you can scroll and zoom. To make this in GameMaker you should another varying vector in vertex shader called "surfacePosition". Then you should be set like this:

"Scroll" will be a vec4 uniform that we'll add next. That code will zoom with z and w components and offset with the x and y.

Make sure you see tutorial 5 part 2 for details about uniforms.

GLSL Sandbox shaders already have the uniforms setup. As long as you set them to the right values it should work fine. If the shader has "surfacePosition" then only thing you need to add is a vec4 uniform called "Scroll". This can be used in GameMaker to scroll and zoom.

GLSL Sandbox shaders already have the uniforms setup. As long as you set them to the right values it should work fine. If the shader has "surfacePosition" then only thing you need to add is a vec4 uniform called "Scroll". This can be used in GameMaker to scroll and zoom.

Here's a final summary of what we have learned:

**#ifdef GL_ES**

precision mediump float;

#endif

uniform float time;

uniform vec2 resolution;

void main( void ) {

vec2 uv = ( gl_FragCoord.xy / resolution.y );

vec3 color = vec3(fract(sin(dot(floor(uv.xy*32.0+time*2.0),vec2(5.364,6.357)))*357.536));

gl_FragColor = vec4( color, 1.0 );

**Converted GameMaker fragment code:**

**#ifdef GL_ES**

precision mediump float;

#endif

varying vec2 FragCoord;

uniform float time;

uniform vec2 resolution;

void main( void )

{

vec2 uv = ( FragCoord.xy / resolution.y );

vec3 color = vec3(fract(sin(dot(floor(uv.xy*32.0+time*2.0),vec2(5.364,6.357)))*357.536));

gl_FragColor = vec4( color, 1.0 );

}

Converted vertex 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) unused in this shader.

//attribute vec2 in_TextureCoord; // (u,v) unused in this shader.

varying vec2 FragCoord;

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;

FragCoord = in_Position.xy;

}

Here is the example file:

DOWNLOAD EXAMPLE

]]>- Change "gl_FragCoord" to "fragCoord"
- Add "fragCoord" and "surfacePosition" (if needed) to vertex shader
- Add "Scroll" vec4 uniform if needed
- Set all uniforms (may include time, resolution or mouse)

precision mediump float;

#endif

uniform float time;

uniform vec2 resolution;

void main( void ) {

vec2 uv = ( gl_FragCoord.xy / resolution.y );

vec3 color = vec3(fract(sin(dot(floor(uv.xy*32.0+time*2.0),vec2(5.364,6.357)))*357.536));

gl_FragColor = vec4( color, 1.0 );

precision mediump float;

#endif

varying vec2 FragCoord;

uniform float time;

uniform vec2 resolution;

void main( void )

{

vec2 uv = ( FragCoord.xy / resolution.y );

vec3 color = vec3(fract(sin(dot(floor(uv.xy*32.0+time*2.0),vec2(5.364,6.357)))*357.536));

gl_FragColor = vec4( color, 1.0 );

}

Converted vertex code:

//attribute vec3 in_Normal; // (x,y,z) unused in this shader.

//attribute vec4 in_Colour; // (r,g,b,a) unused in this shader.

//attribute vec2 in_TextureCoord; // (u,v) unused in this shader.

varying vec2 FragCoord;

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;

FragCoord = in_Position.xy;

}

Here is the example file:

DOWNLOAD EXAMPLE

Note that because the shaders are designed for Shadertoy.com, they won't always be fully optimized for GameMaker. It can often be best to just design a shader yourself. This tutorial may not cover all the differences, but it should help you convert most shaders from Shadertoy.com.

The shaders on Shadertoy.com don't need textures and most of them don't even use textures (my shader does not use any textures either). If a shader does use textures it will show the textures at the bottom right (at the iChannel0-iChannel3). In GameMaker: Studio, shaders must be applied to something, so most of the shaders designed for GameMaker, are applied to sprites, backgrounds, or in 3D they can be applied to planes. That means shaders in GameMaker must have at least one texture (although that texture can be a blank rectangle). Another difference is Shadertoy shaders recently started using fragColor for gl_FragColor. To remove errors simply change those back to gl_FragColor. Also "void mainImage( out vec4 fragColor, in vec2 fragCoord )" should be replaced with this "void main( void)".

Check out tutorial 4 if you haven't already for information about varying vectors. You will need to create a varying vec2 called fragCoord. Then set it in the vertex shader to "in_Position.xy". You can also remove v_vColour and v_vTexcoord because they won' be used in this shader.

To see all the uniforms you may need, click the "Shader inputs" menu above the shader code. This will show all the uniforms ShaderToy uses. You should copy all the uniforms the specific shader uses in to the shader in GameMaker. Make sure you see tutorial 5 part 2 for details about uniforms.

If a shader does not use textures, then you just apply the shader to a rectangle or blank texture. In order to get textures, you must use uniform samplers (unless it's just one texture). With one texture you could just use the base texture (which is whatever you applied it to). For now we won't worry about textures since we're not using them.

So to make things easier I'll make a list for you. So when converting shaders you'll most likely need to change all these things:

{

return fract(abs(sin(dot(n,vec3(4.3357,-5.8464,6.7645))*52.47))*256.75+0.325);

}

void mainImage( out vec4 fragColor, in vec2 fragCoord )

{

vec3 p = vec3(fragCoord.xy+vec2(iGlobalTime)*64.0,0.0);

float b = (rand(floor(p/64.0))*0.5+rand(floor(p/32.0))*0.3+rand(floor(p/16.0))*0.2);

fragColor = vec4(vec3(b*0.6),1.0);

} float rand(vec3 n)

Here's the converted fragment code:

**uniform float iGlobalTime;**

**varying vec2 fragCoord;**

float rand(vec3 n)

{

return fract(abs(sin(dot(n,vec3(4.3357,-5.8464,6.7645))*52.47))*256.75+0.325);

}

void main(void)

{

vec3 p = vec3(fragCoord.xy+vec2(iGlobalTime)*64.0,0.0);

float b = (rand(floor(p/64.0))*0.5+rand(floor(p/32.0))*0.3+rand(floor(p/16.0))*0.2);

gl_FragColor = vec4(vec3(b*0.6),1.0);

}

Now here is the vertex 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) unused in this shader.

//attribute vec2 in_TextureCoord; // (u,v) unused in this shader.

varying vec2 fragCoord;

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;

fragCoord = in_Position.xy;

}

Here's the example file:

DOWNLOAD EXAMPLE

For a challenge you can convert my "Cool Lake"shader.

]]>- Change "fragColor" to "gl_FragColor"

- Change "void mainImage( out vec4 fragColor, in vec2 fragCoord )" to "void main( void)"
- Create the varying vec2 called "fragCoord"
- Remove unneeded attribute vectors
- Add the needed uniforms including the samplers

{

return fract(abs(sin(dot(n,vec3(4.3357,-5.8464,6.7645))*52.47))*256.75+0.325);

}

void mainImage( out vec4 fragColor, in vec2 fragCoord )

{

vec3 p = vec3(fragCoord.xy+vec2(iGlobalTime)*64.0,0.0);

float b = (rand(floor(p/64.0))*0.5+rand(floor(p/32.0))*0.3+rand(floor(p/16.0))*0.2);

fragColor = vec4(vec3(b*0.6),1.0);

}

Here's the converted fragment code:

float rand(vec3 n)

{

return fract(abs(sin(dot(n,vec3(4.3357,-5.8464,6.7645))*52.47))*256.75+0.325);

}

void main(void)

{

vec3 p = vec3(fragCoord.xy+vec2(iGlobalTime)*64.0,0.0);

float b = (rand(floor(p/64.0))*0.5+rand(floor(p/32.0))*0.3+rand(floor(p/16.0))*0.2);

gl_FragColor = vec4(vec3(b*0.6),1.0);

}

Now here is the vertex code:

//attribute vec3 in_Normal; // (x,y,z) unused in this shader.

//attribute vec4 in_Colour; // (r,g,b,a) unused in this shader.

//attribute vec2 in_TextureCoord; // (u,v) unused in this shader.

varying vec2 fragCoord;

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;

fragCoord = in_Position.xy;

}

Here's the example file:

DOWNLOAD EXAMPLE

For a challenge you can convert my "Cool Lake"shader.

Shaders are for changing the color of pixels. It isn't used for anything more. Shaders can use textures or math to mix colors or blend them, but shaders can't easily be used for clickable or interactive objects in a scene. In general shaders are for recoloring or distorting. If you need textured shapes that can have gravity or motion (like rain for example), then you should use particles.

Particles are for moving and animated, textures or shapes that don't interact with objects or the scene. These are useful for effects like fire, rain, smoke, snow, etc. Blend modes can be applied to them also, so you can have additive blend (brightens the scene). However because particles aren't very interactive, then you may need objects.

Objects should be used for anything interactive. That means that it should be used for things that can collide with other objects and bounce for instance. These however should be used lightly, because the are often the slowest. You probably don't want to use this for rain that can collide unless you don't have much rain.

Shaders are designed for effects like scan lines, vignette or sepia tone. Particles are designed for rain, fire, smoke, etc. Objects are for anything interactive like a bouncing ball. Now this doesn't mean that you can't use shaders for interactive things, but the process is harder and may be slower.

]]>Uniforms are for getting values from outside of the shader, into the shader. Uniforms have many uses. One use is to take the coordinates of the mouse for example. Uniforms can be vectors, floats, float arrays, integers and integer arrays. Setting up uniforms is simple you just put "uniform" before your variables. Like this:

*uniform vec2 pos;*

We will add that code to our blur shader from the last tutorial. Put the code under the varying vectors. We will use these coordinates to tell where to center the radial blur.

We can use this code for the position:

varying vec4 v_vColour;

uniform vec2 pos;//x,y

const int Quality = 16;

void main()

{

vec4 Color;

float v;

for( float i=0.0;i<1.0;i+=1.0/float(Quality) )

{

v = 0.9+i*0.1;//convert "i" to the 0.9 to 1 range

Color += texture2D( gm_BaseTexture, v_vTexcoord*v+(pos)*(1.0-v));

}

Color /= float(Quality);

gl_FragColor = Color * v_vColour;

}

This code is just like the last one except with the position uniform is instead of 0.5. Now in order to run this code, we must add the uniforms in GameMaker.

To set these variables in GameMaker, you must get the uniform id.

In the Create event of the control object, put this code:

*upos = shader_get_uniform(shader,"pos");*

That code sets variable "upos" to the id for the uniform "pos" from "shader" (which is the name of our shader). Then, under "shader_set(shader)" add "shader_set_uniform_f(shader,upos)".

"shader_set_uniform_f()" is for floats or vectors. There are types for float arrays, integers, integer arrays, matrices, and matrix arrays, but for now we'll stick with floats.

The whole code should look similar to this:

*shader_set(shader)*

*shader_set_uniform_f(upos,mouse_x/room_width,mouse_y/room_height)*

*draw_self()//any draw code should work*

*shader_reset()*

What that code does, is it applies the shader, sets the uniform pos in the shader to the mouse coordinates (the coordinates are shrunk to a 0 to 1 range), then draws the sprite with the shader applied. When we run the game we get this result:

In the Create event of the control object, put this code:

That code sets variable "upos" to the id for the uniform "pos" from "shader" (which is the name of our shader). Then, under "shader_set(shader)" add "shader_set_uniform_f(shader,upos)".

"shader_set_uniform_f()" is for floats or vectors. There are types for float arrays, integers, integer arrays, matrices, and matrix arrays, but for now we'll stick with floats.

The whole code should look similar to this:

What that code does, is it applies the shader, sets the uniform pos in the shader to the mouse coordinates (the coordinates are shrunk to a 0 to 1 range), then draws the sprite with the shader applied. When we run the game we get this result:

This is the result when the mouse is at the bottom center. Moving the mouse works perfectly.

Next we'll make a motion blur. We will use a loop to get 16 pixels in line and average then to get the blur. Doing this in code is rather simple with the knowledge you have gained so far.

Here is my code:

**varying vec2 v_vTexcoord;**

varying vec4 v_vColour;

uniform vec2 pos;//x,y

const int Quality = 16;

void main()

{

vec4 Color;

for( float i=0.0;i<1.0;i+=1.0/float(Quality) )

{

Color += texture2D( gm_BaseTexture, v_vTexcoord+(0.5-pos)*i);

}

Color /= float(Quality);

gl_FragColor = Color * v_vColour;

}

Now what this does is it loops through a number times equal to quality setting "i" from 0 to 1 then it gets the position in a line based off of "pos". Here is a screenshot of the result:

Here is my code:

varying vec4 v_vColour;

uniform vec2 pos;//x,y

const int Quality = 16;

void main()

{

vec4 Color;

for( float i=0.0;i<1.0;i+=1.0/float(Quality) )

{

Color += texture2D( gm_BaseTexture, v_vTexcoord+(0.5-pos)*i);

}

Color /= float(Quality);

gl_FragColor = Color * v_vColour;

}

Now what this does is it loops through a number times equal to quality setting "i" from 0 to 1 then it gets the position in a line based off of "pos". Here is a screenshot of the result:

And there you have a working motion blur shader! It takes in position between 0 and 1.

And finally lets make the most used blur shader. Lets make a gaussian blur shader.

And finally lets make the most used blur shader. Lets make a gaussian blur shader.

This is done just like the motion blur shader, but in all directions. Since you can put a for statement inside another for statement we will be one loop to loop through all the directions and the other will loop through magnitudes. That means we'll need another Quality constant. We will call that one Directions. We'll also add a constant float called "Pi" and set it to "6.28318530718" (pi times 2) and change "pos" to "size" and make it a 3D vector (vec3). We'll use "size" for the width, height and radius where width and height are the textures height and width. Now add another loop with a float called "d" and it's range will be from 0.0 to "Pi". Here's how the code should look:

**varying vec2 v_vTexcoord;**

varying vec4 v_vColour;

uniform vec3 size;//width,height,radius

const int Quality = 6;

const int Directions = 12;

const float Pi = 6.28318530718;//pi * 2

void main()

{

vec2 radius = size.z/size.xy;

vec4 Color = texture2D( gm_BaseTexture, v_vTexcoord);

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);

}

}

Color /= float(Quality)*float(Directions)+1.0;

gl_FragColor = Color * v_vColour;

}

This code sets radius to 0 to 1 sizes to match with the coordinates.

next it loops through the directions in radians with "d" (0 to "Pi") then loops through the magnitude with "i" (0 to 1). Each loop it gets the color of the texture at that pixel and finally it averages the total after the loops. Next change the "upos" to "usize" and "pos" to "size". Then when setting the uniform set it to 512, 512, and 16 (width, height and radius in pixels).

The final result should look like this:

varying vec4 v_vColour;

uniform vec3 size;//width,height,radius

const int Quality = 6;

const int Directions = 12;

const float Pi = 6.28318530718;//pi * 2

void main()

{

vec2 radius = size.z/size.xy;

vec4 Color = texture2D( gm_BaseTexture, v_vTexcoord);

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);

}

}

Color /= float(Quality)*float(Directions)+1.0;

gl_FragColor = Color * v_vColour;

}

This code sets radius to 0 to 1 sizes to match with the coordinates.

next it loops through the directions in radians with "d" (0 to "Pi") then loops through the magnitude with "i" (0 to 1). Each loop it gets the color of the texture at that pixel and finally it averages the total after the loops. Next change the "upos" to "usize" and "pos" to "size". Then when setting the uniform set it to 512, 512, and 16 (width, height and radius in pixels).

The final result should look like this:

Give yourself a pat on the back for completing three useful shaders!

Here are the examples:

DOWNLOAD RADIAL EXAMPLE

DOWNLOAD MOTION EXAMPLE

DOWNLOAD GAUSSIAN EXAMPLE

]]>Here are the examples:

DOWNLOAD RADIAL EXAMPLE

DOWNLOAD MOTION EXAMPLE

DOWNLOAD GAUSSIAN EXAMPLE