중력 계산 최적화


23

크기와 속도가 다양한 물체가 서로를 향해 끌고 있습니다. 매번 업데이트 할 때마다 모든 객체를 살펴보고 다른 모든 객체의 중력으로 인한 힘을 더해야합니다. 확장 성이 떨어지고 게임에서 찾은 두 가지 큰 병목 현상 중 하나이며 성능을 개선하기 위해 어떻게해야할지 잘 모르겠습니다.

그것은 느낌이 나는 성능을 향상시킬 수있을 것 같은. 주어진 시간에 시스템의 객체 중 99 %는 객체에 거의 영향을 미치지 않습니다. 물론 물체를 질량별로 정렬 할 수 없으며 질량이 물체보다 거리에 따라 힘이 변하기 때문에 상위 10 개의 가장 큰 물체 만 고려할 수 있습니다 (방정식은의 선을 따릅니다 force = mass1 * mass2 / distance^2). 나는 가장 큰 물체 가장 가까운 물체 를 고려하여 세계의 반대편에있는 수백 개의 작은 바위 조각을 무시하고 아무것도 영향을 미치지 않을 것이라고 생각합니다. 그러나 어떤 물체가 어떤 물체인지 알아 내기 위해 가장 가까운 모든 객체 를 반복해야 하며 위치가 지속적 으로 변경 되고 있습니다.한 번만 할 수있는 것은 아닙니다.

현재 나는 다음과 같은 일을하고있다 :

private void UpdateBodies(List<GravitatingObject> bodies, GameTime gameTime)
{
    for (int i = 0; i < bodies.Count; i++)
    {
        bodies[i].Update(i);
    }
}

//...

public virtual void Update(int systemIndex)
{
    for (int i = systemIndex + 1; i < system.MassiveBodies.Count; i++)
    {
        GravitatingObject body = system.MassiveBodies[i];

        Vector2 force = Gravity.ForceUnderGravity(body, this);
        ForceOfGravity += force;
        body.ForceOfGravity += -force;
    }

    Vector2 acceleration = Motion.Acceleration(ForceOfGravity, Mass);
    ForceOfGravity = Vector2.Zero;

    Velocity += Motion.Velocity(acceleration, elapsedTime);
    Position += Motion.Position(Velocity, elapsedTime);
}

(충돌 테스트와 같이 많은 코드를 제거 했으므로 충돌을 감지하기 위해 객체를 두 번 반복하지 않습니다).

그래서 나는 항상 전체 목록을 반복하는 것은 아닙니다. 첫 번째 객체에 대해서만하고, 객체가 다른 객체에 대해 느끼는 힘을 찾을 때마다 다른 객체가 같은 힘을 느낍니다. 그런 다음 나머지 업데이트를 위해 첫 번째 개체를 다시 고려할 필요가 없습니다.

Gravity.ForceUnderGravity(...)Motion.Velocity(...)등 기능은 XNA의 벡터 수학 내장의 비트를 사용합니다.

두 물체가 충돌하면 대량의 파편이 생깁니다. 별도의 목록에 보관되며 속도 계산의 일부로 거대한 물체는 파편을 반복하지 않지만 각 파편은 거대한 입자를 반복해야합니다.

이것은 놀라운 한계까지 확장 할 필요가 없습니다. 세계는 무제한이 아니며, 국경을 넘어서 교차하는 물체를 파괴합니다. 아마도 수천 가지 정도의 물체를 처리 할 수 ​​있기를 원합니다. 현재 게임은 약 200 대 초크를 시작합니다.

내가 어떻게 이것을 향상시킬 수 있을지에 대한 생각? 루프 길이를 수백에서 수백으로 줄이는 데 사용할 수있는 휴리스틱? 일부 코드는 모든 업데이트보다 덜 자주 실행할 수 있습니까? 알맞은 크기의 세계를 허용 할만큼 빠를 때까지 멀티 스레드를해야합니까? 속도 계산을 GPU로 오프로드해야합니까? 그렇다면 어떻게 설계해야합니까? 정적 공유 데이터를 GPU에 유지할 수 있습니까? GPU에서 HLSL 함수를 생성하여 임의로 (XNA를 사용하여) 호출하거나 드로우 프로세스의 일부 여야합니까?


