How to crop sprite in a non-rectangular form?

272 views Asked by At

I'm trying to cut sprites in non-rectangular forms, I found this for angled cutting but is there a way to crop if an equation is given?

for example the equation of an oval (x−h)^2/a^2 + (y−k)^2/b^2=1 would crop as such:

enter image description here
enter image description here

1

There are 1 answers

0
Theraot On

I'll asume you are aware that we can work with UV for the coordinates.

Since the formula has some parameters, let us add them as uniforms:

uniform float a:hint_range(0.0, 1.0);
uniform float b:hint_range(0.0, 1.0);
uniform float h:hint_range(0.0, 1.0);
uniform float k:hint_range(0.0, 1.0);

And we compute our formula:

shader_type canvas_item;
uniform float a:hint_range(0.0, 1.0);
uniform float b:hint_range(0.0, 1.0);
uniform float h:hint_range(0.0, 1.0);
uniform float k:hint_range(0.0, 1.0);

void fragment()
{
    float r = ((UV.x - h)*(UV.x - h))/(a*a) + ((UV.y - k)*(UV.y - k))/(b*b);
    COLOR = vec4(vec3(r), 1.0);
}

This should result in a gradient from white to black.

Remember to edit the shader parameters. If you leave them at zero you will see only white.


So we can go ahead and use discard:

shader_type canvas_item;
uniform float a:hint_range(0.0, 1.0);
uniform float b:hint_range(0.0, 1.0);
uniform float h:hint_range(0.0, 1.0);
uniform float k:hint_range(0.0, 1.0);

void fragment()
{
    float r = ((UV.x - h)*(UV.x - h))/(a*a) + ((UV.y - k)*(UV.y - k))/(b*b);
    if (r > 1.0)
    {
        discard;
    }
}

This should give you the result you want.


Alternatively, let us use step so we get a black and white result:

shader_type canvas_item;
uniform float a:hint_range(0.0, 1.0);
uniform float b:hint_range(0.0, 1.0);
uniform float h:hint_range(0.0, 1.0);
uniform float k:hint_range(0.0, 1.0);

void fragment()
{
    float r = step(((UV.x - h)*(UV.x - h))/(a*a) + ((UV.y - k)*(UV.y - k))/(b*b), 1.0);
    COLOR = vec4(vec3(r), 1.0);
}

And now we use that to decide where to output the original texture and where to output transparency:

shader_type canvas_item;
uniform float a:hint_range(0.0, 1.0);
uniform float b:hint_range(0.0, 1.0);
uniform float h:hint_range(0.0, 1.0);
uniform float k:hint_range(0.0, 1.0);

void fragment()
{
    float r = step(((UV.x - h)*(UV.x - h))/(a*a) + ((UV.y - k)*(UV.y - k))/(b*b), 1.0);
    COLOR = mix(vec4(0.0), texture(TEXTURE, UV), r);
}

And this should also give you the result you want.


Addendum: I had not payed attention that in your example you had cut the image at whole pixels. You can do that like this:

shader_type canvas_item;
uniform float a:hint_range(0.0, 1.0);
uniform float b:hint_range(0.0, 1.0);
uniform float h:hint_range(0.0, 1.0);
uniform float k:hint_range(0.0, 1.0);

void fragment()
{
    vec2 uv = floor(UV / TEXTURE_PIXEL_SIZE) * TEXTURE_PIXEL_SIZE;
    float r = step(((uv.x - h)*(uv.x - h))/(a*a) + ((uv.y - k)*(uv.y - k))/(b*b), 1.0);
    COLOR = mix(vec4(0.0), texture(TEXTURE, UV), r);
}

Here I had made the coordinates uses for the equation advance at discrete steps that match the size of the pixels of the texture.