SATで連絡先を見つける


12

分離軸定理(SAT)により、最小平行移動ベクトル、つまり2つの衝突するオブジェクトを分離できる最短ベクトルを簡単に決定できます。ただし、必要なのは、貫通オブジェクトが移動しているベクトル(つまり、接触点)に沿ってオブジェクトを分離するベクトルです。

明確にするために絵を描きました。前から後の位置に移動する1つのボックスがあります。後の位置では、灰色のポリゴンと交差します。SATは、赤いベクトルであるMTVを簡単に返すことができます。青いベクトルを計算しようとしています。

SAT図

私の現在のソリューションは、青いベクトルの長さが特定のしきい値に達するまで、前と後の位置の間でバイナリ検索を実行します。それは機能しますが、形状間の衝突をループごとに再計算する必要があるため、非常に高価な計算です。

接触点ベクトルを見つけるためのより簡単なおよび/またはより効率的な方法はありますか?


1
SATの使用に取りかかっていますか?MPR(Minkowski Portal Refinement)などのアルゴリズムは、接触多様体を直接見つけることができます。SATとGJKでは、接点を計算するための別個のアルゴリズムが必要です。
ショーンミドルディッチ

回答:


6

最初にオブジェクトを移動し、次に衝突をテストし、オブジェクトから出るまで後退するように構成する場合、あなたが話していることはかなり難しいです。これを動的交差テスト、つまり静止オブジェクトに対する移動オブジェクトとして考える方がおそらく良いでしょう。

幸いなことに、軸テストを分離することはここであなたを助けることができます!以下は、Ron Levineの好意によるアルゴリズムの説明です。

アルゴリズムは次のようになります。2つの凸体の相対速度ベクトルを使用します。2つの物体のそれぞれと相対速度ベクトルをt atで特定の分離軸に投影すると、2つの1D間隔と1D速度が得られるため、2つの間隔が交差するかどうか、またそうでない場合は、彼らは離れて移動するか、一緒に移動しています。それらが分離軸のいずれか(または、実際には、任意の軸)で分離され、離れて移動する場合、将来の衝突がないことがわかります。分離軸上で、2つの投影された間隔がtで交差する場合₀または分離されて一緒に移動している場合、2つの間隔が最初に交差する最初の未来の時間と(2つの単純な1D線形式によって)簡単に計算できます間隔は最後に交差し、離れ始めます。(t atで交差している場合、最も早い将来の交差時間はt₀です)。最大ですべての分離軸に対してこれを行います。最も早い将来の交差時間のすべての軸の最大値が最も遅い将来の交差時間のすべての軸の最小値よりも小さい場合、その最大の最も早い将来の交差時間は、2つの3D多面体の最初の衝突の正確な時間です。将来の衝突ではありません。