1
한 가지 주목할 점은 "거대한 물체는 속도 계산의 일부로 파편을 반복하지 않지만 각 파편은 거대한 입자를 반복해야합니다." 나는 그것이 더 효율적이라고 가정한다는 인상을 받았습니다. 그러나 100 개의 잔해물을 각각 10 번 반복하는 것은 10 개의 거대한 물체를 100 번 반복하는 것과 동일합니다. 아마도 대규모 객체 루프에서 각 잔해물을 반복하여 두 번째로하지 않는 것이 좋습니다.
Richard Marskell-Drackir

시뮬레이션이 얼마나 정확해야합니까? 서로를 향해 가속하는 모든 것이 정말로 필요합니까? 그리고 정말로 중력 계산을 사용해야합니까? 아니면 당신이 이루고자하는 규약에서 벗어날 수 있습니까?
chaosTechnician

@Drackir 당신이 옳다고 생각합니다. 그것들이 분리되는 이유 중 하나는 질량이없는 물체에 대해 수학이 다르기 때문이며, 일부는 원래 중력에 전혀 순종하지 않았기 때문에 포함시키지 않는 것이 더 효율적이었습니다. 그래서 흔적입니다
카슨 마이어스

@chaosTechnician 그것은 매우 정확할 필요는 없습니다. 사실 가장 지배적 인 힘을 거의 고려하지 않는다면 시스템이 더 안정적 일 것입니다. 이상적입니다. 그러나 그것은 내가 겪고있는 효율적인 방법으로 가장 지배적 인 힘을 찾는 중입니다. 또한 중력 계산은 이미 근사치 G * m1 * m2 / r^2입니다. 단지 G가 동작을 조정하는 것입니다. (사용자가 시스템을 방해 할 수 있기 때문에 경로
Carson Myers

질량이없는 경우 각 잔해 조각이 거대한 입자를 반복해야하는 이유는 무엇입니까? 충돌?
sam hocevar

답변:


26

그리드 작업처럼 들립니다. 게임 공간을 그리드로 나누고 각 그리드 셀마다 현재 오브젝트 목록을 유지하십시오. 객체가 셀 경계를 가로 질러 이동할 때 객체의 목록을 업데이트합니다. 객체를 업데이트하고 다른 사람들과 상호 작용할 때 검색 할 때 현재 그리드 셀과 인접 셀 몇 개만 볼 수 있습니다. 최상의 성능을 위해 그리드의 크기를 조정할 수 있습니다 (그리드 셀이 너무 작을 때 더 높은 그리드 셀 업데이트 비용의 균형 조정-그리드 셀이 너무 클 때 검색 비용이 높아짐) 큰).

물론 이것은 두 그리드 셀보다 더 멀리 떨어진 물체가 전혀 상호 작용하지 않게 할 것입니다. 대량의 대량 축적 (큰 물체 또는 많은 작은 물체의 클러스터)이 문제가 될 수 있습니다. 언급했듯이 더 큰 영향을 미치는 영역이 있습니다.

할 수있는 한 가지 방법은 각 그리드 셀 내에서 총 질량을 추적하고 더 먼 상호 작용을 위해 전체 셀을 단일 객체로 취급하는 것입니다. 즉, 객체의 힘을 계산할 때 몇 개의 인접한 그리드 셀에서 객체에 대한 직접적인 객체 간 가속도를 계산 한 다음 멀리 떨어진 그리드 셀마다 셀 간 가속도를 추가하십시오 (또는 어쩌면 무시할 수없는 양의 질량을 가진 것일 수도 있습니다). 셀 간 가속으로, 나는 두 셀의 총 질량과 중심 사이의 거리를 사용하여 계산 된 벡터를 의미합니다. 그것은 격자 셀에있는 모든 물체에서 합산 ​​된 중력을 합리적으로 근사해야하지만 훨씬 저렴합니다.

게임 세계가 매우 큰 경우 쿼드 트리 (2D) 또는 옥트리 (3D)와 같은 계층 적 그리드를 사용하고 비슷한 원리를 적용 할 수도 있습니다. 더 긴 거리의 상호 작용은 더 높은 수준의 계층에 해당합니다.


1
그리드 아이디어의 경우 +1 그리드의 질량 중심을 추적하고 계산을 조금 더 순수하게 유지하는 것이 좋습니다 (필요한 경우).
chaosTechnician

나는이 아이디어를 많이 좋아한다. 나는 세포에 물체를 포함시키는 것을 고려했지만 기술적으로 다른 세포에있는 두 개의 인근 물체를 고려할 때 그것을 버렸습니다. 세포. 제대로하면 제대로 작동해야한다고 생각합니다.
카슨 마이어스

