거리 기능을 최적화하는 방법?


23

합리적으로 간단한 RTS와 유사한 게임을 개발하는 동안 거리 계산으로 인해 성능에 영향을주는 것을 알았습니다.

항상, 유닛이 목표 범위 내에 있는지, 발사체가 목표에 도달했는지, 플레이어가 픽업, 일반 충돌 등을 돌 렸는지 여부를 확인하기위한 거리 점검이 있습니다. 두 점 사이의 거리가 많이 사용됩니다.

내 질문은 정확히 그것에 관한 것입니다. 일반적인 sqrt (x * x + y * y) 접근 방식 이외의 거리를 확인하기 위해 게임 개발자가 어떤 대안을 가지고 있는지 알고 싶습니다. 프레임 당 수천 번 수행하면 상당히 시간이 많이 걸립니다.

맨해튼 거리와 제곱 거리 비교 (sqrt 병목 현상을 건너 뛰는 것으로 알고 있음)를 알고 싶습니다. 다른 거있어?



예를 들어 건물과 같이 이동하지 않을 물체가있는 경우 거리 함수의 2D 테일러 시리즈를 가져 와서 제곱 항에서 자른 다음 결과 함수를 다음과 같이 저장하는 것이 좋습니다. 특정 건물에서 거리 기능. 그것은 약간의 지저분한 작업을 초기화로 옮기고 일을 조금 더 빠르게 할 수 있습니다.
Alexander Gruber

답변:


26

TL; DR; 당신의 문제는 거리 기능을 수행하는 것이 아닙니다. 문제는 거리 기능을 여러 번 수행하는 것입니다. 다시 말해 수학적 최적화보다는 알고리즘 최적화가 필요합니다.

[편집] 사람들이 싫어하기 때문에 답의 첫 번째 섹션을 삭제하고 있습니다. 질문 제목은 편집 전에 대체 거리 기능을 요청했습니다.

매번 제곱근을 계산하는 거리 함수를 사용하고 있습니다. 그러나 제곱근을 전혀 사용하지 않고 간단히 대체하고 제곱 거리를 계산할 수 있습니다. 이렇게하면 많은 소중한 사이클이 절약됩니다.

거리 ^ 2 = x * x + y * y;

이것은 실제로 일반적인 트릭입니다. 그러나 그에 따라 계산을 조정해야합니다. 실제 거리를 계산하기 전에 초기 점검으로 사용할 수도 있습니다. 예를 들어 교차 테스트를 위해 두 점 / 구 간의 실제 거리를 계산하는 대신 거리 제곱을 계산하고 반경 대신 반경 제곱을 비교할 수 있습니다.

@ Byte56이 질문을 읽지 않았고 제곱 거리 최적화를 알고 있다고 지적한 후에 편집하십시오.

글쎄, 불행히도 우리는 거의 독점적으로 Euclidean Space 만 다루는 컴퓨터 그래픽 에 있으며 거리는 정확하게 Sqrt of Vector dot itselfeuclidean 공간 과 같이 정의됩니다 .

거리 제곱은 성능 측면에서 얻을 수있는 가장 좋은 근사치입니다. 두 번 곱하기, 한 번 더하기 및 할당을 꺾는 것은 볼 수 없습니다.

그래서 거리 기능을 최적화 할 수 없다고 어떻게해야합니까?

당신의 문제는 거리 기능을 수행하는 것이 아닙니다. 문제는 거리 기능을 여러 번 수행하는 것입니다. 다시 말해 수학적 최적화보다는 알고리즘 최적화가 필요합니다.

요점은 장면의 각 객체와 각 프레임의 플레이어 교차를 확인하는 대신입니다. 공간 일관성을 쉽게 활용할 수 있으며 플레이어 근처에있는 물체 (적중하거나 교차 할 가능성이 가장 높은 개체 만 확인)

이러한 공간 정보를 공간 분할 데이터 구조 에 실제로 저장하면이 작업을 쉽게 수행 할 수 있습니다 . 간단한 게임의 경우 기본적으로 구현하기 쉽고 동적 장면에 잘 맞기 때문에 Grid를 제안합니다.

모든 셀 / 박스에는 그리드의 경계 상자가 포함하는 객체 목록이 포함되어 있습니다. 그리고 해당 셀에서 플레이어 위치를 쉽게 추적 할 수 있습니다. 거리 계산의 경우 장면의 모든 것이 아니라 동일하거나 인접한 셀 안에있는 객체로 플레이어 거리 만 확인합니다.

