Por que os objetos interpenetram neste solucionador de colisões simples?

7

O código abaixo é de um exemplo do Microsoft XNA aqui . Essa é uma simulação bastante simples do corpo rígido que ignora muitos efeitos físicos (como o momento angular), mas tenta separar objetos (esferas) para que eles não se penetrem.

No entanto, a simulação permite que as esferas não apenas penetrem, mas quando muitas esferas são empilhadas umas sobre as outras, pequenas esferas podem estar quase completamente dentro de esferas maiores. Se eu fizer todas as esferas com o mesmo raio e massa, a simulação terá um desempenho razoavelmente bom (com interpenetração mínima).

Alguém pode explicar por que existe alguma interpenetração? Uma vez que move as posições das esferas, parece que a interpenetração deve ser impossível.

Para cada esfera na simulação, esse método é chamado em todas as outras esferas.

/// <summary>
// Given 2 spheres with velocity, mass and size, evaluate whether
// a collision occured, and if so, excatly where, and move sphere 2
// at the contact point with sphere 1, and generate new velocities.
/// </summary>
private void SphereCollisionImplicit(Sphere sphere1, Sphere sphere2)
{
    const float K_ELASTIC = 0.75f;

    Vector3 relativepos = sphere2.Position - sphere1.Position;
    float distance = relativepos.Length();
    float radii = sphere1.Radius + sphere2.Radius;
    if (distance >= radii)
    {
        return; // No collision
    }

    // Add epsilon to avoid NaN.
    distance += 0.000001f;

    Vector3 relativeUnit = relativepos * (1.0f / distance);
    Vector3 penetration = relativeUnit * (radii - distance);

    // Adjust the spheres' relative positions
    float mass1 = sphere1.Mass;
    float mass2 = sphere2.Mass;

    float m_inv = 1.0f / (mass1 + mass2);
    float weight1 = mass1 * m_inv; // relative weight of sphere 1
    float weight2 = mass2 * m_inv; // relative weight of sphere 2. w1+w2==1.0

    sphere1.Position -= weight2 * penetration;
    sphere2.Position += weight1 * penetration;

    // Adjust the objects’ relative velocities, if they are
    // moving toward each other.
    //
    // Note that we're assuming no friction, or equivalently, no angular momentum.
    //
    // velocityTotal = velocity of v2 in v1 stationary ref. frame
    // get reference frame of common center of mass
    Vector3 velocity1 = sphere1.Velocity;
    Vector3 velocity2 = sphere2.Velocity;

    Vector3 velocityTotal = velocity1 * weight1 + velocity2 * weight2;
    Vector3 i2 = (velocity2 - velocityTotal) * mass2;
    if (Vector3.Dot(i2, relativeUnit) < 0)
    {
        // i1+i2 == 0, approx
        Vector3 di = Vector3.Dot(i2, relativeUnit) * relativeUnit;
        i2 -= di * (K_ELASTIC + 1);
        sphere1.Velocity = (-i2) / mass1 + velocityTotal;
        sphere2.Velocity = i2 / mass2 + velocityTotal;
    }
}

Em particular, eu acho que isso:

sphere1.Position -= weight2 * penetration;
sphere2.Position += weight1 * penetration;

Deve proibir completamente qualquer interpenetração, por que não?

Olhovsky
fonte

Respostas:

5

Acho que seu problema vem de várias colisões.

Considere três esferas A, B e C.

A fica lá.

B bate A. Bem.

C atinge B. B é empurrado para A. Opa.

Loren Pechtel
fonte
Sim, mas agora C corrigirá sua posição contra A e depois contra B. Entendo o que você quer dizer, mas os exemplos que trabalhei com três esferas parecem funcionar - então não tenho certeza se esse é realmente o problema. Além disso, acredito que esse método iterativo é exatamente como a maioria das simulações de física de jogos resolve colisões. Mais uma vez, eu posso estar errado, não tenho certeza. Talvez a solução de colisões, uma de cada vez, evite a interpenetração, se você usar a detecção contínua de colisões.
Olhovsky
Isso pode realmente ser exatamente o motivo. Brandon sugeriu algo semelhante. +1 para a contribuição útil, seja a razão exata ou não.
Olhovsky
5

Este código está pegando a colisão entre duas esferas e movendo-as com base em suas massas e velocidade.

Aqui está uma ilustração realmente básica para mostrar por que há penetração entre uma bola azul e uma parede verde.

insira a descrição da imagem aqui

Neste exemplo, o centro da bola fica a 1,5 metro da borda da parede. A bola está se movendo a 1 pé por segundo e você está correndo com uma taxa de quadros de 1 quadro por segundo. No quadro 4 não há penetração, mas o quadro 5 terá penetração. Não há como saber que o quadro 5 colidirá, então você não pode simplesmente dizer parar quando ele chegar à parede. Em vez disso, verifique cada quadro se a bola estiver parcialmente dentro da parede. Se estiver, mova-o de volta pela quantidade de penetração e aplique a velocidade reflexiva. É assim que a maioria dos algoritmos de detecção de colisão funciona. A alternativa é verificar cada quadro a que distância você está colidindo em uma determinada direção. O problema é que você precisa saber qual direção verificar. Isso não funcionaria na sua situação, portanto o caminho que você está seguindo é o caminho certo.

