2015-05-30 36 views
10

Próbuję napisać funkcję w GLSL, która zwraca podpisaną odległość do prostokąta. Prostokąt jest wyrównany względem osi. Czuję, że utknąłem; Po prostu nie mogę objąć głowy tym, co muszę zrobić, aby to zadziałało.Obliczanie podpisanej odległości między punktem i prostokątem

Najlepszym wymyśliłem to:

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

która produkuje prostokąt, który wygląda tak:

enter image description here

Linie pokazują odległość od prostokąta. Działa dobrze, ale TYLKO dla odległości NA ZEWNĄTRZ prostokąta. Wewnątrz prostokąta odległość jest statyczna 0..

Jak uzyskać dokładne odległości wewnątrz prostokąta za pomocą jednolitej formuły?

Odpowiedz

13

Jak o tym ...

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

Oto result, gdzie zielone znaki pozytywny dystans i czerwony ujemny (kod poniżej):

enter image description here


Podział:

  1. Uzyskaj podpisaną odległość od granic x i y. u - left i right - u są dwiema odległościami w osi x. Przyjęcie maksymalnej wartości daje podpisany dystans do najbliższej granicy. Wyświetlanie d.x i d.y są wyświetlane indywidualnie na poniższych zdjęciach.

  2. Połączyć x i y:

    1. Jeśli obie wartości są negatywne, podejmuje maksimum (to znaczy najbliższe do granicy). Odbywa się to za pomocą min(0.0, max(d.x, d.y)).

    2. Jeśli tylko jedna wartość jest dodatnia, to potrzebna odległość.

    3. Jeśli obie wartości są dodatnie, najbliższym punktem jest róg, w którym to przypadku chcemy mieć długość. Można to połączyć z powyższym przypadkiem, biorąc mimo to długość i upewniając się, że obie wartości są pozytywne: length(max(vec2(0.0), d)).

    Te dwie części równania wzajemnie się wykluczają, tj. Tylko jedna wytworzy niezerową wartość i może być zsumowana.

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

Doskonała! Doskonale spełnia zadanie. – tenfour

+0

@jozxyqk Jak wpadłeś na tę funkcję? –

+0

@ v.shashenko prób i błędów naprawdę. Zaktualizowałem odpowiedź, by przejść do bardziej szczegółowych informacji. – jozxyqk