Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Calculate signed distance between point and rectangle

I'm trying to write a function in GLSL that returns the signed distance to a rectangle. The rectangle is axis-aligned. I feel a bit stuck; I just can't wrap my head around what I need to do to make it work.

The best I came up with is this:

float sdAxisAlignedRect(vec2 uv, vec2 tl, vec2 br)
{
    // signed distances for x and y. these work fine.
    float dx = max(tl.x - uv.x, uv.x - br.x);
    float dy = max(tl.y - uv.y, uv.y - br.y);
    dx = max(0.,dx);
    dy = max(0.,dy);
    return sqrt(dx*dx+dy*dy);
}

Which produces a rectangle that looks like:

enter image description here

The lines show distance from the rectangle. It works fine but ONLY for distances OUTSIDE the rectangle. Inside the rectangle the distance is a static 0..

How do I also get accurate distances inside the rectangle using a unified formula?

like image 667
tenfour Avatar asked May 30 '15 10:05

tenfour


1 Answers

How about this...

float sdAxisAlignedRect(vec2 uv, vec2 tl, vec2 br)
{
    vec2 d = max(tl-uv, uv-br);
    return length(max(vec2(0.0), d)) + min(0.0, max(d.x, d.y));
}

Here's the result, where green marks a positive distance and red negative (code below):

enter image description here


Breakdown:

  1. Get the signed distance from x and y borders. u - left and right - u are the two x axis distances. Taking the maximum of these values gives the signed distance to the closest border. Viewing d.x and d.y are shown individually in the images below.

  2. Combine x and y:

    1. If both values are negative, take the maximum (i.e. closest to a border). This is done with min(0.0, max(d.x, d.y)).

    2. If only one value is positive, that's the distance we want.

    3. If both values are positive, the closest point is a corner, in which case we want the length. This can be combined with the above case by taking the length anyway and making sure both values are positive: length(max(vec2(0.0), d)).

    These two parts to the equation are mutually exclusive, i.e. only one will produce a non-zero value, and can be summed.

enter image description hereenter image description here


void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    vec2 uv = fragCoord.xy / iResolution.xy;
    uv -= 0.5;
    uv *= vec2(iResolution.x/iResolution.y,1.0);
    uv += 0.5;
    float d = sdAxisAlignedRect(uv, vec2(0.3), vec2(0.7));
    float m = 1.0 - abs(d)/0.1;
    float s = sin(d*400.0) * 0.5 + 0.5;
    fragColor = vec4(s*m*(-sign(d)*0.5+0.5),s*m*(sign(d)*0.5+0.5),0,1);
}
like image 147
jozxyqk Avatar answered Sep 19 '22 00:09

jozxyqk