보다 복잡한 방법은 BSP 또는 Octrees를 사용하는 것입니다.


2
질문의 마지막 문장은 OP가 다른 대안을 찾고 있다고 말합니다 (제곱 거리 사용에 대해 알고 있습니다).
MichaelHouse

@ Byte56 네 맞습니다, 나는 그것을 읽지 못했습니다.
concept3d

어쨌든 대답 해 주셔서 감사합니다. 그 방법으로 우리에게 유클리드 거리를 제공하지는 않지만 비교가 매우 정확하다는 문장을 추가하겠습니까? 나는 그것이 검색 엔진에서 여기에 오는 누군가에게 무언가를 추가 할 것이라고 생각합니다.
그림 쇼

@ 그림 쇼 나는 원래 문제를 해결하기 위해 답변을 편집했습니다.
concept3d

지적 해 주셔서 감사합니다. 나는 대답을 편집했다.
concept3d

29

거리와 상관없이 선형으로 유지 distance^2하면서도 체형의 체비 쇼프 및 다이아몬드 같은 맨해튼 거리와 달리 모호하게 나타나는 물체가 필요한 경우 후자의 두 기술을 평균화하여 팔각형 거리 근사치를 얻을 수 있습니다.

dx = abs(x1 - x0)
dy = abs(y1 - y0)

dist = 0.5 * (dx + dy + max(dx, dy))

Wolfram Alpha 덕분에 함수의 시각화 (형상 플롯)는 다음과 같습니다 .

컨투어 플롯

다음은 유클리드 거리와 비교할 때 오류 함수 의 도표입니다 (라디안, 1 사분면에만 해당).

오류 도표

보시다시피 오차는 축의 0 %에서 로브의 약 + 12 %입니다. 계수를 약간 수정하면 +/- 4 %로 낮출 수 있습니다.

dist = 0.4 * (dx + dy) + 0.56 * max(dx, dy)

여기에 이미지 설명을 입력하십시오

최신 정보

위의 계수를 사용하면 최대 오차는 +/- 4 % 이내이지만 평균 오차는 여전히 + 1.3 %입니다. 제로 평균 오차에 최적화 된 다음을 사용할 수 있습니다.

dist = 0.394 * (dx + dy) + 0.554 * max(dx, dy)

-5 %에서 + 3 % 사이의 오류와 + 0.043 %의 평균 오류


이 알고리즘의 이름을 웹에서 검색하는 동안 비슷한 팔각형 근사가 발견되었습니다 .

dist = 1007/1024 * max(dx, dy) + 441/1024 * min(dx, dy)

이것은 본질적으로 동일합니다 (지수는 다르지만 -1.5 % ~ 7.5 % 오류가 발생하지만 +/- 4 %로 마사지 할 수 있습니다) max(dx, dy) + min(dx, dy) == dx + dy. 이 양식을 사용하여 minmax통화는 다음 과 같은 이유로 유리합니다.

if (dy > dx)
    swap(dx, dy)

dist = 1007/1024 * dx + 441/1024 * dy

이것이 내 버전보다 빠를까요? 누가 알겠는가 ... 컴파일러와 타겟 플랫폼에 맞게 최적화하는 방법에 달려 있습니다. 내 생각에 어떤 차이를 보는 것은 꽤 어려울 것입니다.


3
흥미 롭습니다. 전에 본 적이 없습니다! 이름이나 "체비 쇼프와 맨해튼의 평균"이 있습니까?
congusbongus

@congusbongus 아마도 이름이 있지만, 그것이 무엇인지 모르겠습니다. 그렇지 않다면, 언젠가는 Crist Distance (아 ... 아마 아닐 것입니다)라고 불릴 것입니다
bcrist

1
부동 소수점 곱셈은 그다지 효율적이지 않습니다. 그렇기 때문에 다른 근사가 1007/1024 (정수 곱셈과 비트 시프트로 구현 됨)를 사용하는 이유입니다.
MSalters

@MSalters 그렇습니다. 부동 소수점 연산은 종종 정수 연산보다 느리지 만 0.4와 0.56은 정수 연산을 사용하도록 쉽게 변환 될 수 있습니다. 또한, 현대 x86 하드웨어에서, 대부분의 부동 소수점 연산 (이외 FDIV, FSQRT및 기타 초월 함수) 비용 본질적으로 같은 자신의 정수 버전과 : 명령어 당 1 또는 2 회.
bcrist