8
이 본질적으로 반스 - 헛 알고리즘 en.wikipedia.org/wiki/Barnes -Hut_simulation
러셀 Borogove

심지어 물리에서 중력이 작동하는 방식과 비슷하게 들립니다.
잔 Lynx

나는 아이디어가 마음에 들지만 정직하게 조금 더 다듬어야한다고 생각합니다. 두 물체가 서로 매우 근접하지만 별도의 셀에 있으면 어떻게 될까요? 세 번째 단락이 어떻게 도움이 될지 알 수 있지만 상호 작용하는 객체에 대해 원형 컬링을 수행하지 않는 이유는 무엇입니까?
Jonathan Dickinson

16

Barnes-Hut 알고리즘이이 방법을 사용하는 방법입니다. 정확한 문제를 해결하기 위해 슈퍼 컴퓨터 시뮬레이션에 사용되었습니다. 코딩하기가 어렵지 않고 매우 효율적입니다. 실제로이 문제를 해결하기 위해 얼마 전에 Java 애플릿을 작성했습니다.

http://mathandcode.com/programs/javagrav/를 방문 하여 "start"및 "show quadtree"를 누르십시오.

옵션 탭에서 파티클 수는 최대 200,000까지 올라갈 수 있습니다. 내 컴퓨터에서 계산은 약 2 초 후에 완료됩니다 (200,000 개의 점 그리기에는 약 1 초가 걸리지 만 계산은 별도의 스레드에서 실행 됨).

내 애플릿의 작동 방식은 다음과 같습니다.

  1. 임의의 질량, 위치 및 시작 속도로 임의의 입자 목록을 만듭니다.
  2. 이 파티클로 쿼드 트리를 만듭니다. 각 쿼드 트리 노드에는 각 하위 노드의 질량 중심이 포함됩니다. 기본적으로 각 노드에 대해 massx, massy 및 mass의 세 가지 값이 있습니다. 주어진 노드에 입자를 추가 할 때마다 particle.x * particle.mass 및 particle.y * particle.mass 씩 질량과 질량을 각각 증가시킵니다. 위치 (질량 / 질량, 질량 / 질량)는 노드의 질량 중심이됩니다.
  3. 각 입자에 대해 힘을 계산하십시오 ( 여기에 자세히 설명되어 있음 ). 이는 최상위 노드에서 시작하여 주어진 하위 노드가 충분히 작아 질 때까지 쿼드 트리의 각 하위 노드를 통해 반복됩니다. 되풀이를 멈 추면 파티클에서 노드의 질량 중심까지의 거리를 계산 한 다음 노드 질량과 입자 질량을 사용하여 힘을 계산할 수 있습니다.

당신의 게임은 서로를 끌어들이는 수천 개의 물건을 쉽게 다룰 수 있어야합니다. 각 객체가 "애덤"(내 애플릿의 베어 본 입자와 같은) 인 경우 8000 ~ 9000 개의 입자를 얻을 수 있습니다. 그리고 이것은 단일 스레딩을 가정합니다. 다중 스레드 또는 병렬 컴퓨팅 응용 프로그램을 사용하면 실시간으로 업데이트하는 것보다 더 많은 입자를 얻을 수 있습니다.

이것의 큰 렌더링에 대해서는 http://www.youtube.com/watch?v=XAlzniN6L94 를 참조하십시오.


첫 번째 링크는 죽었습니다. 애플릿이 다른 곳에서 호스팅됩니까?
Anko

1
결정된! 죄송합니다, 해당 도메인에 임대료를 지불 깜빡, 누군가는 그것을 자동 구입 : \ 또한, 3 분은 13 세 이후 8D에 꽤 좋은 응답 시간이

그리고 나는 추가해야합니다 : 소스 코드가 없습니다. 소스 코드를 찾고 있다면 part-nd (c로 작성)를 확인하십시오. 나는 거기에도 다른 사람들이 있다고 확신합니다.

3

네이선 리드는 훌륭한 답변을 가지고 있습니다. 그것의 짧은 버전은 시뮬레이션의 토폴로지에 맞는 넓은 위상 기술을 사용하고 서로에 눈에 띄게 영향을 줄 객체 쌍에 대해서만 중력 계산을 실행하는 것입니다. 정기적 인 충돌 탐지를 위해 수행하는 작업과 크게 다르지 않습니다.

