Tìm điểm liên lạc với SAT


12

Định lý trục tách (SAT) giúp đơn giản xác định Vector dịch tối thiểu, tức là vectơ ngắn nhất có thể tách hai đối tượng va chạm. Tuy nhiên, điều tôi cần là vectơ phân tách các đối tượng dọc theo vectơ mà đối tượng thâm nhập đang di chuyển (tức là điểm tiếp xúc).

Tôi đã vẽ một bức tranh để giúp làm rõ. Có một hộp, di chuyển từ vị trí trước sang vị trí sau. Ở vị trí sau, nó giao với đa giác màu xám. SAT có thể dễ dàng trả lại MTV, đó là vector màu đỏ. Tôi đang tìm cách tính toán các vectơ màu xanh.

Sơ đồ SAT

Giải pháp hiện tại của tôi thực hiện tìm kiếm nhị phân giữa các vị trí trước và sau cho đến khi độ dài của vectơ màu xanh được biết đến một ngưỡng nhất định. Nó hoạt động nhưng đó là một tính toán rất tốn kém vì sự va chạm giữa các hình dạng cần phải được tính toán lại mỗi vòng lặp.

Có cách nào đơn giản và / hoặc hiệu quả hơn để tìm vectơ điểm tiếp xúc không?


1
Bạn đã chết khi sử dụng SAT? Các thuật toán như MPR (sàng lọc cổng thông tin Minkowski) có thể tìm thấy đa tạp liên hệ trực tiếp. Với SAT và GJK, bạn cần một thuật toán riêng để tính các điểm tiếp xúc.
Sean Middleditch

Câu trả lời:


6

Điều bạn đang nói khá khó khăn nếu bạn cấu trúc nó như là lần đầu tiên di chuyển một vật thể, sau đó kiểm tra va chạm, sau đó lùi lại cho đến khi bạn ra khỏi vật thể. Có lẽ tốt hơn khi nghĩ về điều này như một thử nghiệm giao nhau động : một vật thể chuyển động chống lại một vật thể đứng yên.

May mắn thay, kiểm tra trục tách có thể giúp bạn ở đây! Dưới đây là mô tả về thuật toán, lịch sự của Ron Levine :

Các thuật toán đi như thế này. Bạn làm việc với vectơ vận tốc tương đối của hai cơ thể lồi. Chiếu mỗi hai vật thể và vectơ vận tốc tương đối lên một trục phân tách cụ thể tại t ₀ cho hai khoảng 1-D và vận tốc 1-D, để dễ dàng biết liệu hai khoảng đó có giao nhau hay không, và nếu không, liệu có hay không họ đang di chuyển xa nhau hoặc di chuyển cùng nhau. Nếu chúng được tách ra và di chuyển tách biệt trên bất kỳ trục phân tách nào (hoặc trên thực tế, trên bất kỳ trục nào), thì bạn biết rằng không có va chạm trong tương lai. Nếu trên bất kỳ trục phân tách nào, hai khoảng chiếu được cắt nhau tại tHoặc được tách ra và đang di chuyển cùng nhau, sau đó dễ dàng tính toán (bằng hai biểu thức tuyến tính 1D đơn giản) thời gian sớm nhất trong đó hai khoảng thời gian đầu tiên sẽ giao nhau và (giả sử tiếp tục chuyển động thẳng) trong thời gian tương lai gần nhất mà hai khoảng thời gian sẽ giao nhau và bắt đầu di chuyển xa nhau. (Nếu chúng giao nhau tại t thì thời gian giao nhau sớm nhất trong tương lai là t ). Làm điều này cho hầu hết tất cả các trục tách. Nếu tối đa trên tất cả các trục của thời gian giao nhau trong tương lai sớm nhất nhỏ hơn tối thiểu trên tất cả các trục của thời gian giao cắt trong tương lai gần nhất thì thời gian giao nhau trong tương lai sớm nhất đó là thời gian va chạm đầu tiên của hai khối đa diện 3D, nếu không thì không có va chạm trong tương lai.

