GameMaker Newbie Needs Helping Drawing Shapes with Shaders

G

GoldenLeaf

Guest
So real quick, this is my first post on here and my first attempt at a project, so I apologize in advance if this has been covered before, if this is in the wrong section, if there is some formatting or other nuances I get incorrect, or if I just flat out sound dumb, lol. Noob trying to learn 😅😅

Anyways, what I am trying to do is have an object be able to be placed by the player, and once placed it has a circle around it (at a set radius) with all the colors inverted within the circle (and only within the circle) until the object expires.

So far, I've made the object (or three object rather, 1) the object on the ground for the player to pick up, 2) the object that hangs out on the player sprite to indicate they have the item, and 3) the object which is created when the player uses the item) and then is destroyed once it's internal timer runs out, So all good there.

Then I also downloaded a shader off of the yoyo game store to be used to invert the color, I will be referring to this shader as "shInvert"
VVV see code below VVV
GML:
attribute vec3 in_Position;
attribute vec4 in_Colour;
attribute vec2 in_TextureCoord;

varying vec2 v_texcoord;

void main()
{
    gl_Position = gm_Matrices[MATRIX_WORLD_VIEW_PROJECTION] * vec4(in_Position, 1.0);
    v_texcoord = in_TextureCoord;
}
Code:
varying vec2 v_texcoord;

uniform float time;
uniform vec2 mouse_pos;
uniform vec2 resolution;
uniform float strength;

void main()
{
    vec4 colour = texture2D(gm_BaseTexture, v_texcoord);
    gl_FragColor.rgb = 1.0 - colour.rgb;
    gl_FragColor.a = colour.a;
}
However, this is where my issue comes up. I can't find a way to draw the circle and only have the shader apply within the circle. Whenever I do "shader_set(shInvert);" it inverts the color to the entire screen, and not just within the area of the circle. I've tried messing around with draw_circle and draw_circle_color with no luck as I'm not sure how to apply a shader to either of those, and draw_circle_color only lets me input a single color argument (well 2, the inside and the outline colors). I've been looking around on the forums and YouTube for the past couple of days with no luck so I figured I'd make an account and make a post (as I'm sure I will have a million more issues to come, lol).

Anyways, any helps would be greatly appreciated! And please, let me know if I should include any code or anything like that
 
Last edited by a moderator:

FoxyOfJungle

Kazan Games

NOTE: If you are wanting to draw a shape using a shader, you should be aware that most shaders expect the following inputs: vertex, texture, Colour. However, when using this function, only vertex and colour data are being passed in, and so the shader may not draw anything (or draw something but not correctly). If you need to draw shapes in this way then the shader should be customised with this in mind.
 
G

GoldenLeaf

Guest
Right, and I saw that, but at this point I don't know enough about shaders to specifically customize it to allow me to pass it as an argument (hence why I made this post in the first place). Hell, I'm not even sure if that's exactly what I am supposed to be doing since (and again, from a noob perspective) it seems like those arguments are for single colors (like c_black, c_red, etc) but it would need to invert a wide array of colors based on where the object that makes the circle is placed on the screen (as well as any other objects that might move in or out of the circle's radius).

Or is there a way in the shader to allow me to pass the whole shader as an argument? And if so, any resources on that is greatly appreciated!

Either way, thanks for responding! 😃👍
 
Last edited by a moderator:
G

GoldenLeaf

Guest
**Bump** // is this a situation where the only answer is just get good at shaders?
 

Yal

🐧 *penguin noises*
GMC Elder
However, this is where my issue comes up. I can't find a way to draw the circle and only have the shader apply within the circle. Whenever I do "shader_set(shInvert);" it inverts the color to the entire screen, and not just within the area of the circle. I've tried messing around with draw_circle and draw_circle_color with no luck as I'm not sure how to apply a shader to either of those, and draw_circle_color only lets me input a single color argument (well 2, the inside and the outline colors). I've been looking around on the forums and YouTube for the past couple of days with no luck so I figured I'd make an account and make a post (as I'm sure I will have a million more issues to come, lol).
Shaders applies to everything you draw between setting and resetting the shader. If you want to invert everything in a specific region (including stuff that was previously drawn there) you need to do the following:
  • Create a surface the size of the circle you wanna invert
  • Copy a rectangular region onto it (there's a function to copy between surfaces)
  • Erase the corners off this region so you only have a circle left (easiest way probably is disabling the color channels and writing to alpha directly, first wiping it all to 0 and then drawing a circle of 1)
  • Set the shader
  • Draw the cookie cutout surface at the appropriate place in the room
  • Reset the shader
 
G

GoldenLeaf

Guest
@Yal Awesome feedback! I'll have to look into that a bit more.

Admittedly, I ended up just coping and pasting someone else's code who was able to get it to work the way I wanted. Pretty much it'll invert anything within the circle on the instances layer the object and below.


Here's the code I found for reference:
((note, lifespan and stopSignRadius are variables in my game))

GML:
if(lifespan > 0)
{
   
    //all this is to invert the color, DO NOT TOUCH
    gpu_set_blendmode_ext(bm_inv_dest_color,bm_zero);
    draw_set_color(c_white);
    draw_set_alpha(1);
    draw_circle(x,y,stopSignRadius, false);
   
    draw_set_color(c_black);
    gpu_set_blendmode(bm_add);
    draw_circle(x,y,stopSignRadius, false);
   
    gpu_set_blendmode(bm_normal);
    draw_set_color(c_black);
    //end of color change block
}
 

sp202

Member
Alternatively, you can provide the shader with a centre position as a uniform, and add an if statement to check that the distance from the current fragment is within the radius.
 

Yal

🐧 *penguin noises*
GMC Elder
Alternatively, you can provide the shader with a centre position as a uniform, and add an if statement to check that the distance from the current fragment is within the radius.
That approach will work if you only need to run that effect, and only have one inverted circle at a time, so you can have the shader active during the entire draw event (so it applies to everything). If you need to run other shaders as well, it suddenly gets a lot more complicated (e.g. you might need to draw everything twice, once for the inversion shader and once for potential other effects). Simpler to implement... but more limited.
 
Top