그럼에도 불구하고 또 다른 가능성은 간헐적으로 객체를 업데이트하는 것입니다. 기본적으로 각 시간 단계 (프레임)는 모든 객체의 일부만 업데이트하고 다른 객체에 대해 속도 (또는 선호도에 따라 가속도)를 동일하게 유지합니다. 간격이 너무 길지 않은 한 사용자는 업데이트가 지연되는 것을 느끼지 못할 것입니다. 이렇게하면 알고리즘의 선형 속도가 향상되므로 Nathan이 제안한 것과 같은 광대역 기술을 확실히 살펴보십시오. 이는 많은 객체가있는 경우 훨씬 더 빠른 속도를 제공 할 수 있습니다. 아주 조금만 똑같이 모델링 된 것은 아니지만, 이것은 "중력파"를 갖는 것과 같습니다. :)

또한 한 번에 중력 필드를 생성 한 다음 두 번째 단계에서 객체를 업데이트 할 수도 있습니다. 첫 번째 단계는 기본적으로 각 객체의 중력 영향으로 그리드 (또는 더 복잡한 공간 데이터 구조)를 채우는 것입니다. 결과는 이제 주어진 위치에서 물체에 어떤 가속이 적용되는지를보기 위해 렌더링 할 수있는 중력장입니다. 그런 다음 객체를 반복하고 중력장의 효과를 해당 객체에 적용하면됩니다. 더 멋진 경우, 객체를 텍스처에 대한 원 / 구로 렌더링 한 다음 텍스처를 읽거나 GPU에서 다른 변환 피드백 패스를 사용하여 객체의 속도를 수정하여 GPU에서이 작업을 수행 할 수 있습니다.


프로세스를 패스로 분리하는 것은 훌륭한 아이디어입니다. 업데이트 간격은 (내가 아는 한) 초의 작은 부분이기 때문입니다. 중력장 텍스처는 굉장하지만 지금은 내 손이 닿지 않는 부분 일 것입니다.
카슨 마이어스

1
마지막 업데이트 이후에 건너 뛴 시간 조각 수에 적용된 힘을 곱하는 것을 잊지 마십시오.
잔 Lynx

@seanmiddlemitch : 중력장 질감에 대해 좀 더 자세히 설명해 주시겠습니까? 죄송합니다. 그래픽 프로그래머는 아니지만 정말 흥미로운 것 같습니다. 나는 그것이 어떻게 작동 해야하는지 이해하지 못합니다. 그리고 / 또는 당신은 기술의 설명에 대한 링크가 있습니까?
Felix Dombek

@FelixDombek : 대상을 영향 영역을 나타내는 원으로 렌더링합니다. 프래그먼트 셰이더는 객체의 중심을 가리키고 적절한 크기 (중심과 객체의 질량을 기준으로 한 거리)를 가진 벡터를 씁니다. 하드웨어 블렌딩은 이러한 벡터를 가산 모드로 합산 할 수 있습니다. 결과는 정확한 중력장은 아니지만 거의 게임의 요구에 충분할 것입니다. 다음 GPU를 사용하는 또 다른 방법으로,이 CUDA 기반 N 바디 중력 시뮬레이션 기법 참조 http.developer.nvidia.com/GPUGems3/gpugems3_ch31.html
숀 Middleditch

3

쿼드 트리를 사용하는 것이 좋습니다. 임의의 직사각형 영역에서 모든 객체를 빠르고 효율적으로 찾을 수 있습니다. 여기에 위키 기사가 있습니다 : http://en.wikipedia.org/wiki/Quadtree

그리고 SourceForge의 내 XNA 쿼드 트리 프로젝트에 대한 뻔뻔한 링크 : http://sourceforge.net/projects/quadtree/

또한 거리에 관계없이 모든 물체와 상호 작용할 수 있도록 모든 큰 물체의 목록을 유지합니다.


0

약간의 (아마도 순진한) 입력 만 가능합니다. 게임 프로그래밍은하지 않지만 기본 병목 현상은 중력-중력 계산입니다. 각 객체 X를 반복 한 다음 각 객체 Y에서 중력 효과를 찾아서 추가하는 대신 각 쌍 X, Y를 가져 와서 그 사이의 힘을 찾을 수 있습니다. 그것은 O (n ^ 2)에서 중력 계산의 수를 줄여야합니다. 그런 다음 많은 추가 작업을 수행하지만 (O (n ^ 2)) 일반적으로 비용이 덜 듭니다.

또한이 시점에서 "본체가 너무 작아서 중력이 \ epsilon보다 작 으면 힘을 0으로 설정"과 같은 규칙을 구현할 수 있습니다. 다른 목적으로도 (충돌 검출 포함)이 구조를 갖는 것이 유리할 수있다.