1
이것은 Alpha max + Beta Min과 매우 유사합니다 : en.wikipedia.org/wiki/Alpha_max_plus_beta_min_algorithm
drake7707

21

때때로이 질문은 거리 계산 수행 비용이 아니라 계산 횟수 때문에 발생할 수 있습니다 .

A의 대형 와 게임 세계 많은 배우,이다 기어 오를 하나의 배우와 모든 다른 사람 사이의 거리를 계속 확인 할 수 있습니다. 더 많은 플레이어, NPC 및 발사체가 세계에 진입함에 따라 필요한 비교 횟수는 2 차적 으로 증가 할 것 입니다 O(N^2).

이러한 성장을 줄이는 한 가지 방법은 좋은 데이터 구조를 사용하여 원치 않는 액터를 계산에서 빠르게 버리는 것입니다.

우리는 효율적으로 반복 모든 배우에 방법을 찾고 있습니다 동안, 범위에 대부분 제외 됩니다 행위자의 범위를 벗어난 확실히를 .

액터가 월드 공간에 균등하게 퍼져 있다면 버킷 그리드 가 적합한 구조 여야합니다 (허용 된 답변에서 알 수 있듯이). 액터에 대한 참조를 굵은 격자로 유지하면 근처에있는 버킷 중 몇 개만 확인하면 범위 내에있을 수있는 모든 액터를 덮고 나머지는 무시합니다. 액터가 움직일 때, 그의 오래된 버킷에서 새로운 버킷으로 그를 옮겨야 할 수도 있습니다.

균등하게 퍼지지 않는 액터의 경우 쿼드 트리 가 2 차원 세계에 더 좋을 수도 있고 옥트리 가 3 차원 세계에 적합 할 수도 있습니다. 이들은 넓은 공간의 빈 공간과 많은 액터가 포함 된 작은 영역을 효율적으로 분할 할 수있는보다 일반적인 목적의 구조입니다. 들어 정적 배우가 이진 공간 분할 을 검색하지만, 실시간으로 갱신에 너무 비용이 많이 드는 매우 빠르고 (BSP). BSP는 평면을 사용하여 공간을 반으로 자르고 공간을 반으로 자르며 여러 치수에 적용 할 수 있습니다.

물론 액터가 파티션 사이를 이동할 때 이러한 구조를 유지하는 데에는 오버 헤드가 있습니다. 그러나 배우가 많지만 관심 범위가 작은 대도시에서는 비용이 모든 물체에 대한 순진한 비교로 발생하는 비용보다 훨씬 낮아야합니다.

더 많은 데이터를 수신 할 때 알고리즘 비용이 어떻게 증가하는지에 대한 고려는 확장 가능한 소프트웨어 설계에 중요합니다. 때로는 올바른 데이터 구조 를 선택하는 것만 으로 충분합니다. 비용은 일반적으로 Big O 표기법을 사용하여 설명 됩니다 .

(이것은 질문에 대한 직접적인 대답이 아니라는 것을 알고 있지만 일부 독자에게는 유용 할 수 있습니다. 시간을 낭비하면 사과드립니다!)


7
이것이 가장 좋은 대답입니다. 거리 기능에는 최적화 할 것이 없습니다. 하나만 덜 사용하면됩니다.
sam hocevar

3
허용되는 답변은 공간 분할에도 적용됩니다. 그렇지 않으면 답변이 실제로 최적입니다. 고맙습니다.
그림 쇼

내 시간은 당신의 대답을 읽는 데 아주 잘 보냈습니다. 고마워요, 조이
Patrick M

1
이것은 최고의 답변이며 거리 함수 성능의 청어가 아닌 실제 문제 에 중점을 둔 유일한 방법 입니다. 허용되는 답변은 공간 분할에도 적용 할 수 있지만, 제쳐두고 있습니다. 거리 계산에 중점을 둡니다. 거리 계산은 여기서 중요한 문제 가 아닙니다 . 거리 계산을 최적화하는 것은 규모에 맞지 않는 무차별 대항 솔루션입니다.
Maximus Minimus

비교 횟수가 지수가되는 이유를 설명해 주시겠습니까? 나는 그것이 2 차적 일 것이지만, 각 시간 프레임 동안 각 액터를 서로 비교합니다.
Petr Pudlák

4

