Вычислить расстояние между точкой и прямоугольником

Я пытаюсь написать функцию в GLSL, которая возвращает знаковое расстояние до прямоугольника. Прямоугольник выравнивается по оси. Я чувствую себя немного застрявшим; Я просто не могу оборачивать голову тем, что мне нужно сделать, чтобы он работал.

Лучшее, что я придумал, это:

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);
}

Создает прямоугольник, который выглядит следующим образом:

enter image description here

Линии показывают расстояние от прямоугольника. Он отлично работает, но ТОЛЬКО для расстояний вне прямоугольника. Внутри прямоугольника расстояние статическое 0..

Как получить точные расстояния внутри прямоугольника с помощью единой формулы?

Ответ 1

Как насчет этого...

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));
}

Здесь результат, где зеленый обозначает положительное расстояние и красный негатив (код ниже):

enter image description here


Структура:

  • Получите знаковое расстояние от границ x и y. u - left и right - u - это два расстояния по оси x. Принимая максимум этих значений, вы получаете знаковое расстояние до ближайшей границы. Просмотр d.x и d.y отображается отдельно на изображениях ниже.

  • Объединить x и y:

    • Если оба значения отрицательные, возьмите максимум (т.е. ближе к границе). Это делается с помощью min(0.0, max(d.x, d.y)).

    • Если только одно значение положительно, то требуемое расстояние.

    • Если оба значения положительны, ближайшая точка является углом, и в этом случае мы хотим длину. Это можно комбинировать с приведенным выше случаем, принимая длину в любом случае и убедившись, что оба значения положительны: length(max(vec2(0.0), d)).

    Эти две части уравнения являются взаимоисключающими, т.е. только один будет производить ненулевое значение и может быть суммирован.

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);
}