이것은 기본적으로 내가하고있는 일입니다. X와 관련된 모든 쌍을 얻은 후에는 X를 다시 반복하지 않습니다. X와 Y, X와 Z 등의 힘을 찾아서 그 힘을 쌍의 두 물체에 적용합니다. 루프가 완료된 후 ForceOfGravity벡터는 모든 힘의 합이며 속도와 새로운 위치로 변환됩니다. 나는 중력 계산이 특히 비싸다는 것을 확신하지 못하고 그것이 임계 값을 초과하는지 확인하는 것이 눈에 띄는 시간을 절약하지 못할 것이라고 생각하지 않습니다.
Carson Myers

0

seanmiddleditch의 답변을 연장하면서 중력장 아이디어에 약간의 빛을 비출 수도 있다고 생각했습니다.

첫째, 텍스처로 생각하지 말고 수정 가능한 개별 값 필드 (2 차원 배열)를 생각하십시오. 시뮬레이션의 후속 정확도는 해당 필드의 해상도 일 수 있습니다.

물체를 필드에 도입하면 모든 주변 값에 대해 중력 전위를 계산할 수 있습니다. 따라서 현장에서 중력 싱크 를 만듭니다 .

그러나 이전보다 비효율적이거나 비효율적이되기 전에 계산해야 할 점은 몇 개입니까? 아마도 32x32조차도 각 객체마다 반복되는 실질적인 필드는 많지 않습니다. 따라서 전체 프로세스를 여러 단계로 나눕니다. 각각 다양한 해상도 (또는 정확도)를 갖습니다.

즉, 제 1 패스는 공간의 2D 좌표를 나타내는 각각의 셀 값과 함께 4x4 그리드로 표현 된 물체 중력을 계산할 수있다. O (n * 4 * 4) 소계 복잡도 제공.

두 번째 패스는 64x64 해상도의 중력장으로 공간의 2D 좌표를 나타내는 각 셀 값으로 더 정확할 수 있습니다. 그러나 복잡성이 매우 높기 때문에 영향을받는 주변 셀의 반경을 제한 할 수 있습니다 (아마도 주변 5x5 셀만 업데이트 됨).

1024x1024의 해상도로 고정밀 계산에 세 번째 패스를 추가로 사용할 수 있습니다. 실제로 1024x1024의 개별 계산을 수행하고 있지만이 필드의 일부 (아마도 6x6 하위 섹션)에서만 작동한다는 것을 기억하십시오.

이런 식으로 업데이트의 전체적인 복잡성은 O (n * (4 * 4 + 5 * 5 + 6 * 6))입니다.

그런 다음 각 물체의 속도 변화를 계산하려면 각 중력 필드 (4x4, 64x64, 1024x1024)에 대해 점 질량 위치를 그리드 셀에 매핑하고 그리드 셀 전체 중력 전위 벡터를 새 벡터에 적용하십시오. "계층"또는 "통과"마다 반복; 그런 다음 함께 추가하십시오. 이것은 좋은 결과 중력 벡터를 제공해야합니다.

따라서 전체 복잡도는 O (n * (4 * 4 + 5 * 5 + 6 * 6) + n)입니다. 실제로 계산하는 것은 (중복도) 중력장의 전체 해상도가 아니라 패스의 중력 전위를 계산할 때 업데이트하는 주변 셀의 수입니다.

저해상도 필드 (첫 번째 패스)의 이유는 우주 전체를 분명히 포함하고 거리에도 불구하고 외계인이 더 밀집된 지역으로 유인되도록하기위한 것입니다. 그런 다음 더 높은 해상도의 필드를 별도의 레이어로 사용하여 주변 행성의 정확도를 높이십시오.

이것이 의미가 있기를 바랍니다.


0

다른 접근법은 어떻습니까?

질량을 기준으로 물체에 영향을주는 영역을 할당하십시오. 그 범위를 넘어 측정 할 수있는 영향을주기에는 너무 작습니다.

이제 세계를 격자로 나누고 각 개체를 영향을주는 모든 셀 목록에 넣습니다.

개체가있는 셀에 첨부 된 목록의 개체에 대해서만 중력 계산을 수행하십시오.

개체가 새 격자 셀로 이동할 때만 목록을 업데이트해야합니다.

격자 셀이 작을수록 업데이트 당 계산 횟수는 줄어들지 만 목록 업데이트는 더 많이 수행하게됩니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.