Blot cut mask

Based on circular cut mask.

It’s a blot cut mask. Use it on ColorRect or TextureRect. Change progress shader parameter in a script to cut-in and cut-out (maybe even with Tweens because they have different animation modes).

The shader uses circumradius to figure out how far the mask should go. But you can use your own radius, just comment the #define USE_CIRCUMRADIUS line in the shader.

It looks ugly in the editor because it uses screen size to determine its own size. It will look great when you run the game.

 

Shader code
shader_type canvas_item;

// Comment this if you want to use custom radius
#define USE_CIRCUMRADIUS

/** Transition of the mask effect. */
uniform float progress: hint_range(0.0, 1.0) = 0.5;
/** Center of the blot in normalized UV coordinates. */
uniform vec2 center = vec2(0.5);

/** The difference in the farest and the nearest waveness to the center. */
uniform float amplitude: hint_range(0.0, 1.0) = 0.1;
/** Amount of waves. */
uniform float period: hint_range(0.0, 256.0, 1.0) = 5.0;
/** Normalized rotation on 0 progress. */
uniform float base_rotation: hint_range(0.0, 1.0) = 0.0;
/** Added rotation on 1 progress. */
uniform float add_rotation = 0.4;

#ifndef USE_CIRCUMRADIUS
uniform float radius = 0.5;
#endif

float calc_angle(vec2 diff) {
	float angle = atan(-diff.y, diff.x);
	if (angle < 0.0) angle += 2.0 * PI;
	return angle;
}

void fragment() {
	float aspect_ratio = SCREEN_PIXEL_SIZE.y / SCREEN_PIXEL_SIZE.x;

	// Scale coords to non-square screens.
	vec2 ar_uv = vec2(aspect_ratio * UV.x, UV.y);
	vec2 ar_center = vec2(aspect_ratio * center.x, center.y);

	vec2 mask = ar_uv - ar_center;

	#ifdef USE_CIRCUMRADIUS
	float d1 = distance(ar_center, vec2(0.0, 0.0));
	float d2 = distance(ar_center, vec2(0.0, 1.0 + amplitude));
	float d3 = distance(ar_center, vec2(aspect_ratio * (1.0 + amplitude), 0.0));
	float d4 = distance(ar_center, vec2(aspect_ratio * (1.0 + amplitude), 1.0 + amplitude));
	float radius = max(max(d1, d2), max(d3, d4));
	#endif

	float angle = calc_angle(mask);

	float offset = amplitude * sin(PI * base_rotation + period * (angle - progress * add_rotation * PI));
	COLOR.a = step((1.0 - progress) * radius, length(mask) + offset * (1.0 - progress));
}
Tags
blot, cut, mask, ui
The shader code and all code snippets in this post are under CC0 license and can be used freely without the author's permission. Images and videos, and assets depicted in those, do not fall under this license. For more info, see our License terms.

More from breadpack

Starry sky

Color Vision Deficiency (CVD) Simulation Shader

Circular cut mask

Related shaders

Sprite Cut-Out/Cut-In Mask

Circular cut mask

Blend damage revealed with noise texture mask

guest

2 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Snowdrama
4 months ago

I’m probably going to use this in quite a few games for my tutorial to highlight things! I made some changes I’ll share for those looking for some added functionality

I modified it by storing the alpha early in the fragment shader then doing a min at the end for the cutout or the alpha letting me make the non-masked parts partially transparent!

void fragment() {
    // near the top
	float alpha = COLOR.a;

    //...
    // At the end
  	COLOR.a = min(
  		step((1.0 - progress) * radius, length(mask) + offset * (1.0 - progress)),
  		alpha
  	);
  }

I also modified the script to use screen coordinates by multiplying in the screen pixel size again into the center calculation with the aspect ratio.

This way I can highlight things in the tutorial by their global position in the canvas vs the anchor position in the canvas

uniform bool use_percent = false;

//... later

vec2 ar_center = vec2(aspect_ratio * center.x * SCREEN_PIXEL_SIZE.x, center.y * SCREEN_PIXEL_SIZE.y);
vec2 ar_center_screen = vec2(aspect_ratio * center.x, center.y);
vec2 mask = ar_uv - mix(ar_center, ar_center_screen, float(use_percent));