つまり、静的な分離軸テストで通常行うすべての軸をループします。オーバーラップが見つからない場合は早出しの代わりに、移動を続け、移動するオブジェクトの投影速度を確認します。場合には、静的オブジェクトから離れて動いて、その後、あなたは早期にアウト。そうでなければ、接触の最も早い時間と最も遅い時間をかなり簡単に解決できます(1つの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
これはすべて非常にクールですが、接触多様体の計算に関する質問には直接答えませんでした。また、私がそれを正しく理解している場合、この答えは線形速度でのみ機能するため、回転オブジェクトをサポートできません。質問者がそれを望んでいるかどうかわからない。
ショーンミドルディッチ

1
@seanmiddleditchそれは本当です、フレーム上の回転を無視します。最初は瞬時に回転する必要があります。しかし、保守的な進歩が不足していると私が知っている方法は、実際には回転を正確に処理しません。ただし、回転がない場合は、接触点のより良い推定値が生成されます。
ジョンカルスビーク

2

ポリゴンクリッピングを使用します。これは私が持っていない写真で最もよく説明されますが、この男はそうしましたので、私は彼にそれを説明させます。

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

接触マニホールドは、衝突の直接のポイントではなく、衝突の「最も責任がある」オブジェクトの1つのポイントを返します。ただし、直接的な衝突点は実際には必要ありません。すでに持っている浸透深さと法線を使用してオブジェクトを押し広げ、他の物理的影響を適用するために接触マニホールドを使用することができます(たとえば、ボックスをタンブル/スロープにロールダウンします)。

あなたの写真は小さな問題を示していることに注意してください:実際にボックスがヒットする場所ではないため、求めている青いベクトル上の点は物理シミュレーションでは見つかりません。角のほんの少しが貫通しているため、ボックスの左下隅が斜面のさらに上のどこかにヒットします。

侵入の深さは比較的小さく、ボックスを侵入法線に沿って斜面から押し出すだけで、特にボックスがバウンド、タンブルする場合、実際にはほとんど気付かないほど「正しい」位置にボックスを近づけますとにかく後でスライドします。


SATを使用して、その「青いベクトル」(速度ベクトルに沿ってオブジェクトを形状から押し戻すために必要なもの)を計算する方法があるかどうか知っていますか?
タラ

@Dudeson:SATを使用していません。それはSATが行うことではありません。SATは、最初の接触エッジではなく、最小の侵入深さのエッジを提供します。スイープシェイプの衝突検出を使用して、求めていることを行う必要があると思います。
ショーンミドルディッチ

私はSATが何をするか知っています。以前に実装しました。しかし、SATの出力を使用して最初の接触エッジを計算できれば解決する問題があります。「someguy」の回答も参照してください。それは可能であることを示唆していますが、あまりうまく説明していません。
タラ

@Dudeson:最小浸透のエッジ/軸は必ずしも最初の接触のエッジではないため、SATがここでどのように役立つかはまだわかりません。私は決してこのトピックの専門家ではないので、間違っている可能性があることを認めます。:)
ショーン・ミドルディッチ

丁度。それが、これが可能かどうかわからない理由です。しかし、それは、誰かの答えが間違っていることを意味します。とにかく助けてくれてありがとう!:D
タラ

0

MATベクトルを方向ベクトルに投影するだけです。結果のベクターを方向ベクトルに追加して、浸透を補正できます。SATを行うときにAxisで行うのと同じ方法で投影します。これにより、オブジェクトは、他のオブジェクトと接触する位置に正確に設定されます。小さなイプシロンを追加して、浮動小数点の問題と戦います。


1
「MAT Vector」?「MTV」という意味ですか?
タラ

0

私の答えにはいくつかの注意点があります。最初に邪魔にならないようにしましょう。非回転の境界ボックスのみを扱います。トンネルの問題、つまり高速で移動するオブジェクトに起因する問題に対処しようとしていることを前提としています。

MTVを特定したら、テストする必要があるエッジ/サーフェス法線がわかります。また、相互貫通オブジェクトの線速度ベクトルも知っています。

あなたがでていることを確立した後、いくつかのポイントフレームの間に、交差点が発生しました、あなたはその後、以下の出発点に基づいて、バイナリハーフステップの操作を実行できます。フレームの間に第1の浸透というの頂点を識別します。

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;

これはかなり正確ですが、単一の場合に単一の衝突点のみを提供します。

問題は、通常、オブジェクトがこのようにトンネルを通過するのに十分な速度でフレームごとに移動するかどうかを事前に知ることができるため、最良のアドバイスは、速度に沿って先頭の頂点を特定し、速度ベクトルに沿って光線テストを行うことです。オブジェクトを回転させる場合、正しい接触点をアサートするには、何らかのバイナリハーフステップslerpを実行する必要があります。

ただし、ほとんどの場合、シーン内のほとんどのオブジェクトは単一フレームでそこまで浸透するのに十分な速度で移動しないため、ハーフステップは不要であり、個別の衝突検出で十分であると安全に想定できます。弾丸のように高速で見にくいオブジェクトは、接触点でレイトレースできます。

興味深いことに、このハーフステップメソッドは、フレーム中にオブジェクトが発生した(ほぼ)正確な時間も提供できます。

float collisionTime = frametimeSeconds * halfstep;

何らかの物理衝突解決を行っている場合は、次の方法でAの位置を修正できます。

v - (v*halfstep)

そこから普通に物理学を行うことができます。欠点は、オブジェクトが適度に高速で移動すると、速度ベクトルに沿ってテレポートして戻ることです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.