SAT ile iletişim noktasını bulma


12

Ayırma Ekseni Teoremi (SAT), Minimum Çeviri Vektörünü, yani iki çarpışan nesneyi ayırabilecek en kısa vektörü belirlemeyi kolaylaştırır. Ancak, ihtiyacım olan şey, nüfuz eden nesnenin hareket ettiği (yani temas noktası) vektör boyunca nesneleri ayıran vektör.

Açıklığa kavuşturmak için bir resim çizdim. Önceden sonrakine geçen bir kutu vardır. Sonrasında gri poligonla kesişir. SAT, kırmızı vektör olan MTV'yi kolayca döndürebilir. Mavi vektörü hesaplamak istiyorum.

SAT diyagramı

Mevcut çözümüm, mavi vektörün uzunluğu belirli bir eşik tarafından bilinene kadar önceki ve sonraki konumlar arasında ikili bir arama gerçekleştirir. Çalışır, ancak şekiller arasındaki çarpışmanın her döngüde yeniden hesaplanması gerektiğinden çok pahalı bir hesaplamadır.

Temas noktası vektörünü bulmanın daha basit ve / veya daha etkili bir yolu var mı?


1
SAT'ı kullanmaya hazır mısın? MPR (Minkowski Portal İyileştirme) gibi algoritmalar kontak manifoldunu doğrudan bulabilir. SAT ve GJK ile, temas noktalarını hesaplamak için ayrı bir algoritmaya ihtiyacınız vardır.
Sean Middleditch

Yanıtlar:


6

Eğer önce bir nesneyi hareket ettirip sonra çarpışmayı test edip sonra nesne dışına çıkana kadar geri çekiliyorsanız, bahsettiğiniz şey oldukça zordur. Bunu dinamik bir kavşak testi olarak düşünmek daha iyidir : sabit bir nesneye karşı hareket eden bir nesne.

Neyse ki, eksen testlerini ayırmak size yardımcı olabilir! İşte Ron Levine'nin izniyle algoritmanın açıklaması :

Algoritma böyle gider. İki dışbükey cismin göreli hız vektörü ile çalışırsınız. İki cismin her birini ve bağıl hız vektörünü t at'de belirli bir ayırma eksenine yansıtmak, iki 1-B aralığı ve 1-B hızı verir, böylece iki aralığın kesişip kesişmediğini ve değilse, birbirlerinden uzaklaşıyorlar veya birlikte hareket ediyorlar. Eğer ayrılırlar ve ayırıcı eksenlerden herhangi birinde (veya aslında herhangi bir eksende) hareket ederse, gelecekteki bir çarpışma olmadığını bilirsiniz. Herhangi bir ayırma ekseni üzerinde, yansıtılan iki aralık t₀ veya ayrılır ve birlikte hareket ederse, iki aralığın ilk önce kesişeceği en erken gelecekteki zamanı (iki düz 1D doğrusal ifade ile) hesaplamak kolaydır ve (devam eden doğrusal hareket varsayarak) aralıklar kesişecek ve birbirinden uzaklaşmaya başlayacaktır. ( T ₀ ile kesişiyorlarsa, gelecekteki en erken kavşak süresi t ₀ olur). Bunu en fazla tüm ayırma eksenleri için yapın. Gelecekteki en erken kavşak zamanının tüm eksenleri üzerindeki maksimum, en son gelecekteki kavşak zamanının tüm eksenleri üzerindeki minimumdan azsa, o zaman gelecekteki en erken gelecekteki en yüksek kavşak süresi, iki 3B polihedranın ilk çarpışmasının kesin zamanıdır. gelecekte bir çarpışma değildir.