Nói cách khác, bạn lặp qua tất cả các trục mà bạn thường làm trong bài kiểm tra trục tách tĩnh. Thay vì ra ngoài sớm nếu bạn thấy không có sự trùng lặp, bạn tiếp tục đi và kiểm tra vận tốc dự kiến của vật chuyển động. Nếu nó di chuyển ra khỏi đối tượng tĩnh, thì bạn sẽ sớm ra ngoài. Mặt khác, bạn có thể giải quyết thời gian liên lạc sớm nhất và sớm nhất khá dễ dàng (đó là khoảng thời gian 1D chuyển sang khoảng thời gian 1D khác). Nếu bạn làm điều đó cho tất cả các trục và giữ tối đa thời gian giao nhau sớm nhất và tối thiểu của thời gian giao nhau gần nhất, thì bạn sẽ biết liệu đối tượng chuyển động của mình sẽ chạm vào đối tượng tĩnh, cũng như khi nào. Vì vậy, bạn có thể tiến lên đối tượng chuyển động của mình chính xác đến điểm mà nó sẽ chạm vào đối tượng tĩnh.

Đây là một số mã giả thô và hoàn toàn chưa được xác minh cho thuật toán:

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

Đây là một bài viết Gamasutra nói về điều này được thực hiện cho một vài thử nghiệm nguyên thủy khác nhau. Lưu ý rằng giống như SAT, điều này đòi hỏi các đối tượng lồi.

Ngoài ra, đây là một chút phức tạp hơn một thử nghiệm trục tách đơn giản. Hãy chắc chắn rằng bạn cần nó trước khi bạn thử nó. Một số lượng lớn các trò chơi chỉ đơn giản là đẩy các vật thể ra khỏi nhau dọc theo vectơ dịch tối thiểu, bởi vì chúng đơn giản không xâm nhập rất xa vào nhau trên bất kỳ khung hình cụ thể nào và nó không thể nhận thấy bằng mắt thường.


2
Điều này rất tuyệt, nhưng nó không trả lời trực tiếp câu hỏi về tính toán đa tạp liên hệ. Ngoài ra, nếu tôi hiểu chính xác, câu trả lời này chỉ hoạt động với vận tốc tuyến tính và do đó không thể hỗ trợ các vật thể quay; không chắc người hỏi có muốn điều đó hay không.
Sean Middleditch

1
@seanmiddleditch Điều đó đúng, nó bỏ qua các góc quay trên khung. Bạn phải xoay ngay lập tức khi bắt đầu. Nhưng không có phương pháp nào mà tôi biết thiếu sự tiến bộ bảo thủ thực sự giải quyết chính xác các phép quay. Tuy nhiên, do không có phép quay, nó tạo ra ước tính tốt hơn về điểm tiếp xúc.
John Calsbeek

2

Bạn muốn sử dụng cắt đa giác. Điều này được giải thích tốt nhất bằng những bức ảnh mà tôi không có, nhưng anh chàng này đã làm, vì vậy tôi sẽ để anh ta giải thích.

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

Đường ống tiếp xúc sẽ trả về một điểm trên một trong những vật thể "chịu trách nhiệm cao nhất" cho vụ va chạm, chứ không phải là điểm va chạm trực tiếp. Tuy nhiên, bạn không thực sự cần điểm va chạm trực tiếp đó. Bạn có thể chỉ cần đẩy các vật thể ra xa nhau bằng cách sử dụng độ sâu thâm nhập và bình thường bạn đã có, và sử dụng đa tạp tiếp xúc để áp dụng các ảnh hưởng vật lý khác (ví dụ, làm cho hộp rơi xuống / lăn xuống dốc).

Lưu ý rằng hình ảnh của bạn minh họa một vấn đề nhỏ: điểm trên vectơ màu xanh mà bạn yêu cầu sẽ không được tìm thấy trong bất kỳ mô phỏng vật lý nào, bởi vì đó thực sự không phải là nơi chiếc hộp sẽ chạm vào. Chiếc hộp sẽ va vào góc dưới bên trái của nó ở đâu đó lên dốc khi chỉ một chút góc nhỏ thâm nhập.

Độ sâu thâm nhập sẽ tương đối nhỏ, và chỉ cần đẩy hộp ra khỏi độ dốc dọc theo độ xuyên thấu thông thường sẽ đặt hộp đủ gần với vị trí "chính xác" để gần như không thể nhận thấy trong thực tế, đặc biệt là nếu hộp sẽ bị bật, vỡ hoặc trượt sau đó.


Bạn có biết có cách nào để tính "vectơ màu xanh" đó không (cách cần thiết để đẩy vật trở lại khỏi hình dạng dọc theo vectơ vận tốc) bằng SAT?
Tara

@Dudeson: không sử dụng SAT, không. Đó không phải là những gì SAT làm. SAT cung cấp cho bạn các cạnh của độ sâu thâm nhập tối thiểu, không phải là cạnh tiếp xúc đầu tiên. Bạn nghĩ rằng bạn phải sử dụng tính năng phát hiện va chạm hình dạng quét để làm những gì bạn yêu cầu.
Sean Middleditch

