중력 계산 최적화


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.