Shader Basics – Getting Started

Hey! In this guide, you’ll learn the basics of shaders in GameMaker. So let’s get started!

Shaders use OpenGL which is independent of GameMaker and GML, so the shader code should work with all GameMaker versions that support shaders.


Parts of a Shader

There are two parts in a shader: the vertex shader, and the fragment shader. For a start, the fragment shader is the only part that you need to learn – so we’ll leave the vertex shader as is.

So in this tutorial, whenever I mention “shaders”, I am essentially talking about the fragment shader.


What shaders do

A sprite is stored in texture pages as a texture. When a sprite is drawn, its texture from the texture page is drawn on the screen.

When you use a shader to draw a sprite, you basically control the process of getting the sprite data from the texture page.

A fragment shader works with a single pixel. If you write a shader, you write it to manipulate a single pixel (its position, color, etc.). That shader then runs for each pixel. This way, it manipulates the whole texture: pixel-by-pixel.

A surface is a texture too. To a shader, it is the same as a sprite. The main difference is that while a texture page may have multiple sprites/sub-images, a surface has a separate texture page to itself.


Using a Shader

Using a shader is simple: You use shader_set(shader), draw what you want to, and then use shader_reset().

5.PNG


Basic Fragment Shader

If you create a shader, its vertex (vsh) and fragment (fsh) shaders will open. If you open the fragment shader, you’ll see something like this:

1.PNG

This is a simple shader that draws the sprite texture normally.

Area A: The area above void main() is where you initialize all the variables and vectors.

Area B: The area inside void main() is where you manipulate the pixel data and pass a color to be drawn.


Area A

Types

varying signifies that the variable being declared is getting its value from the vertex shader.

A float is a variable holding numerical data that can have decimals.

An int is a variable holding only integer values.

vec2, vec3 and vec4 are 2D, 3D and 4D vectors, respectively. They are nothing complicated: They’re just groups of floats. So a vec2 contains two floats, a vec3 contains three, and so on.

Variables

v_vTexcoord is a 2D vector which contains the (x and y) position of the pixel on the texture page. It lies in the range of 0-1.

v_vColour is a 4D vector which contains the blend color data for the sprite. It’s the color set through image_blend or draw_sprite_ext()‘s “colour” argument.

It might look like these are empty variables being initialized, but they are actually getting their values from the vertex shader.


Area B

2.PNG

gl_FragColor is a 4D vector that is the final output of the shader for a pixel. It has four values: (R, G, B and A), all in the range of 0-1. They stand for red, green, blue and alpha, respectively.

texture2D() gets the colour of a pixel from a texture. Its first argument is the texture (which here is gm_BaseTexture, the texture of the sprite being drawn) and its second argument is the position (which here is v_vTexcoord, the position of the pixel being drawn).

As told above, v_vColour is the image blend colour. It will be multiplied with the pixel colour to get the final colour. Normally, if an image blend colour is not set, there would be no effect.


gl_FragColor

Setting this 4D vector will change the colour of a sprite.

Its components can be separately accessed or edited by using r, g, b, a after its name and a point. So, if you wanted to change the red value of the pixel, you would do this:

gl_FragColor.r = 1.0;

This will set the red value of the color to 1.

gl_FragColor.a = 0.5;

This will set the alpha to 0.5, making the sprite half transparent.

You can also access multiple components:

gl_FragColor.rgb = vec3(0.0, 0.0, 0.0);

The vec3() function will return a 3D vector with the values inside its parentheses. Here, we’re setting the red, green and blue values to zero, so it would result in a black colour.

When dealing with floats, it's important to use decimal numbers.
So, instead of typing "1", we type "1.0".

v_vTexcoord

You can manipulate this 2D vector to change the pixel position.

If the current position is, say, (0.4, 0.4), and you add 0.1 to it, making it (0.5, 0.5), it will get that pixel from the texture, instead of the pixel at (0.4, 0.4). So by changing this vector, it is not the final drawing position of the pixel that is changed, but the original position of the pixel to be used from the texture.


Uniforms

Uniforms are variables that are passed from the object drawing the sprite. This way a dynamic value can be passed to the shader.

As an example, I will make an uniform and pass the current_time value to it.

To create an uniform, you initialize it in Area A:

3.PNG
Line #7

Here I’ve initialized a float uniform called “time”.

Then you get its ID in a Create event, which can be used later to pass a value to it:

4.PNG

Then you pass a value to the uniform after you set the shader:

6.PNG

This way, the shader will be able to use any value you pass.


Conclusion

Now that you know how shaders work, check out my tutorial about making a water shader:

If you want water like this in your game with minimal effort, check out my top-down and platformer water assets.

Happy dev’ing!

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s