원-라인 충돌 감지 문제


11

현재 브레이크 아웃 클론을 개발 중이며 공 (원)과 벽돌 (볼록 다각형) 사이의 충돌 감지가 올바르게 작동하는 장애물을 발견했습니다. 각 선이 볼록 다각형 벽돌의 가장자리를 나타내는 원 선 충돌 감지 테스트를 사용하고 있습니다.

대부분의 경우 Circle-Line 테스트가 올바르게 작동하고 충돌 지점이 올바르게 해결됩니다.

충돌 감지가 올바르게 작동합니다.

그러나 때로는 충돌 감지 코드가 공이 실제로 벽돌과 교차 할 때 음의 판별으로 인해 false를 반환합니다.

충돌 감지에 실패했습니다.

이 방법의 비 효율성을 알고 있으며 축 정렬 경계 상자를 사용하여 테스트 된 벽돌 수를 줄입니다. 내 주요 관심사는 아래 코드에 수학 버그가 있는지 여부입니다.

/* 
 * from and to are points at the start and end of the convex polygons edge.
 * This function is called for every edge in the convex polygon until a
 * collision is detected. 
 */

bool circleLineCollision(Vec2f from, Vec2f to)
{
    Vec2f lFrom, lTo, lLine;
    Vec2f line, normal;
    Vec2f intersectPt1, intersectPt2;
    float a, b, c, disc, sqrt_disc, u, v, nn, vn;
    bool one = false, two = false;

    // set line vectors
    lFrom = from - ball.circle.centre;      // localised
    lTo = to - ball.circle.centre;          // localised
    lLine = lFrom - lTo;                    // localised
    line = from - to;

    // calculate a, b & c values
    a = lLine.dot(lLine);
    b = 2 * (lLine.dot(lFrom));
    c = (lFrom.dot(lFrom)) - (ball.circle.radius * ball.circle.radius);

    // discriminant
    disc = (b * b) - (4 * a * c);

    if (disc < 0.0f)
    {
        // no intersections
        return false;
    }
    else if (disc == 0.0f)
    {
        // one intersection
        u = -b / (2 * a);

        intersectPt1 = from + (lLine.scale(u));
        one = pointOnLine(intersectPt1, from, to);

        if (!one)
            return false;
        return true;
    }
    else
    {
        // two intersections
        sqrt_disc = sqrt(disc);
        u = (-b + sqrt_disc) / (2 * a);
        v = (-b - sqrt_disc) / (2 * a);
        intersectPt1 = from + (lLine.scale(u));
        intersectPt2 = from + (lLine.scale(v));

        one = pointOnLine(intersectPt1, from, to);
        two = pointOnLine(intersectPt2, from, to);

        if (!one && !two)
            return false;
        return true;
    }
}

bool pointOnLine(Vec2f p, Vec2f from, Vec2f to)
{
    if (p.x >= min(from.x, to.x) && p.x <= max(from.x, to.x) && 
        p.y >= min(from.y, to.y) && p.y <= max(from.y, to.y))
        return true;
    return false;
}

lLine과 line의 차이점을 찾을 수 없습니다 ...
FxIII

실제 포인트를 계산하기 전에 pointOnLine 테스트를 단순화하고 수행 할 수 있습니다.
FxIII

sqrt_disc는 어떻게 계산됩니까?
FxIII

FxIII 죄송합니다. 벡터를 현지화 할 때 약간 혼란스러워 졌을 것입니다. 게시하기 전에 코드를 약간 정리하고 sqrt_disc = sqrt(disc);다시 넣는 것을 잊었습니다 . 아래 답변에 대해 대단히 감사합니다.
jazzdawg

답변:


20

A 에서 B 까지의 구간 은 다음과 같이 계산할 수 있습니다.

P (t) = A + D · t 여기서 DB - A 이고 t는 0에서 1까지입니다.

이제 원은 원점을 중심으로하고 (원점에 중심 을 배치하는 데 필요한 경우 AB를 이동 ) 반지름은 r 입니다.

일부 t에 대해 P 의 길이가 r 과 같 거나 P의 제곱 길이 가 과 같다면 교차점이 있습니다.

벡터의 제곱 길이는 벡터의 내적을 스스로 얻습니다 (이것은 내적에 적합한 연산을 찾으면 새롭고 일관된 길이 개념을 정의 할 수 있음)

