Shaders are often used to create beautiful graphical effects in games. They are also among the most advanced features offered by GameMaker, so it is necessary that you have a basic understanding of programming and how GameMaker works before getting started with them.
The vertex shader is executed first, and as we explained above, it deals with vertices. It is used to calculate positions, normals, and texture coordinates. These shaders are not particularly useful in 2D, since every sprite is usually a square, but it can be used to do some skewing, scaling, etc... It becomes much more useful in 3D for lighting calculations and mesh deformations. Fragment shaders are much more interesting and are what will be covered mostly here, since the fragment shader is where we get information about our textures and can tweak the final color of each pixel in our image.
If you have created a shader in GameMaker, you might have noticed the following keywords in the default pass-through shader. These keywords help the shader understand the purpose and scope of each variable:
You'll also see the use of vec as a keyword. This is used to identify a vector variable in the shader and you'll soon see that vectors are very important when working with shaders. That is why they are implemented as a base type in GLSL. If you are unfamiliar with them, they are a mathematical term represented as a matrix with only one column. In programming, we usually represent them as an array where the number of components corresponds to the dimension. Two and three-dimensional vectors are often used for positions, texture coordinates, or colors without an alpha channel, while four-dimensional ones are used for colors with an alpha channel. We can also specify if they hold booleans, integers, or floating point values. The syntax to declare a vector is this:
To initialize them, we can use the constructor to create the vector. You need to provide the same number of values as the length of the vector, but you can mix and match scalars and smaller vectors to reach the target length. Here are some examples of this:
This uses the component names inside the vector to access them. You can use x, y, z, or w, to get the first, second, third, or fourth components, respectively. We refer to this method as swizzling because the following syntax is also valid:
When you create a shader in GameMaker, it will open two files for you: a vertex shader (.vsh) and a fragment shader (.fsh). This is the most basic shader you can make, which takes a sprite, reads the texture, and colors each pixel with that color. If you specify vertex colors when drawing, those colors will blend with the texture.
Outside of the main function, we see some variable declarations and their qualifiers. The attributes are given to us by GameMaker. The varying ones are created by the user to pass that information over to the fragment shader. Inside the main function, we have the calculations to find the screen position of the vertex:
This shader should be left alone if you are not planning to play with vertex positions and it will not be used in any of the examples given below because all the effects shown will be created using the fragment shader.
As explained before, the idea behind a fragment shader is to return the color of the current pixel. This is done by assigning the variable gl_FragColor the final color value. The texture2D function takes a texture and a vec2 with the UV coordinates you want to check in that texture, which returns a vec4 with the color. In the pass through shader, all we are doing is grabbing the color of the texture in the coordinate of this pixel and multiplying it by the color of the vertex associated with this pixel.
As you might have guessed, this does not visually changing anything, as this is a simple pass-through shader. However the sections below outline some simple steps you can take to modify this and change the way the sprite will be drawn. Each of the section shows a different shader that you can create and use in your projects, explaining the steps required to create them and why we are doing things the way we are.
That's the end of this short guide and you should now have a better understanding of how shaders work and some of the uses they can be put to. You should take your time to play with the shaders you've created following this guide, and try to experiment with them do other things - how about creating a blur shader, or a shader that makes a gameboy-style monochrome screen? - since shaders are an incredibly powerful tool for adding visual complexity and style to your games.
Welcome to GM Shaders!
These shader tutorials were carefully written with beginners and amateurs in mind, however, this can be a difficult topic. I advise taking your time when reading through them and take as many breaks as you need. They are designed to be comprehensive, yet concise which means you may not want to skip over anything unless suggested, or you'll miss something important! I avoid repeating myself wherever possible to keep these short. Throughout the tutorials, you will find highlighted text which contains additional information. Thanks for reading!
This first tutorial covers the basics of shaders in GameMaker Studio. Make sure you have an understanding of coding in GML as I won't explain it here. If you are already familiar with the purpose and setup of shaders, then feel free to skip to the next tutorial.
Shaders are used to produce all kinds of graphical and visual effects for 2D or 3D. They are perfect for enhancing any game's visuals. You can use them for anything from color effects like grayscale, sepia tone to blur effects, to lighting effects like normal maps, shadows to water reflections/refractions, shockwaves, wind, and much more! Let's get started from the very beginning by looking at texture pages in GM.
Before we get to the shaders, I want to explain a bit about texture pages (also known as a texture atlas) and how draw_sprite() works in GM. First, when you run your game, all textures including, sprites, fonts, are placed on texture pages. In summary, a texture page takes trimmed-down sprites and fits as many as possible on a single page. You can read more about texture pages here. Additionally, you can assign sprites to specific texture pages in the settings or use the "Separate Texture Page" option to put one on its own texture page. That means drawing a sprite is actually just drawing a part of a larger texture page: On the left you can see the texture page, on the right is how it looks in game. Sprites by JstFredrik
When you draw a sprite, it is actually drawing two triangles together which form a rectangle (quad). Each vertex carries information such as its position, texture coordinates, and color. So the vertices' positions are in the corners of the sprite. The texture coordinates of the whole texture page range from 0 to 1. So 0,0 is the top-left corner of the texture page and 0,1 is the bottom-left corner. Typically a sprite's texture coordinates will be somewhere in the middle of the texture page.
When you use draw_sprite_ext() it sets the color and alpha of all the vertices in the sprite. If you've used vertex buffers or draw_vertex_* functions you'll probably know that you can change the colors of individual vertices and you can use this to create gradients and other effects. Understanding this helps with shaders because these are the inputs the shaders have to work with.
Shaders are graphical programs run on your GPU that handle drawing of sprites, surfaces, vertex buffers, etc. GM supports two types of shaders: the vertex shader and fragment shader (sometimes called pixel shaders).
When you don't apply any shader, GM uses a default "passthrough" shader. This passthrough shader outputs the texture with the draw color and alpha the way you're used to. It also handles things such as alpha testing and fog/lights in 3D, but that is for another time.
Shaders process all vertices simultaneously and then all the pixels. This means that any conditional effect (dependent on a specific area, time or any other variable) will perform slower since all vertices/pixels use the same code despite varying results.
Right-click the shaders folder and select "Create Shader" or press ALT+A. I'll title mine "shd_passthrough" because this is going to be just another (simplified) passthrough shader. If you right-click the shader in the resource tree, you have three options to choose from:
I've gone through the default vertex and fragment shader, but I replaced the comments with my own. This will give a basic explanation of what each line does, but don't worry about the details just yet. Vertex shader code quickly explained:
Okay, so now you have a shader, but how do you use it? Well, you just have to set it, draw anything that you want the shader effect to apply to (surfaces, sprites, etc) and then reset it when you're done to resume normal drawing. Here's an example:
This way you have full control what each shader applies to. Sometimes I need separate shaders for separate objects (e.g. a wind shader only for foilage) and using draw_self with a shader works neatly.
Shaders also work great with GM's surfaces especially for effects that apply across multiple objects or screen effects (e.g. motion blur or bloom).
Congrats on creating and examining a shader! For many, matrices and vertex positions look quite complicated, but that's okay! Don't worry too much about the details as the next tutorials will break the process down into bite-sized pieces.
For now, I hope you learned something interesting and are looking forward to writing your own shaders.
I know shaders can seem like a scary world for some however in just a few lines of code I want to give you an easy to implement and impressive shader that will be a great starting point for learning your first shader.
First we are going to make a new shader with Alt+A and you will notice it comes as two files, the Vertex Shader and the Fragment Shader. We will only be using the Fragment Shader which in GameMaker is the second tab along, this code runs on the graphics card for every pixel on the screen.
c80f0f1006