SAT와의 접점 찾기


12

SAT (Separating Axis Theorem)를 사용하면 최소 변환 벡터, 즉 두 개의 충돌하는 객체를 분리 할 수있는 가장 짧은 벡터를 쉽게 결정할 수 있습니다. 그러나 필요한 것은 관통하는 물체가 움직이는 벡터를 따라 물체를 분리하는 벡터입니다 (예 : 접점).

나는 명확히하기 위해 그림을 그렸다. 이전 위치에서 이후 위치로 이동하는 하나의 상자가 있습니다. 그 후 위치에서 회색 다각형과 교차합니다. SAT는 빨간색 벡터 인 MTV를 쉽게 반환 할 수 있습니다. 파란색 벡터를 계산하려고합니다.

토 다이어그램

내 현재 솔루션은 파란색 벡터의 길이가 특정 임계 값으로 알려질 때까지 전후 위치 사이에서 이진 검색을 수행합니다. 작동하지만 셰이프 간의 충돌을 루프마다 다시 계산해야하기 때문에 계산이 매우 비쌉니다.

접점 벡터를 찾는 더 간단하고 효율적인 방법이 있습니까?


1
SAT 사용을 중단 했습니까? MPR (Minkowski Portal Refinement)과 같은 알고리즘은 접점 매니 폴드를 직접 찾을 수 있습니다. SAT 및 GJK를 사용하면 접점을 계산하기위한 별도의 알고리즘이 필요합니다.
Sean Middleditch

답변:


6

먼저 물체를 움직 인 다음 충돌을 테스트 한 다음 물체에서 나올 때까지 뒤로 물러서도록 구조를 설정하면 상당히 어렵습니다. 이것을 동적 교차 테스트 ( 정적 물체에 대한 움직이는 물체) 로 생각하는 것이 좋습니다 .

운 좋게도 축 테스트를 분리하면 여기에서 도움이 될 수 있습니다! Ron Levine이 제공 한 알고리즘에 대한 설명은 다음과 같습니다 .

알고리즘은 다음과 같습니다. 두 볼록 바디의 상대 속도 벡터로 작업합니다. t at 에서 두 몸체와 상대 속도 벡터를 특정 분리 축에 투영하면 두 개의 1-D 간격과 1-D 속도가 주어 지므로 두 간격이 교차하는지 여부를 쉽게 알 수 있습니다. 그들은 떨어져 움직이거나 함께 움직입니다. 분리 축 중 하나 (또는 ​​실제로는 임의의 축)에서 분리되어 이동하면 향후 충돌이 없음을 알 수 있습니다. 분리 축에서 두 투영 간격이 t 에서 교차하는 경우separated 또는 분리되어 함께 움직이면 두 간격이 처음 교차하는 가장 빠른 미래 시간을 계산하고 (두 개의 연속 된 직선 운동을 가정하는) 가장 미래의 시간을 계산하기가 쉽습니다. 간격이 마지막으로 교차하고 떨어져 나갑니다. (그들은에서 교차하는 경우 t ₀ 후 최초의 미래 교차로 시간은 t ₀). 대부분의 모든 분리 축에 대해이 작업을 수행하십시오. 가장 빠른 미래의 교차 시간의 모든 축에 대한 최대 값이 가장 최근의 미래 교차 시간의 모든 축에 대한 최소값보다 작 으면 최대 가장 빠른 미래의 교차 시간은 두 3D 다면체의 첫 충돌의 정확한 시간입니다. 미래에는 충돌이 없습니다.

다시 말해, 정적 분리 축 테스트에서 일반적으로 수행하는 모든 축을 반복합니다. 겹치는 부분이 없으면 조기에 나가지 않고 움직이는 물체 의 투사 속도 를 계속 확인 합니다. 경우는 정적 객체로부터 멀리 움직이는 다음 일찍 아웃. 그렇지 않으면 가장 빠른 최신 접촉 시간을 상당히 쉽게 해결할 수 있습니다 (1D 간격이 다른 1D 간격으로 이동 함). 모든 축에 대해이를 수행하고 가장 빠른 교차 시간과 가장 최근의 교차 시간을 유지하면 움직이는 객체가 정적 객체와 언제 충돌하는지 알 수 있습니다. 따라서 움직이는 객체가 정적 객체에 닿는 지점까지 정확하게 이동할 수 있습니다.

알고리즘에 대한 거칠고 완전히 검증되지 않은 의사 코드는 다음과 같습니다.