P · P = ( A + D · t) · ( A + D · t) =

A · A + 2 A · D t + D · D

우리는 어느 t에 대해 P · P = r²를 구하고 싶었을 때

A · A + 2 A · D t + D · D t² = r²

또는 언제

D · D t² + 2 A · D t + A · A -r² = 0

이것은 매우 유명한 이차 방정식입니다

at² + bt + c = 0

a = D · D ; b = 2 A · D 및 c = A · A -r²

행렬식 b²-4ac 가 양수 인지 확인해야 하므로 교차점 P (t) 를 제공하는 t의 2 개 값을 찾습니다 .

t는 0과 1 사이에 있어야합니다. 그렇지 않으면 AB를 통과하는 회선에 있지만 A 이전 또는 B 이후 에있는 솔루션을 찾았습니다.

[편집하다]

다른 질문이이 답변에 도움이 될 수 있으므로 일부 이미지를 사용하여이 편집의 추론을 단순화하려고했습니다. 시작 조건 이것이 시작 조건입니다. 이제 A_B 세그먼트에 집중하십시오

A에서 B로가는 세그먼트

D 는 A를 B로 이동시키는 벡터이므로 t가 0과 1 사이 인 경우 D · t는 D 의 "적절한 비율"이므로 A + D · t 점은 A_B 세그먼트에 있습니다. 갈색 점은 t가 0과 1 사이이며 짙은 녹색은 t> 1 일 때입니다.

원의 중심을 원점으로 이동하면 상황을 단순화 할 수 있습니다. 지오메트리, 각도, 교차점, 측정 값 등을 유지하는 좌표계의 변경으로 인해 항상 수행 할 수 있습니다.

중심으로 이동하는 원

이제 t가 변할 때 P 의 길이를 계산하고 어느 t P 가 원의 경계를 가로 지르는 지 간단히 알 수 있습니다 .

예

보시다시피 P '의 길이가 r보다 크고 P "의 길이가 r보다 작습니다. 벡터 길이와 r이 양수이기 때문에 유지되는 것보다 크거나 작은 순서의 관계는 우리가 길이 사이의 관계를 계산하는 것입니다 제곱 및 반지름 제곱 P * 1P * 2| P | ² 를 r²와 동일 하게 만드는 점입니다.

사전 편집 섹션에서 언급했듯이 t는 변수 인 2 차 방정식을 얻습니다. t의 알려진 솔루션 값은 t가 두 개의 복소수 인 경우에서 시작합니다. 이는 교차가 없음을 의미합니다. t가 두 개의 동일한 해인 경우-그것은 하나의 교차점이 있음을 의미합니다. 두 개의 별개의 솔루션이있는 경우-두 개의 교차점이 있음을 의미합니다.

판별은 이전 상태를 식별하는 데 사용되며 유효 테스트는 경우 유효한 교차하지만 우리의 세그먼트 외부를 볼 수 t에 이루어집니다 - 즉, 솔루션 t 진짜이어야하고 0과 1 사이의 적절한 교차 가을이 고려되어야 세그먼트 A_B에서


3
이것이 올바른 알고리즘입니다. 작동 방식에 대한 정말 좋은 설명은 Realtime Rendering Third Edition , 787 ~ 791 페이지에서 찾을 수 있습니다. 라이브러리에서 찾을 수 있다면 살펴볼 가치가 있습니다.
Darcy Rayner

4
이 답변에 대한 8 차 찬성으로 2k 평판 포인트에 도달했습니다. 당신이 저에게 맡겨 주신 신뢰에 크게 감사드립니다. 이것은 나의 노력을 인정하고 최고의 품질의 답변을 내기 위해 계속 최선을 다하는 자극입니다. 감사합니다
FxIII

잠깐만, 이건 두 코너 사건을 올바르게 설명합니까? 예를 들어, 원은 t0 <= t <= t1 외부의 선으로 정의 된 평면을 가로 지르지 만 선 세그먼트의 끝점을 약간 hit습니다. 선 끝점과 원 경로 사이의 최소 거리를 확인해야합니다. 해당 거리가 원 반경보다 작 으면 선에 부딪힌 것입니다.
Darcy Rayner 1

@DarcyRayner 두 점이 모두 원 영역 안에있는 경우를 의미합니까?
FxIII
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.