A desvantagem dessa abordagem é o nervosismo que você provavelmente está enfrentando. Quando esse script instrui uma bola a se mover com base na última colisão, ela pode estar se movendo para penetrar em outra bola, o que fará com que a bola volte a colidir com a primeira. Fazendo-o pular para frente e para trás. Você pode modificar a massa dos objetos para ajudar nisso. Isso fará com que eles recuem um pouco menos drasticamente ou reduzam a velocidade com que colidem.

brandon
fonte
Na verdade, o problema não é a "agitação" (a instabilidade). Eu estou bem com a instabilidade e posso corrigir isso mais tarde com um limite de correção de distância ao custo da precisão. O problema que estou tendo (bem, e que a Microsoft está tendo, como é o código deles) é que a interpenetração não está sendo resolvida. Ou seja, não é apenas nervoso, mas também há muita interpenetração, apesar do código parecer resolver a penetração. Alguma idéia do porquê disso? Perdi alguma coisa na sua resposta? Obrigado pela entrada btw!
Olhovsky
Ah, acho que você está dizendo que várias colisões não se resolvem corretamente devido a várias bolas serem empurradas uma para a outra, semelhante à resposta de Loren - está certo?
Olhovsky
Certo, isso é o nervosismo, o que também faz com que elas não colidem corretamente
Brandon
Bem, na verdade não. Instabilidade! = Interpenetração. Os objetos podem tremer (devido à resolução da penetração) sem interpenetrar. De qualquer forma +1 :)
Olhovsky
5

Você deve ler sobre contatos especulativos para quando ocorrerão colisões antes de acabar com esse tipo de penetração.

Jason Roelofs
fonte
+1 para esse bom link. Na verdade, comprei uma assinatura no blog de Paul ontem por causa desse artigo.
Olhovsky
1

SphereCollisionImplicit () é chamado depois que as esferas foram movidas. Esse movimento poderia ter acabado com colisão e interpenetração, esse método resolve isso.

quadro inicial:

  1. em outro lugar no seu código, a posição é atualizada pela velocidade.

  2. Então, esse método é chamado para verificar se isso resultou em colisão / penetração.

  3. Nesse caso, ele os separa e reflete / altera suas velocidades.

  4. No próximo quadro, as posições são atualizadas com velocidade nova / alterada ... goto 1.

Steve H
fonte
Não entendo o que você está tentando dizer. Atualmente nessa amostra de XNA, ele faz exatamente o que dizem seus pontos de bala, e o desenho das bolas ocorre logo após a etapa 3. Então, por que ainda existe interpenetração? Como você disse, esperamos que a etapa 3 os separe.
Olhovsky
Os passos 1 e 4 podem causar penetração; o passo 3 remove a penetração, se ocorreu.
Steve H
O objetivo desta pergunta é que a penetração não está sendo completamente removida. Veja outras respostas para saber o motivo.
Olhovsky
1

Existe interpenetração porque, toda vez que a posição e a velocidade de uma bola são alteradas, ela precisa ser verificada para verificar se colidiu com outra bola que foi verificada anteriormente no loop. Por exemplo, dadas 3 bolas:

Partida 1. A bola # A é checada contra a bola #B, sem colisão

  1. A bola # A é checada contra a bola # C, sem colisão

  2. A bola # B é verificada contra a bola # A, sem colisão

  3. A bola # B é verificada contra a bola # C, encontrou colisão !! A bola # B e # C são movidas.

  4. A bola # C é marcada contra a bola # A, sem colisão

  5. A bola # C é verificada contra a bola # B, sem colisão

Feito

Então ... A Etapa 2 pode ter movido a Bola # B para a Bola # A, causando interpenetração.

Para consertar, acho que sempre que uma bola é movida, ela precisa ser verificada novamente com todas as outras bolas, sempre que for movida. Nesse cenário, a bola # B esbarra na bola # C, a bola richocheting # B na bola # A, a bola richocheting # A na bola # C !, que atinge a bola # B novamente ....

Quantos richochets você processa em um quadro? Você também precisa verificar novamente todas as paredes após cada richochet?

Partida 1. A bola # A é checada contra a bola #B, sem colisão

  1. A bola # A é checada contra a bola # C, sem colisão

  2. A bola # B é verificada contra a bola # A, sem colisão

  3. A bola # B é verificada contra a bola # C, encontrou colisão !! A bola # B e # C são movidas.

  4. Reinicie o loop #B e #C ...

  5. A bola # B é verificada contra a bola # A, encontrou uma colisão !! A bola #B e #A são movidas

  6. Reinicie o loop #B e #A ...

  7. A bola # A é checada contra a bola #B, sem colisão

  8. A bola # A é verificada contra a bola # C, colisão encontrada !! A bola # A e #C são movidas

  9. Reinicie o loop #A e #C

  10. A bola # A é checada contra a bola #B, sem colisão

  11. A bola # A é checada contra a bola # C, sem colisão

  12. A bola # B é verificada contra a bola # A, sem colisão

  13. A bola # B é verificada contra a bola # C, encontrou colisão !! A bola #B e #C são movidas

  14. Reinicie o loop #B e #C

  15. A bola # B é verificada contra a bola # A, sem colisão

  16. A bola # B é marcada contra a bola # C, sem colisão

  17. A bola # C é marcada contra a bola # A, sem colisão

  18. A bola # C é marcada contra a bola # B, sem colisão Concluída

fuul
fonte
Na verdade, não é isso. As colisões são verificadas em todas as outras bolas, o problema está na ordem dos cheques (como sugerido pela resposta com a marca de seleção verde :). Verificar todas as outras bolas não é suficiente por si só.
precisa