Başka bir deyişle, statik bir ayırma ekseni testinde normalde yapacağınız tüm eksenler arasında geçiş yaparsınız. Çakışma bulamazsanız erken çıkış yapmak yerine , hareket eden nesnenin tahmini hızını kontrol edersiniz . Eğer statik nesneden Uzaklaşıyor sonra erken aşımı. Aksi takdirde, en erken ve en son temas süresini oldukça kolay bir şekilde çözebilirsiniz (bir 1D aralığı başka bir 1D aralığına doğru hareket eder). Bunu tüm eksenler için yaparsanız ve en erken kavşak süresinin maksimumunu ve en son kavşak süresinin minimumunu tutarsanız, hareketli nesnenizin statik nesneye ve ne zaman çarpacağını bilirsiniz. Böylece hareketli nesnenizi tam olarak statik nesneye çarpacağı noktaya kadar ilerletebilirsiniz.

İşte algoritma için bazı kaba ve tamamen doğrulanmamış sahte kod:

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

Birkaç farklı ilkel test için uygulanan bir Gamasutra makalesi . SAT gibi, bunun da dışbükey nesneler gerektirdiğini unutmayın.

Ayrıca, bu basit bir ayırma ekseni testinden biraz daha karmaşıktır. Denemeden önce mutlaka ihtiyacınız olduğundan emin olun. Çok sayıda oyun, nesneleri minimum çeviri vektörü boyunca birbirinden iter, çünkü herhangi bir çerçevede birbirine çok fazla nüfuz etmezler ve görsel olarak hemen hemen fark edilmezler.


2
Bunların hepsi çok güzel, ancak kontak manifoldunun hesaplanması hakkındaki soruya doğrudan cevap vermedi. Ayrıca, doğru anlarsam, bu cevap sadece doğrusal hız ile çalışır ve bu nedenle dönen nesneleri destekleyemez; askerin bunu isteyip istemediğinden emin değilim.
Sean Middleditch

1
@seanmiddleditch Bu doğru, çerçeve üzerindeki dönüşleri ihmal ediyor. Başlangıçta hemen dönmelisiniz. Ancak muhafazakar ilerlemenin yetersiz olduğunu bildiğim hiçbir yöntem aslında rotasyonlarla doğru bir şekilde ilgilenmiyor. Bununla birlikte, hiçbir rotasyon verilmediğinde, temas noktasının daha iyi bir tahminini üretir.
John Calsbeek

2

Çokgen kırpma kullanmak istiyorsunuz. Bu en iyi sahip olmadığım resimlerle açıklanır, ancak bu adam yaptı, bu yüzden açıklamasına izin vereceğim.

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

Kontak manifoldu, çarpışmadan "en sorumlu" olan nesnelerden birinde, doğrudan çarpışma noktasında değil, bir nokta döndürür. Ancak, bu doğrudan çarpışma noktasına gerçekten ihtiyacınız yok. Zaten penetrasyon derinliğini ve zaten sahip olduğunuz normali kullanarak nesneleri birbirinden itebilir ve diğer fiziksel etkileri uygulamak için kontak manifoldunu kullanabilirsiniz (örneğin kutuyu yuvarlayın / eğimi aşağı doğru yapın).

Resminizin küçük bir problemi gösterdiğini unutmayın: Mavi vektörün üzerindeki nokta herhangi bir fiziksel simülasyonda bulunmayacaktır, çünkü aslında kutunun çarptığı yer bu değildir. Kutu, sol alt köşesiyle, köşenin sadece küçük bir kısmı nüfuz ederken, eğim üzerinde bir yere vuracaktı.

Penetrasyon derinliği nispeten küçük olacaktır ve kutuyu normalde penetrasyon boyunca eğimin dışına itmek, kutuyu pratikte neredeyse farkedilemeyecek kadar “doğru” pozisyona yeterince yaklaştırır, özellikle de kutu zıplayacaksa, takla veya daha sonra yine de kaydırın.


SAT kullanarak bu "mavi vektörü" (nesneyi hız vektörü boyunca şeklin dışına itmek için gerekli olan) hesaplamanın bir yolu olup olmadığını biliyor musunuz?
Tara

@ Ahbap: SAT kullanmıyorum, hayır. SAT bunu yapmaz. SAT size ilk temas kenarını değil, minimum giriş derinliğinin kenarını verir. Sorduğun şeyi yapmak için süpürülmüş şekil çarpışma algılaması kullanmalısın bence.
Sean Middleditch