체비 쇼프 거리는 어떻습니까? 점 p, q의 경우 다음과 같이 정의됩니다.

거리

따라서 점 (2, 4) 및 (8, 5)의 경우 체비 쇼프 거리는 6입니다. | 2-8 | > | 4-5 |.

또한 E는 유클리드 거리이고 C는 체비 쇼프 거리입니다. 그때:

거리 2

제곱근을 계산해야하기 때문에 상한은 많이 사용되지 않지만 하한은 도움이 될 수 있습니다. 체비 쇼프 거리가 범위를 벗어날 정도로 충분히 클 때마다 유클리드 거리도 길어야 그것을 계산해야합니다.

물론 체비 쇼프 거리가 범위 내에 있으면 유클리드 거리를 계산하여 시간을 낭비해야합니다. 순 승리인지 알 수있는 한 가지 방법!


1
맨해튼 거리를 상한으로 사용할 수도 있습니다.
congusbongus

1
충분합니다. 나는 거기에서 bcrist가 제안한 것처럼 "Chbyshev와 Manhattan의 평균"으로의 홉, 건너 뛰기 및 점프라고 생각합니다.
Tetrinity

2

매우 간단한 로컬 최적화는 단순히 단일 차원을 먼저 확인하는 것입니다.

그건 :

distance ( x1, y1 , x1, y2) > fabs (x2 - x1)

따라서 fabs (x2 - x1)첫 번째 필터로 확인 하면 상당한 이득을 얻을 수 있습니다. 세계 크기와 관련 범위에 따라 달라집니다.

또한이를 공간 파티셔닝 데이터 구조의 대안으로 사용할 수 있습니다.

모든 관련 개체가 x 좌표 순서로 목록에 정렬되어 있으면 근처의 개체가 목록의 근처에 있어야합니다. 개체가 이동할 때 목록이 완전히 유지되지 않아 목록이 잘못되어도 알려진 속도 범위가 주어지면 목록의 섹션을 검색하여 주변 개체를 검색 할 수 있습니다.


2

과거에는 최적화를 위해 노력했다 sqrt. 오늘날의 머신에는 더 이상 적용되지 않지만 다음은 매직 넘버 를 사용하는 Quake 소스 코드의 예입니다 0x5f3759df.

float Q_rsqrt( float number )
{
  long i;
  float x2, y;
  const float threehalfs = 1.5F;

  x2 = number * 0.5F;
  y  = number;
  i  = * ( long * ) &y;  // evil floating point bit level hacking
  i  = 0x5f3759df - ( i >> 1 ); // what the hell?
  y  = * ( float * ) &i;
  y  = y * ( threehalfs - ( x2 * y * y ) ); // 1st iteration
  // y  = y * ( threehalfs - ( x2 * y * y ) ); // 2nd iteration (optional)
  // ...
  return y;
}

자세한 설명은 여기에서 무슨 일이 일어나고 있는지의 위키피디아에서 찾을 수 있습니다.

요컨대, 합리적인 초기 추정치를 제공하는 데 사용되는 매직 넘버를 사용하여 Newton의 방법 (추정을 반복적으로 개선하는 수치 알고리즘)을 몇 번 반복 합니다.

Travis가 지적했듯이 이러한 종류의 최적화는 더 이상 현대 건축에서 유용하지 않습니다. 또한 알고리즘 재 설계가 더 나은 결과를 얻을 수있는 반면 병목 현상에 일정한 속도의 속도 만 제공 할 수 있습니다.


2
이것은 더 이상 가치있는 최적화가 아닙니다. 오늘날 구매할 수있는 거의 모든 소비자 급 PC 아키텍처에는 클럭 사이클 이하에서 제곱근을 수행하는 하드웨어 최적화 sqrt 명령어가 있습니다. 가능한 가장 빠른 sqrt가 정말로 필요한 경우 x86 simd floating point sqrt 명령어를 사용하십시오. en.wikipedia.org/wiki/… GPU의 셰이더와 같은 경우 sqrt를 호출하면 자동으로 이러한 명령어가 발생합니다. CPU에서는 많은 컴파일러가 SIMD sqrt를 통해 sqrt를 구현한다고 가정합니다.
TravisG

@TravisG 예, 언급 할 가치가 있으므로 답변을 업데이트했습니다. 이 답변은 재미 있고 역사적인 관심사를 위해서만 제공되었습니다!
joeytwiddle
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.