많은 큐브 작업. 성능 향상?


12

편집 : 질문을 요약하면 성능이 좋지 않은 복셀 기반 세계 (Minecraft 스타일 (Thanks Communist Duck))가 있습니다. 나는 출처에 대해 긍정적이지 않지만 그것을 제거하는 방법에 대한 가능한 조언을 원합니다.

나는 세계가 많은 양의 큐브로 구성된 프로젝트를 진행하고 있습니다 (나는 당신에게 숫자를 줄 것이지만 사용자 정의 세계입니다). 내 테스트는 약 (48 x 32 x 48) 블록입니다.

기본적으로이 블록들은 자체적으로 아무것도하지 않습니다. 그들은 단지 거기에 앉아 있습니다.

플레이어 상호 작용과 관련하여 사용되기 시작합니다.

사용자 마우스가 어떤 큐브 (마우스 오버, 클릭 등)와 상호 작용하고 플레이어가 움직일 때 충돌을 감지하는지 확인해야합니다.

이제 처음에는 모든 블록을 반복하면서 엄청난 지연이 발생했습니다.

모든 블록을 반복하고 문자의 특정 범위 내에있는 블록을 찾은 다음 충돌 감지를 위해 해당 블록을 반복하는 등으로 지연을 줄였습니다.

그러나 나는 여전히 우울한 2fps에 가고 있습니다.

이 지연을 줄이는 방법에 대한 다른 아이디어가 있습니까?

Btw, XNA (C #)를 사용하고 있으며 3d입니다.


마인 크래프트 같은가요? 복셀?
공산주의 오리

4
옥트리를 살펴 보셨습니까? en.wikipedia.org/wiki/Octree
bummzack

1
게임 프로파일 링을 시도 했습니까? 시간이 가장 많이 소요되는 주요 영역이 표시 될 수 있습니다. 당신이 생각하는 것과 다를 수도 있습니다.
감속

2
각 큐브의 6 개의면을 모두 그리는 대신 아무 것도 접촉하지 않는 면만 그릴 수 있습니다.
David Ashmore

1
@David : 예, 또는 큐브 당 단일 드로우 콜을 먼저 중단 한 다음 나중에 개별 다각형에 대해 걱정할 수 있습니다.
Olhovsky

답변:


20

나무에 대해 배우고 싶은 것 같습니다!

그리고 나는 심각합니다. 현재 모든 큐브의 배열을 반복하고 있다면 실제로 다양한 공간 데이터 구조를 조사해야합니다. 이 경우 큐브 세계를 다시 상상하는 가장 좋은 방법은 나무입니다.

이유에 대한 이유를 살펴보기 전에 문제에 대해 생각해 봅시다. 가능한 적은 비용으로 플레이어가 충돌 할 수있는 근처 큐브 목록을 검색 할 수있는 솔루션을 찾고 있습니다. 이 목록은 가능한 작지만 정확해야합니다.

이제이 영역을 결정하려면 플레이어의 좌표 공간을 큐브 맵의 좌표 공간에 매핑해야합니다. 즉, 플레이어의 부동 소수점 위치를 다차원 큐브 배열의 이산 인덱스에 매핑해야합니다 (예 : 표기법은 world[31][31][31]64 * 64 * 64 다차원 배열의 정확한 중간).

우리는이 같은 이산 인덱싱을 사용하여 주변 블록을 간단히 계산할 수있을 것입니다. 아마도 근처 큐브 만 샘플링 할 것입니다. 그러나 여전히 일정한 재 계산이 필요하며 배치에서 불연속적인 개체는 허용하지 않습니다 (예 : 큐브에 매핑되지 않을 수 있음) 지도).

이상적인 상황은 큐브 맵의 특정 섹션에 대한 큐브 세트를 포함하는 버킷 세트이며 주변 영역을 다시 계산하는 대신 동일하게 나눠서 단순히 이러한 영역 으로 들어오고 나가는 것입니다 . 사소한 계산의 경우, 이와 같이 데이터를 보유하면 모든 큐브를 반복하고 근처에있는 개별 세트 만 제거 할 수 있습니다.

문제는 이것을 어떻게 구현 하는가입니다.

64 * 64 * 64 세계의 경우 8 * 8 * 8 영역으로 분류되었다고 상상해보십시오 . 이것은 여러분의 세계에서 축당 8 개의 존 (X, Y, Z)을 갖게됨을 의미합니다. 이 각 영역 에는 8 개의 큐브가 포함되어 있으며이 새로운 단순 인덱스로 쉽게 검색 할 수 있습니다.

월드의 모든 큐브를 반복하지 않고 근처의 큐브 세트에서 작업을 수행해야하는 경우 이러한 영역 을 반복하여 원래 64 * 64 * 64 (262144)에서 최대 반복 횟수를 세분화 할 수 있습니다. 단지 520 (8 * 8 * 8 + 8).

