In the previous article we went over sending numbers and arrays to the shader. Now let’s look at sending a texture into the shader.
In GameMaker shaders always get called when drawing something to the screen, however usually they don’t manipulate how things are drawn and just pass the graphics straight through to the screen. But let’s replace the default behaviour and send an additional image to the shader so we have two images we can manipulate and draw simultaneously.
Now lets see how this changes for sending an image to a shader.
CREATE EVENT:shader_sampler = shader_get_sampler_index(SHADER NAME, "name_in_shader"); shader_image = sprite_get_texture(spr_player,0);DRAW EVENT:
shader_set(SHADER NAME); texture_set_stage(shader_sampler, shader_image); Draw something here shader_reset();SHADER CODE:
uniform sampler2D name_in_shader; // This will now contain the all the data from the array
This is nice and simple and will work for sending the image data into the shader, however its not actually very helpful because the WHOLE texture page is sent with every other image on it as well.
We want to know where our image is on this texture page, and luckily we’ve already looked at how we can send data into the shader so we can use this to pass in those coordinates.
Normally when talking about computer graphics we are measuring in pixels, however when working with textures on the graphics card we are going to switch to a system called UV coordinates.
These are a value from 0 to 1 in both the horizontal and vertical axis.

We are going to want to find the UV coordinates for where our sprite is on the whole texture page, luckily GameMaker provides us with the function sprite_get_uvs(). This will return the top/left and bottom/right position of the sprite amongst all the other sprites. (Obviously we could avoid all this hassle by just ticking the box that says the sprite should be put on its own texture page, however that just doesn’t seem like the right way to solve this, and we are programmers so lets do it properly.)

Once we bring that code together with the code we wrote for sending data into a shader you will have something that looks like this:
CREATE EVENT:shader_sampler = shader_get_sampler_index(SHADER NAME, "name_in_shader"); shader_image = sprite_get_texture(spr_player,0); shader_params = shader_get_uniform(SHADER NAME, "u_image_uvs") var _uvs = sprite_get_uvs(spr_player,0); shader_image_uvs = [_uvs[0],_uvs[1],_uvs[2],_uvs[3]DRAW EVENT:
shader_set(SHADER NAME) shader_set_uniform_f_array(shader_params,shader_image_uvs) texture_set_stage(shader_sampler, shader_image) shader_set_uniform_f_array(shader_params[i],shader_image_uvs[i]); Draw something here shader_reset()SHADER CODE:
uniform sampler2D name_in_shader; // This will now contain the all the data from the array uniform float u_image_uvs[4]; // Left, Top, Right, Bottom
This will give us an array inside the shader that we can access with [0.38, 0.48, 0.56, 0.59]
Now inside the shader we want to be able to map the UV coordinates of one sprite onto the UV coordinates of another. These dont have to be the same width and height but it does help. With this we will be able to pull data or image information from the same points on the image.

In the above image of a texture page with lots of sprites on it, we want to be able to find our two (or more) particular images. so when we are looking at the top left corner of one image we can pull data down about another image.
Please note these images could be on different texture pages. Or they could be surfaces with dynamic data on them.
In the first article we passed in a value to switch between two colours, lets now use that same technique but switch between two different images.

CREATE EVENT:
shader_name = shader_dave in_image_0 = spr_1 in_image_1 = spr_2 var i = 0; shader_params[i++] = shader_get_uniform(shader_name, "u_image_0_uvs"); shader_params[i++] = shader_get_uniform(shader_name, "u_image_1_uvs"); shader_params[i++] = shader_get_uniform(shader_name, "u_value"); i = 0; shader_image[i] = sprite_get_texture(in_image_1, 0); // Pass in this image into the shader shader_sampler[i] = shader_get_sampler_index(shader_name, "s_imageIn1"); // We need to know its texture page i++; // Get all the UVs for these sprite and other image data in one place. i = 0; var _uvs1 = sprite_get_uvs(in_image_0, 0); shader_image_uvs[i] = [_uvs1[0], _uvs1[1], _uvs1[2], _uvs1[3], sprite_get_width(in_image_0), sprite_get_height(in_image_0)]; i++; var _uvs1 = sprite_get_uvs(in_image_1, 0); shader_image_uvs[i] = [_uvs1[0], _uvs1[1], _uvs1[2], _uvs1[3], sprite_get_width(in_image_1), sprite_get_height(in_image_1)]; i++;DRAW EVENT:
shader_set(shader_name); var i = 0; shader_set_uniform_f_array(shader_params[i], shader_image_uvs[i]); i++ shader_set_uniform_f_array(shader_params[i], shader_image_uvs[i]); i++ shader_set_uniform_f(shader_params[i] , mouse_y/room_height); i++ i = 0; texture_set_stage(shader_sampler[i], shader_image[i]); i++ draw_sprite(in_image_0,0,0,0) shader_reset();SHADER CODE:
varying vec2 v_vTexcoord; varying vec4 v_vColour; uniform float u_image_0_uvs[6]; // Left, Top, Right, Bottom, Width, Height uniform float u_image_1_uvs[6]; // Left, Top, Right, Bottom, Width, Height uniform float u_value; uniform sampler2D s_imageIn1; // Texture sampler for image 1 float lerp_long(float _input_min, float _input_max, float _output_min, float _output_max, float _amount) { return _output_min + (_amount - _input_min) * (_output_max - _output_min) / (_input_max - _input_min); } void main() { // Calculate the UV coordinates for the first image vec2 use_pos1 = vec2( lerp_long(u_image_0_uvs[0], u_image_0_uvs[2], u_image_1_uvs[0], u_image_1_uvs[2], v_vTexcoord.x), lerp_long(u_image_0_uvs[1], u_image_0_uvs[3], u_image_1_uvs[1], u_image_1_uvs[3], v_vTexcoord.y) ); // Sample the textures vec4 image_in_1 = texture2D(s_imageIn1, use_pos1); vec4 og_img = texture2D(gm_BaseTexture, v_vTexcoord); if (u_value > use_pos1.y) { gl_FragColor = og_img; } else { gl_FragColor = image_in_1; } }
The only new part is the lerp_long() function that is used to actually calculate where one place is relative to another.
Some more ideas on how it can be used thanks to Doom Guy:

Now that we can gain additional information about parts of our sprite lets have a look at what we can do with that.

Next we will be using this simply to draw some health bars and progress bars of different shapes.