Tôi biết SAT làm gì. Tôi đã thực hiện nó trước đây. Nhưng có một vấn đề tôi phải đối mặt sẽ được giải quyết nếu tôi chỉ có thể sử dụng đầu ra của SAT để tính cạnh tiếp xúc đầu tiên. Cũng xem câu trả lời của "someguy". Nó cho thấy nó có thể nhưng không giải thích nó rất tốt.
Tara

@Dudeson: Cạnh / trục của sự thâm nhập ít nhất không nhất thiết là cạnh của lần tiếp xúc đầu tiên, vì vậy tôi vẫn không thấy SAT giúp ở đây như thế nào. Tôi không có nghĩa là một chuyên gia trong chủ đề này vì vậy tôi thừa nhận rằng tôi có thể sai. :)
Sean Middleditch

Chính xác. Đó là lý do tại sao tôi không chắc chắn nếu điều này thậm chí có thể. Điều đó có nghĩa là mặc dù, câu trả lời của someguy là sai. Nhưng dù sao cũng cảm ơn sự giúp đỡ! : D
Tara

0

Chỉ cần chiếu Vector MAT lên hướng Vector. Vector kết quả có thể được thêm vào Direction Vector để bù cho sự thâm nhập. Chiếu nó theo cách tương tự, giống như bạn làm trên Trục khi làm SAT. Điều này đặt đối tượng chính xác vào vị trí mà nó chạm vào đối tượng khác. Thêm một epsilon nhỏ để chống lại các vấn đề điểm nổi.


1
"Vectơ"? Bạn có nghĩa là "MTV"?
Tara

0

Có một vài lời cảnh báo cho câu trả lời của tôi, rằng tôi sẽ tránh đường trước: Nó chỉ liên quan đến các hộp giới hạn không quay. Nó giả định rằng bạn đang cố gắng giải quyết các vấn đề đường hầm , tức là các vấn đề gây ra bởi các vật thể di chuyển ở tốc độ cao.

Khi bạn đã xác định được MTV, bạn biết cạnh / bề mặt bình thường bạn cần kiểm tra lại. Bạn cũng biết vectơ vận tốc tuyến tính của đối tượng xen kẽ.

Khi bạn đã thiết lập rằng tại một số điểm trong khung hình, một giao lộ đã xảy ra, sau đó bạn có thể thực hiện các thao tác nửa bước nhị phân, dựa trên các điểm bắt đầu sau: Xác định đỉnh xâm nhập đầu tiên trong khung:

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

Khi bạn đã xác định được đỉnh, nửa bước nhị phân trở nên rẻ hơn rất nhiều:

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

Điều này là chính xác hợp lý, nhưng sẽ chỉ cung cấp một điểm va chạm duy nhất, trong một trường hợp duy nhất.

Vấn đề là, thường có thể nói trước liệu một vật thể sẽ di chuyển đủ nhanh trên mỗi khung hình để có thể chui như thế này, vì vậy lời khuyên tốt nhất là xác định các đỉnh dẫn dọc theo vận tốc và thực hiện kiểm tra tia dọc theo vectơ vận tốc. Trong trường hợp các đối tượng quay, bạn sẽ phải thực hiện một số loại trượt nửa bước nhị phân để xác nhận điểm tiếp xúc chính xác.

Tuy nhiên, trong hầu hết các trường hợp, có thể giả định một cách an toàn rằng hầu hết các đối tượng trong cảnh của bạn sẽ không di chuyển đủ nhanh để xâm nhập đến mức đó trong một khung hình duy nhất, do đó không cần phải thực hiện nửa bước và phát hiện va chạm rời rạc. Các vật thể tốc độ cao như đạn, di chuyển quá nhanh để nhìn thấy, có thể được chiếu tia cho các điểm tiếp xúc.

Thật thú vị, phương pháp nửa bước này cũng có thể cung cấp cho bạn thời gian (gần như) chính xác mà đối tượng xảy ra trong khung:

float collisionTime = frametimeSeconds * halfstep;

Nếu bạn đang thực hiện một số loại giải quyết va chạm vật lý, thì bạn có thể sửa vị trí của A bằng cách:

v - (v*halfstep)

sau đó bạn có thể làm vật lý của bạn bình thường từ đó. Nhược điểm là nếu đối tượng di chuyển hợp lý nhanh, bạn sẽ thấy nó dịch chuyển tức thời dọc theo vectơ vận tốc của nó.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.