이제이 영역 세계에서 축소하고 영역 을 더 큰 수퍼 영역 에 배치하십시오 . 여기서 각 수퍼 존 은 2 * 2 * 2 정규 존을 포함 합니다. 현재 세계에 512 (8 * 8 * 8) 영역이 포함되어 있으므로 8 개의 영역수퍼 영역 당 2 개의 영역 으로 나누어 8 * 8 * 8 영역 을 64 (4 * 4 * 4) 수퍼 영역 으로 나눌 수 있습니다 . 위에서 동일한 논리를 적용하면 최대 반복을 512에서 8로 나누고 수퍼 존 을 찾을 수 있습니다 . 그리고 진행 영역 을 찾기 위해 최대 64(총 최대 72)! 이것이 이미 많은 반복을 어떻게 절약하고 있는지 알 수 있습니다 (262144 : 72).

이제 나무가 얼마나 유용한 지 알 수있을 것입니다. 각 영역 은 트리의 분기이며 각 수퍼 영역 은 선행 분기입니다. 당신은 단순히 당신이 필요로하는 것을 찾기 위해 나무를 순회하고 있습니다; 더 작은 데이터 세트를 사용하여 전체 비용을 최소화합니다.

아래 다이어그램은 개념을 시각화하는 데 도움이됩니다. ( Wikipedia : Octrees의 이미지 ) : 옥트리

기권:

위와 같이 이상적인 설정에서 복셀 월드가 이미 고정 크기의 다차원 배열로 배치되어 있으면 플레이어 위치를 쿼리 한 다음 주변 블록을 O (1) 비용으로 인덱싱 할 수 있습니다! (Ollovskys 설명 참조) 그러나 복셀 게임에서 세상의 크기가 거의 고정되어 있지 않다는 점을 고려하면 더욱 어려워집니다. HDD에서 메모리로 전체 수퍼 존 을로드 할 수 있도록 데이터 구조가 필요할 수 있습니다 . 고정 크기의 다차원 배열과 달리 트리는 결합 알고리즘에 너무 많은 시간을 소비하지 않고도이를 가능하게합니다.


나는 그것을 얻을 것이라고 말하지만 불행히도 나는 그렇지 않습니다. 블록의 충돌 상자는 움직이지 않고 플레이어의 충돌 상자 만 움직입니다. 그리고 지금 (모든 블록을 반복하지 않고) 플레이어의 5 블록 반경 내에있는 모든 블록을 반환하는 방법이 있습니다. 번거롭지 만 죄송합니다. 그건 그렇고, 세상을 더 단순하게 만들기 위해 세계가 64 x 64 x 64라고 가정 할 수 있습니까?
Joel