t_min := +∞
t_max := -∞
foreach axis in potential_separating_axes
    a_min := +∞
    a_max := -∞
    foreach vertex in a.vertices
        a_min = min(a_min, vertex · axis)
        a_max = max(a_max, vertex · axis)
    b_min := +∞
    b_max := -∞
    foreach vertex in b.vertices
        b_min = min(b_min, vertex · axis)
        b_max = max(b_max, vertex · axis)
    v := b.velocity · axis
    if v > 0 then
        if a_max < b_min then
            return no_intersection
        else if (a_min < b_min < a_max) or (b_min < a_min < b_max) then
            t_min = min(t_min, (a_max - b_min) / v)
            t_max = max(t_max, 0)
        else
            t_min = min(t_min, (a_max - b_min) / v)
            t_max = max(t_max, (a_min - b_max) / v)
    else if v < 0 then
        // repeat the above case with a and b swapped
    else if v = 0 then
        if a_min < b_max and b_min < a_max then
            t_min = min(t_min, 0)
            t_max = max(t_max, 0)
        else
            return no_intersection
if t_max < t_min then
    // advance b by b.velocity * t_max
    return intersection
else
    return no_intersection

다음 은 몇 가지 다른 기본 테스트를 위해 구현 된 Gamasutra 기사 입니다. SAT와 마찬가지로 볼록한 물체가 필요합니다.

또한 이것은 간단한 분리 축 테스트보다 다소 복잡합니다. 시도하기 전에 꼭 필요한지 확인하십시오. 매우 많은 수의 게임은 최소한의 변환 벡터를 따라 객체를 서로 밀어냅니다. 왜냐하면 그들은 주어진 프레임에서 서로 매우 멀리 침투하지 않으며 시각적으로 거의 눈에 띄지 않기 때문입니다.


2
이것은 매우 멋지지만 접촉 매니 폴드 계산에 대한 질문에 직접 대답하지는 않았습니다. 또한 올바르게 이해하면이 답변은 선형 속도로만 작동하므로 회전 객체를 지원할 수 없습니다. 질문하는 사람이 원하는지 확실하지 않습니다.
Sean Middleditch

1
@seanmiddleditch 사실입니다. 프레임 위의 회전을 무시합니다. 처음에 즉시 회전해야합니다. 그러나 보수적 인 진보가 부족하다는 것을 실제로 알고있는 방법은 실제로 회전을 정확하게 처리하지 않습니다. 그러나 회전이 없으면 접점의 더 나은 추정치를 생성합니다.
John Calsbeek

2

다각형 클리핑을 사용하려고합니다. 이것은 내가 가지고 있지 않은 그림으로 가장 잘 설명되어 있지만이 사람은 설명 했으므로 설명하겠습니다.

http://www.codezealot.org/archives/394

접촉 매니 폴드는 직접 충돌 지점이 아니라 충돌을 "가장 많이 담당하는"객체 중 하나의 지점을 반환합니다. 그러나 직접 충돌 지점은 필요하지 않습니다. 관통 깊이와 이미 가지고있는 법선을 사용하여 단순히 물체를 밀어 내고 접촉 다기관을 사용하여 다른 물리적 영향을 적용 할 수 있습니다 (예 : 상자를 기울이거나 아래로 기울이십시오).

그림은 작은 문제를 보여줍니다. 실제로 파란색 벡터의 점은 실제 시뮬레이션에서 찾을 수 없습니다. 실제로 상자에 부딪히지 않기 때문입니다. 상자는 왼쪽 하단 모서리에 약간의 모서리가 침투함에 따라 경사면 위로 올라갑니다.

침투 깊이는 상대적으로 작으며 관통 법선을 따라 경사면에서 상자를 밀기 만하면 상자가 "정확한"위치에 가깝게 배치되어 실제로 거의 눈에 띄지 않게됩니다. 특히 상자가 튀거나 넘어 질 경우 또는 나중에 슬라이드합니다.


SAT를 사용하여 "파란 벡터"(속도 벡터를 따라 모양에서 개체를 다시 밀어내는 데 필요한)를 계산할 수있는 방법이 있는지 알고 있습니까?
Tara

@Dudeson : SAT를 사용하지 않습니다. SAT가하는 일은 아닙니다. SAT는 첫 번째 접촉 모서리가 아닌 최소 관통 깊이의 모서리를 제공합니다. 나는 당신이 요구하는 것을하기 위해 스윕 모양 충돌 감지를 사용해야한다고 생각합니다.
Sean Middleditch 2016 년

