GameMaker: Studio

GameMaker: Studio

Not enough ratings
How to use the new application_surface variable to apply full screen shader effects
By Scorcher24
This guide shows you how to take advantage of this new feature of GameMaker Studio 1.3
   
Award
Favorite
Favorited
Unfavorite
What is application_surface?
Everyone knows full screen shader effects from modern gaming. Mostly every gamer knows what FXAA is. But only a few know, how it is actually done.

A shader cannot modify the screen "directly". It can only modify a texture. So a surface is basically a texture that is rendered upon and this texture is then modified by a fragment shader, which is used to modify the colors of a texture.

Before, surfaces existed in GM:S but if you wanted to do fullscreen effects, you needed to draw your whole game on one, which was a hassle. With GM:S 1.3, this is about to change. The game is drawn onto a surface by default, enabling us to do full screen effects more easily. This default surface is accessible in GML via the built-in constant application_surface. This guide exists to cover that topic.

If you want to read more, head over to YoYo Games Blog:
http://yoyogames.com/tech_blog/45
A cool Shader
1.) Create a shader
2.) Paste the following code into the fragment section, leave the vertex section as it is.

varying vec2 v_texcoord; varying vec4 v_color; uniform float time; void main() { vec2 uv = v_texcoord.xy * 0.986 ;//+ 0.007; vec3 col; // Note: Emulates color distortion caused by subpixels. This can reduced to a single texture-fetch to increase performance. col.r = texture2D(gm_BaseTexture, vec2(uv.x + 0.0003, uv.y)).x; col.g = texture2D(gm_BaseTexture, vec2(uv.x + 0.0003, uv.y)).y; col.b = texture2D(gm_BaseTexture, vec2(uv.x - 0.0003, uv.y)).z; col = clamp(col * 0.5 + 0.6 * col * col, 0.0, 1.0); col *= 0.6 + 6.4 * uv.x * uv.y * (1.0 - uv.x) * (1.0 - uv.y); // Vignetting //col *= vec3(0.9, 1.0, 0.7); // "TV Color" col *= vec3(0.9, 1.0, 0.9); // "TV Color" // Scanlines col *= 0.8 + 0.2 * sin(10.0 * time + uv.y * 768.0); // Similar results to old rand() function, but more efficient. col *= 1.0 - 0.07 * fract(tan(time * 100.0)); gl_FragColor = vec4(col, 1.0); }

This code is taken from this thread:
http://gmc.yoyogames.com/index.php?showtopic=586380

I won't go into detail about how it works, since it is not the scope of this tutorial. So if you want to get going with shaders first, feel free to visit any online GLSL Tutorial.
Creating the object
Now, create your object that you want to use to display this effect on screen. I named it objTV since the shader is a TV effect.

What we need is three events:

1.) Create Event

In this section, we are going to do three things. First we are going to disable to automatic drawing of the application_surface. This is done with this code:

application_surface_draw_enable(false);

As it says, it disables the new automatic surface from being drawn automatically and this enables us to apply a shader before we actually render this surface.

Next, we need a variable from the shader. To access this variable, we need to ask GMS where it is located so it can provide us with a pointer. (This is actually some low level stuff here, but necessary, read up on GLSL uniforms if you want to know more.)

s_time = shader_get_uniform( shd_tv, "time");

So we store the location of the "time" uniform in our variable s_time to access it later. The shader needs this uniform to generate some animation.

The last one is straight forward, we are going to use our own variable for time. The built in variables are either too slow or too fast. So we need our own time measurement. The shader uses this one for animating the scanlines.

myTime = 0;

2.) Step Event

The step event is pretty straightforward:

myTime += 0.01; if ( myTime >= 65000 ) myTime = 0;

It increases the time with every step and resets it if it grows too big. You can modify the speed of the animation by changing 0.01 to whatever you want.

3.) End Draw Event

So now this is where the magic happens.
First of all, we need to bind the shader to current output and send the time variable:

shader_set(shd_tv); shader_set_uniform_f(s_time, myTime);

Now the shader is bound and we can finally draw the built-in surface:
draw_surface( application_surface, 0, 0 );

shader_reset();
This tells GMS that we are done with this shader.
Using the object
Now there is only one thing left to do: Set the object to persistent and add it to the first room of your project or don't set it to persistent and only add it to the rooms where you want to use this nice little effect.
Downloads
Look at the example via Steam:
http://steamproxy-script.pipiskins.com/sharedfiles/filedetails/?id=259224550

Download the whole Project for import into GMS:
https://drive.google.com/file/d/0B5-pGJYVbatLZDJkLW1uV1BFS0U/edit?usp=sharing

Thanks for reading and if you find this guide useful, feel free to send any comments, hookers, games and flames to scorcher24@gmail.com.

Please note, that this guide needs GameMaker Studio 1.3 Standard Edition. At the time of writing, you have to opt-in to the current Beta, which can potentially break any projects in the making. So only do this if you know what you do ( you can roll back the client any time though, but not any projects saved with 1.3 ).
6 Comments
Csaki 28 Feb, 2020 @ 11:18am 
Thank you for this thread, it was really helpful.
Ghostomato 3 Jan, 2018 @ 9:20am 
I tried this, and I found that the surface only drew correctly when I put it in Post Draw, not End Draw. Was that a mistake, or has GMS changed since this guide was written?

Also, thanks for the guide! Apart from using the wrong draw event, it works like a charm.
MOOPALOO 6 Aug, 2017 @ 8:41am 
in v1.4.1763 I get a black screen when I do this. Strangely, when I set application_surface_draw_enable(false); to application_surface_draw_enable(true); my shader works if the texture isn't accessed.
nerel 29 Aug, 2014 @ 7:17am 
Thank you!
Scorcher24  [author] 14 May, 2014 @ 11:18am 
People see your name when they click on the link. I don't see how it matters. Source was given.
Crumpet Butter 14 May, 2014 @ 2:59am 
Nice of you to put a link to my GMC topic, would have been nicer if you used my GMC username. Thanks - xygtop3