그리고 나는 여전히 약 5fps를 받고 있습니다 :(
Joel

나는 대답을 다시 한 번 말하고 도움이되는지 알려주십시오.
감속

이 octree 기법을 사용하여 플레이어가 할 수있는 블록을 좁 힙니다. 나는 그것을 확신합니다. 그러나 작은 블록 선택으로 범위를 좁 히면 충돌 감지를 사용하도록 제안합니까? 아니면 다른 방법이 있습니까?
Joel

2
플레이어가 큐브의 크기에 비해 매우 크지 않은 경우 플레이어 주위의 충돌을 확인하는 것이 가장 빠른 방법 일 것입니다. 예를 들어 플레이어가 하나의 큐브보다 많은 공간을 차지하지 않으면 충돌을 찾기 위해 최대 27 개의 주변 큐브 만 확인하면됩니다. 큐브를 인덱싱 할 수있는 배열에 큐브를 저장하고 가능한 모든 큐브 위치에 하나의 슬롯을 할당한다고 가정하면 큐브 위치에 직접 인덱스 할 수 있으므로 트리가 필요하지 않습니다.
Olhovsky

13

다니엘스의 답변에 동의합니다. 대량의 상자를 반복하는 것이 가장 큰 원인이며, 공간 분할을 사용하면 게임 속도를 크게 높일 수 있지만 문제 다른 곳에서도 발생할 수 있으며 시간을 낭비 할 수 있다는 점에 동의합니다 .

게임 속도를 크게 높이려면 코드를 프로파일 링해야합니다. 병목 현상이있는 위치를 식별하면 가장 크게 개선 할 수 있습니다.

코드를 프로파일 링하는 방법에는 여러 가지가 있으며, 자체 성능 분석 클래스 (MSDN (Stopwatch class)를 사용할 수 있음)를 롤업 하거나 PIX를 사용하여 CPU / GPU가 얼마나 바쁜지에 대한 일반적인 아이디어를 얻을 수 있습니다. .

PIX 이벤트 마커 를 코드에 넣을 수도 있습니다 . PIX 판독 값에서 색상 영역으로 표시됩니다. 이러한 함수에 대한 공식 C # 인터페이스는 없지만 이 스레드 는 C # 인터페이스를 직접 만드는 방법을 보여줍니다.


2
+1, 완전히 동의합니다. 알고리즘을 조사해서는 안되며 코드를 프로파일 링해야합니다. 내 생각에 그것은 당신이 블록을 반복하고 있다는 사실과 관련이 없으며 (별로 많지는 않습니다), 그것은 당신이 각 블록으로하고있는 일입니다. 궁극적으로 그렇습니다. 무차별 대입보다 더 나은 방법이 필요하지만 48x32x48 큐브에서 간단한 반복을 처리 할 수없는 경우 루핑 방식이 아니라 각 큐브로 수행중인 작업을 다시 생각해야합니다.
팀 홀트

4
@ 팀 : 그의 플레이어가 48x32x48의 공간을 차지할만큼 충분히 크지 않다면, 그 큐브 근처의 다른 곳을 반복해서는 안됩니다. 그가 프레임 당 73000 큐브를 반복하는 경우 프로파일 링을 수행하지 않고도 말할 수 있습니다. 그 이유는 수십 배 더 많은 반복을 피하는 방법을 배우는 것 외에 다른 이유가 없다면 그 문제를 해결하는 것이 좋습니다 필요합니다. 이것은 내가 마이크로 또는 조기 최적화라고 부르는 것이 아닙니다.
Olhovsky

내 플레이어는 1 큐브의 크기보다 작지만 어떤 단계에서는 더 클 수도 있지만 (많지는 않지만)
Joel

Randomman159 : 그런 다음 충돌을 찾기 위해 주변 27 개 큐브에 대해서만 테스트하면됩니다. 내 대답을 참조하십시오.
Olhovsky

6

플레이어가 큐브의 크기에 비해 큰 경우 다른 사람이 제안한 것처럼 옥트리 또는 다른 공간 분할 구조를 원할 것입니다.

그러나 플레이어가 큐브 크기에 비해 작은 경우 큐브와의 충돌을 감지하는 가장 빠른 방법은 플레이어 주변 영역을 간단한 선형 검색하는 것입니다.

플레이어가 1 큐브보다 작기 때문에 최대 27 개의 큐브에 대해서만 충돌을 테스트하면됩니다.

이는 모든 큐브에 대해 배열에 하나의 슬롯이있는 색인을 작성할 수있는 배열에 큐브를 저장한다고 가정합니다.

다른 사람들이 지적했듯이 실제로 속도를 늦추는 것을 보려면 코드를 프로파일 링해야합니다.

그래도 추측해야한다면, 모든 큐브에 대해 드로우 콜을하고있을 것입니다. 이를 해결하려면 지오메트리 인스턴스화를 살펴 봐야합니다.


또는 플레이어를 둘러싸는 '바운딩 박스'가있을 수 있습니다. 그런 다음 충돌하는 객체를 확인하여 플레이어가 충돌 할 객체를 결정하면됩니다. 좋은 물리 엔진은 모든 최적화를 수행 할 수 있습니다. 또한 단순히 '블록'이상의 충돌을 허용합니다.
감속

개인적으로 물리 엔진의 광범위한 단계에 의존하여 73000 큐브에 대해 테스트하고 싶지 않습니다. 충돌을 효율적으로 테스트하기 위해 24 줄 정도의 코드를 작성할 수 있습니다. 또한, 그는 현재 물리 엔진이 없을 것입니다.
Olhovsky

1

작업 속도를 높이기위한 또 다른 제안 : 블록은 거의 고정되어 있으므로 플레이어가 대부분의 블록과 충돌 할 수있는 방법이 없습니다. 블록에 노출 여부를 나타내는 부울을 추가하십시오. (이는 이웃을보고 재 계산할 수 있습니다.) 노출되지 않은 블록은 충돌을 확인할 필요가 없습니다.

마인 크래프트가 이것과 비슷한 일을하는 것은 명백하다. 한 번은로드되지 않은 덩어리에 도달하면 세상을 볼 수있게되었다. 노출 된 표면이되어 렌더링되었습니다.)


-1

복셀 엔진에 문제가있었습니다.

솔루션 : (octrees보다 훨씬 간단합니다) 모든 블록을 반복하는 대신 방정식을 사용하여 블록 배열에서 블록의 위치를 ​​결정하십시오.

BlockIndex = (x * WorldWidth * WorldHeight) + (z * WorldHeight) + y;

그런 다음 블록이 존재하는지 확인하려면 다음을 수행하십시오.

Blocks[BlockIndex].Type > -1;

또는 블록이 존재하는지 확인합니다.


3D 월드로 테스트하기 위해 마우스에 좌표가 2 개 밖에 없기 때문에 여기서 문제가 더 복잡합니다. 뷰가 위에서 아래로 내려 오면 마우스 위치에 대해 3 개의 오른쪽 인덱스 중 2 개를 찾은 다음 위에서부터 시작하여 카메라에 더 가까운 높이로 루프하고 블록의 첫 번째 발생을 찾을 수 있습니다.
Markus von Broady
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.