SAT가하는 일을 알고 있습니다. 전에 구현했습니다. 그러나 SAT의 출력을 사용하여 첫 번째 접촉면을 계산할 수 있다면 해결해야 할 문제가 있습니다. "someguy"의 답변도 참조하십시오. 그것은 가능하다는 것을 암시하지만 잘 설명하지는 않습니다.
Tara

@Dudeson : 최소 침투의 모서리 / 축은 반드시 첫 접촉의 모서리 일 필요는 없으므로 SAT가 어떻게 도움이되는지 여전히 알 수 없습니다. 나는 결코이 주제의 전문가가 아니므로 내가 잘못 될 수 있음을 인정합니다. :)
Sean Middleditch 2016 년

바로 그거죠. 이것이 가능한지 확실하지 않은 이유입니다. 그것은 누군가의 대답이 잘못되었다는 것을 암시합니다. 그러나 어쨌든 도움을 주셔서 감사합니다! : D
Tara

0

MAT Vector를 Vector 방향으로 투영하십시오. 침투를 보상하기 위해 결과 벡터를 방향 벡터에 추가 할 수 있습니다. SAT를 수행 할 때 Axis에서와 같은 방식으로 투사하십시오. 이렇게하면 다른 개체와 닿는 위치에 개체가 정확하게 설정됩니다. 부동 소수점 문제를 해결하기 위해 작은 엡실론을 추가하십시오.


1
"MAT Vector"? "MTV"를 의미합니까?
Tara

0

내 대답에는 몇 가지주의 사항이 있습니다. 터널링 문제, 즉 고속으로 움직이는 물체로 인한 문제 를 처리하려고한다고 가정합니다 .

MTV를 식별하면 테스트해야 할 가장자리 / 표면 법선을 알 수 있습니다. 또한 상호 침투 물체의 선형 속도 벡터도 알고 있습니다.

당신에게 있음을 설립 한 후 어느 시점 프레임 동안, 교차로가 발생하면 다음 시작 포인트를 기준으로, 진 반 단계의 작업을 수행 할 수 있습니다 : 프레임 중 첫번째 관통하는 정점을 확인합니다 :

vec3 vertex;
float mindot = FLT_MAX;
for ( vert : vertices )
{
    if (dot(vert, MTV) < mindot)
    {
         mindot = dot(vert, MTV);
         vertex = vert;
    }
}

정점이 식별되면 이진 반 단계는 훨씬 저렴합니다.

//mindistance is the where the reference edge/plane intersects it's own normal. 
//The max dot product of all vertices in B along the MTV will get you this value.
halfstep = 1.0f;
vec3 cp = vertex;
vec3 v = A.velocity*framedurationSeconds;
float errorThreshold = 0.01f; //choose meaningful value here
//alternatively, set the while condition to be while halfstep > some minimum value
while (abs(dot(cp,normal)) > errorThreshold)
{            
    halfstep*=0.5f;
    if (dot(cp,normal) < mindistance) //cp is inside the object, move backward
    {
        cp += v*(-1*halfstep);
    }
    else if ( dot(cp,normal) > mindistance) //cp is outside, move it forward
    {
        cp += v*(halfstep);
    }
}

return cp;

이것은 상당히 정확하지만, 단일 경우에는 단일 충돌 지점 만 제공합니다.

문제는 일반적으로 프레임 당 객체가 이와 같이 터널링 할 수있을만큼 충분히 빠르게 움직 일지 미리 알 수 있으므로 속도를 따라 주요 정점을 식별하고 속도 벡터를 따라 광선 테스트를 수행하는 것이 가장 좋습니다. 회전하는 물체의 경우 올바른 접점을 확보하기 위해 일종의 이진 반 단계 슬리핑을 수행해야합니다.

그러나 대부분의 경우 장면의 대부분의 객체가 단일 프레임에서 멀리까지 충분히 빠르게 이동하지 않기 때문에 하프 스테핑이 필요하지 않으며 불연속 충돌 감지로 충분합니다. 너무 빨리 이동하는 탄환과 같은 고속 물체는 접점에 대해 광선 추적 될 수 있습니다.

흥미롭게도,이 반 단계 방법은 프레임 동안 오브젝트가 발생한 (거의) 정확한 시간을 제공 할 수도 있습니다.

float collisionTime = frametimeSeconds * halfstep;

어떤 종류의 물리 충돌 해결을 수행하는 경우 다음을 통해 A의 위치를 ​​수정할 수 있습니다.

v - (v*halfstep)

거기서 물리학을 정상적으로 할 수 있습니다. 단점은 물체가 합리적으로 빠르게 움직이면 속도 벡터를 따라 순간 이동하는 것을 볼 수 있다는 것입니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.