SAT'ın ne yaptığını biliyorum. Daha önce uygulamıştım. Ama ilk temas kenarını hesaplamak için SAT çıkışını kullanabilirsem çözülecek bir sorun var. Ayrıca bkz. Mümkün olduğunu gösteriyor ama çok iyi açıklamıyor.
Tara

@Dudeson: En az penetrasyonun kenarı / ekseni mutlaka ilk temasın kenarı değildir, bu yüzden SAT'ın burada nasıl yardımcı olduğunu göremiyorum. Bu konuda uzman değilim, bu yüzden yanlış olabileceğimi itiraf ediyorum. :)
Sean Middleditch

Kesinlikle. Bu yüzden bunun mümkün olup olmadığından emin değilim. Ancak bu, someguy'un cevabının yanlış olduğunu ima eder. Ama yine de yardım için teşekkürler! : D
Tara

0

Sadece MAT Vector öğesini Vector yönüne yansıtın. Elde edilen Vector, penetrasyonu telafi etmek için Yön Vektörüne eklenebilir. SAT yaparken Eksen üzerinde yaptığınız gibi aynı şekilde yansıtın. Bu, Nesneyi tam olarak diğer nesneye dokunduğu konuma ayarlar. Kayan nokta problemleriyle savaşmak için küçük bir epsilon ekleyin.


1
"MAT Vektör"? Şunu mu demek istediniz: "MTV"?
Tara

0

Cevabımdan önce yoldan çıkacağım için birkaç uyarı var: Sadece dönmeyen sınırlama kutuları ile ilgileniyor. Tünel açma sorunlarını, yani yüksek hızda hareket eden nesnelerin neden olduğu sorunları ele almaya çalıştığınızı varsayar .

MTV'yi belirledikten sonra, test etmeniz gereken kenar / yüzey normalini bilirsiniz. Ayrıca, iç içe geçen nesnenin doğrusal hız vektörünü de bilirsiniz.

En tespit sonra bir noktada dilimi içinde, bir kesişme oluştu, daha sonra takip eden başlangıç noktaları göre, ikili yarım adım işlemleri gerçekleştirebilir: çerçeve sırasında birinci nüfuz tepe belirlenmesi:

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

Tepe noktası belirlendikten sonra, ikili yarım adım çok daha ucuz hale gelir:

//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;

Bu oldukça doğrudur, ancak tek bir durumda yalnızca tek bir çarpışma noktası sağlayacaktır.

Mesele şu ki, bir nesnenin böyle tünelleme yapabilmek için kare başına yeterince hızlı hareket edip etmeyeceğini önceden söylemek mümkündür, bu nedenle en iyi tavsiye hız boyunca önde gelen köşeleri tanımlamak ve hız vektörü boyunca bir ışın testi yapmaktır. Dönen nesneler söz konusu olduğunda, doğru temas noktasını belirlemek için bir çeşit ikili yarım adımlı slerp yapmanız gerekir.

Bununla birlikte, çoğu durumda, sahnenizdeki çoğu nesnenin tek bir karede o kadar nüfuz edecek kadar hızlı hareket edemeyeceği, bu nedenle yarım adım gerekmeyeceği ve ayrık çarpışma tespitinin yeterli olacağı güvenli bir şekilde kabul edilebilir. Görmek için çok hızlı hareket eden mermiler gibi yüksek hızlı nesneler, temas noktaları için ışınlanabilir.

İlginç bir şekilde, bu yarım adım yöntemi, nesnenin çerçeve sırasında meydana geldiği (neredeyse) kesin zamanı da verebilir:

float collisionTime = frametimeSeconds * halfstep;

Bir tür fizik çarpışma çözünürlüğü yapıyorsanız, A'nın konumunu şu şekilde düzeltebilirsiniz:

v - (v*halfstep)

o zaman fiziğinizi normal olarak oradan yapabilirsiniz. Dezavantajı, eğer nesne oldukça hızlı hareket ederse, hız vektörü boyunca geri ışınlandığını göreceksiniz.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.