Minecraft-esque 복셀 세계를 어떻게 최적화 할 수 있습니까?


76

쿼드 코어와 고기 그래픽 카드가 있어도 Minecraft의 놀라운 대형 세계는 탐색 속도가 매우 느리다는 것을 알았습니다.

Minecraft의 속도 저하는 다음과 같습니다.

  • 공간 분할 및 메모리 관리가 기본 C ++에서 더 빠르기 때문에 Java.
  • 약한 세계 분할.

나는 두 가지 가정 모두에서 틀릴 수 있습니다. 그러나 이것은 큰 복셀 세계를 관리하는 가장 좋은 방법에 대해 생각하게했습니다. 이 블록은 세계의 어떤 부분에 존재할 수있는 진정한 3D 세계입니다, 그것은 큰 차원 배열 기본적으로 [x][y][z]세계 각 블록이 유형이, (즉 BlockType.Empty = 0, BlockType.Dirt = 1등)

이런 종류의 세계를 제대로 작동 시키려면 다음이 필요하다고 가정합니다.

  • 모든 큐브를 분리하기 위해 다양한 나무 ( oct / kd / bsp )를 사용하십시오. 삼각형 레벨이 아닌 큐브 레벨마다 파티션을 나눌 수 있기 때문에 oct / kd가 더 나은 옵션 인 것 같습니다.
  • 일부 알고리즘을 사용하면 현재 볼 수있는 블록을 파악할 수 있습니다. 사용자에게 더 가까운 블록은 블록을 난독 처리하여 렌더링 할 수 없게되므로
  • 블록 오브젝트 자체를 가볍게 유지하여 나무에서 빠르게 추가하고 제거하십시오.

나는 더이없는 것 같아요 바로 이에 대한 대답은,하지만 난 주제에 대한 사람들의 의견을보고 관심이있을 것이다. 대규모 복셀 기반 세계에서 어떻게 성능을 향상 시키겠습니까?



2
그래서 실제로 무엇을 묻고 있습니까? 당신은 큰 세계를 관리하기위한 좋은 접근법, 또는 당신의 특정 접근법에 대한 피드백, 또는 큰 세계를 관리하는 주제에 대한 의견을 요구하고 있습니까?
doppelgreener

1
지금까지 좋은 점은 이러한 종류의 것들에 대한 가장 일반적인 접근 방식 에 관한 것입니다. 내가 제안한 모든 것은 논리적으로 일어날 것으로 예상되는 것이므로 접근 방식에 대한 피드백을받은 후에는 구체적이지 않습니다. 나는 그 주제에 대해 더 많은 정보를 원했고 몇 번의 검색 후에 많은 정보가 나오지 않았습니다. 내 질문은 렌더링 성능에 관한 것이 아니라 영역을 청킹하는 것과 같은 대량의 데이터를 관리하는 방법에 관한 것입니다.
SomeXnaChump

2
명확하게 작성하고 게시물에 질문을 추가하여 어떤 질문에 답하고 있는지 알려주세요. ;)
doppelgreener

3
"탐색이 매우 느리다"는 것은 무엇을 의미합니까? 게임이 새로운 지형을 생성 할 때 속도가 약간 느려지지만, 그 후에는 마인 크래프트가 지형을 꽤 잘 처리하는 경향이 있습니다.
thedaian

답변:


106

복셀 엔진 록스

복셀 엔진 잔디

Java 대 C ++과 관련하여 두 가지 모두에서 voxel 엔진을 작성했습니다 (위의 C ++ 버전). 또한 2004 년부터 보셀 엔진을 작성해 왔습니다 (보그가 아니었을 때). :) C ++ 성능이 훨씬 뛰어나다는 것을 망설이지 않고 말할 수는 있지만 코딩하기가 더 어렵습니다. 계산 속도에 관한 것이 아니라 메모리 관리에 관한 것입니다. 복셀 세계의 데이터만큼 많은 데이터를 할당 / 할당 해제 할 때 C (++)가 이길 언어입니다. 하나목표에 대해 생각해야합니다. 성능이 최우선 순위라면 C ++를 사용하십시오. 최신 성능을 발휘하지 않고 게임을 작성하려는 경우 Minecraft에서 입증 한 바와 같이 Java를 사용할 수 있습니다. 사소한 / 가장자리가 많지만 일반적으로 Java가 (잘 작성된) C ++보다 약 1.75-2.0 배 느리게 실행될 것으로 예상 할 수 있습니다. 제대로 작동하지 않는 오래된 버전의 엔진이 여기 에서 작동하는 것을 볼 수 있습니다 (EDIT : newer version here ). 청크 생성은 느리게 보일 수 있지만 3D 보로 노이 다이어그램을 체적으로 생성하여 무차별 강제 방법으로 CPU의 표면 법선, 조명, AO 및 그림자를 계산한다는 점을 명심하십시오. 나는 다양한 기술을 시험해 보았고 다양한 캐싱 및 인스 턴싱 기술을 사용하여 약 100 배 빠른 청크 생성을 얻을 수 있습니다.

나머지 질문에 대답하기 위해 성능을 향상시키기 위해 할 수있는 일이 많이 있습니다.

  1. 캐싱. 가능하면 데이터를 한 번 계산해야합니다. 예를 들어 조명을 장면에 굽습니다. 동적 조명 (후 처리로 화면 공간에서)을 사용할 수 있지만 조명에서 베이킹하면 삼각형의 법선을 통과하지 않아도됩니다.
  2. 비디오 카드에 가능한 적은 데이터를 전달하십시오. 사람들이 잊어 버리는 경향은 GPU에 더 많은 데이터를 전달할수록 시간이 더 걸린다는 것입니다. 단색과 정점 위치를 전달합니다. 주야간주기를 원한다면 컬러 그레이딩을하거나 태양이 서서히 변할 때 장면을 다시 계산할 수 있습니다.

  3. GPU로 데이터를 전달하는 것은 비용이 많이 들기 때문에 어떤면에서는 더 빠른 소프트웨어로 엔진을 작성할 수 있습니다. 소프트웨어의 장점은 GPU에서는 불가능한 모든 종류의 데이터 조작 / 메모리 액세스를 수행 할 수 있다는 것입니다.

  4. 배치 크기로 재생하십시오. GPU를 사용하는 경우 전달하는 각 정점 배열의 크기에 따라 성능이 크게 달라질 수 있습니다. 따라서 청크 크기로 놀아보십시오 (청크를 사용하는 경우). 64x64x64 청크가 꽤 잘 작동한다는 것을 알았습니다. 어쨌든 청크를 입방체로 유지하십시오 (직사각 프리즘 없음). 이를 통해 코딩 및 변환과 같은 다양한 작업이 쉬워지고 경우에 따라 성능이 향상됩니다. 모든 차원의 길이에 대해 하나의 값만 저장하는 경우 계산 중에 스왑되는 레지스터가 2 개 줄어 듭니다.

  5. 표시 목록 (OpenGL 용)을 고려하십시오. 비록 그들이 "오래된"방식이지만 더 빠를 수 있습니다. 표시 목록을 변수로 구워야합니다 ... 표시 목록 작성 작업을 실시간으로 호출하면 불필요하게 느려집니다. 표시 목록이 얼마나 빠릅니까? 정점 별 속성 대 상태 만 업데이트합니다. 즉, 최대 6 개의면을 통과 한 다음 1 개의 색상 (복셀의 각 정점에 대한 색상)을 전달할 수 있습니다. GL_QUADS와 3 차 복셀을 사용하는 경우 복셀 당 최대 20 바이트 (160 비트)를 절약 할 수 있습니다! (알파가없는 15 바이트, 일반적으로 사물을 4 바이트로 정렬하려는 경우)

  6. "청크"또는 데이터 페이지를 렌더링하는 무차별 방식을 사용합니다. 이는 일반적인 기술입니다. octrees와는 달리, 데이터를 읽고 / 처리하는 것이 훨씬 쉽고 빠르지 만, 메모리에 덜 친숙합니다 (그러나 요즘에는 $ 200- $ 300에 64 기가 바이트의 메모리를 얻을 수 있습니다). 분명히, 전세계에 하나의 거대한 배열을 할당 할 수는 없습니다 (복셀 당 32 비트 int가 사용되는 경우 1024x1024x1024 복셀 세트는 4 기가 바이트의 메모리입니다). 따라서 뷰어와의 근접성에 따라 많은 작은 배열을 할당 / 할당 해제합니다. 데이터를 할당하고 필요한 표시 목록을 얻은 다음 데이터를 덤프하여 메모리를 절약 할 수도 있습니다. 이상적인 조합은 octrees와 arrays의 하이브리드 접근법을 사용하는 것입니다. 세계의 절차 생성, 조명 등을 수행 할 때 데이터를 배열에 저장합니다.

  7. 먼 곳까지 렌더링 ... 클리핑 된 픽셀은 시간이 절약됩니다. gpu는 깊이 버퍼 테스트를 통과하지 않으면 픽셀을 던집니다.

  8. 뷰포트에서 청크 / 페이지 만 렌더링합니다 (자체 설명). GPU가 뷰포트 외부에서 폴 지온을 클립하는 방법을 알고 있더라도이 데이터를 전달하는 데 여전히 시간이 걸립니다. 나는 이것을위한 가장 효율적인 구조가 무엇인지 모르지만 ( "부끄럽게도, BSP 트리를 작성한 적이 없다") 청크 단위로 간단한 레이 캐스트조차도 성능을 향상시킬 수 있으며 분명히 관찰 절두체에 대한 테스트는 시간을 절약.

  9. 명백한 정보이지만 초보자에게는 : 표면에없는 모든 단일 다각형을 제거합니다. 즉, 복셀이 6 개의면으로 구성된 경우 절대 렌더링되지 않는면을 제거합니다 (다른 복셀을 만지고 있음).

  10. 프로그래밍에서 수행하는 모든 작업의 ​​일반적인 규칙 : CACHE LOCALITY! 캐시를 로컬에 유지할 수 있다면 (소량의 시간에도 큰 차이가 생길 수 있습니다.) 이는 동일한 메모리 영역에서 데이터를 합리적으로 유지하고 메모리 영역을 너무 자주 처리하도록 전환하지 않음을 의미합니다. 이상적으로는 스레드 당 하나의 청크에 대해 작업하고 해당 메모리를 스레드 전용으로 유지하십시오. 이는 CPU 캐시에만 적용되지 않습니다. 캐시 계층 구조는 다음과 같습니다 (가장 느리거나 빠른) : 네트워크 (클라우드 / 데이터베이스 / 등) -> 하드 드라이브 (아직 SSD가없는 경우 SSD를 얻음), 램 (삼중 채널 또는 RAM이없는 경우 더 큰 RAM을 얻음), CPU 캐시 (들), 레지스터. 후자이며, 필요 이상으로 바꾸지 마십시오.

  11. 스레딩. 해. Voxel 월드는 스레딩에 매우 적합합니다. 각 부분은 (대부분) 다른 부품과 독립적으로 계산할 수 있습니다. 스레딩 루틴.

  12. 문자 / 바이트 데이터 형식을 사용하지 마십시오. 또는 반바지. 일반 소비자에게는 최신 AMD 또는 Intel 프로세서가있을 것입니다 (아마도). 이 프로세서에는 8 비트 레지스터가 없습니다. 바이트를 32 비트 슬롯에 넣은 다음 메모리에서 다시 변환하여 바이트를 계산합니다. 컴파일러는 모든 종류의 부두를 수행 할 수 있지만 32 또는 64 비트 숫자를 사용하면 가장 예측 가능하고 빠른 결과를 얻을 수 있습니다. 마찬가지로 "bool"값은 1 비트를 사용하지 않습니다. 컴파일러는 종종 bool에 전체 32 비트를 사용합니다. 데이터에 대해 특정 유형의 압축을 시도하고 싶을 수 있습니다. 예를 들어, 8 개의 복셀이 모두 같은 유형 / 색상 인 경우 단일 숫자 (2 ^ 8 = 256 조합)로 저장할 수 있습니다. 그러나 이것의 결과에 대해 생각해야합니다. 메모리를 많이 절약 할 수 있습니다. 작은 압축 시간으로도 성능이 저하 될 수 있습니다. 그로 인해 소량의 추가 시간도 세계의 크기와 입방체로 확장되기 때문입니다. 레이 캐스트 계산을 상상해보십시오. 레이 캐스트의 모든 단계에 대해 압축 해제 알고리즘을 실행해야합니다 (한 번에 8 개의 복셀 계산을 일반화하는 현명한 방법이 아니라면).

  13. Jose Chavez가 언급했듯이 플라이급 디자인 패턴이 유용 할 수 있습니다. 비트 맵을 사용하여 2D 게임에서 타일을 나타내는 것처럼 여러 3D 타일 (또는 블록) 유형으로 세계를 만들 수 있습니다. 이것의 단점은 텍스처의 반복이지만, 함께 맞는 분산 텍스처를 사용하여이를 개선 할 수 있습니다. 경험상 어디에서나 인스 턴싱을 활용하려고합니다.

  14. 지오메트리를 출력 할 때 쉐이더에서 정점 및 픽셀 처리를 피하십시오. 복셀 엔진에는 필연적으로 많은 삼각형이 있으므로 간단한 픽셀 쉐이더조차도 렌더링 시간을 크게 줄일 수 있습니다. 버퍼로 렌더링하는 것이 좋습니다. 그런 다음 픽셀 셰이더를 포스트 프로세스로 사용하십시오. 그렇게 할 수 없다면 정점 셰이더에서 계산을 시도하십시오. 다른 계산은 가능한 정점 데이터로 구워 져야합니다. 모든 지오메트리 (예 : 그림자 매핑 또는 환경 매핑)를 다시 렌더링해야하는 경우 추가 패스가 매우 비쌉니다. 때로는 더 풍부한 디테일을 위해 역동적 인 장면을 포기하는 것이 좋습니다. 게임에 수정 가능한 장면 (예 : 파괴 가능한 지형)이있는 경우 사물이 파괴 될 때 항상 장면을 다시 계산할 수 있습니다. 재 컴파일은 비싸지 않으며 1 초도 걸리지 않습니다.

  15. 루프를 풀고 배열을 평평하게 유지하십시오! 이 작업을 수행하지 마십시오 :

    for (i = 0; i < chunkLength; i++) {
     for (j = 0; j < chunkLength; j++) {
      for (k = 0; k < chunkLength; k++) {
       MyData[i][j][k] = newVal;
      }
     }
    }
    //Instead, do this:
    for (i = 0; i < chunkLengthCubed; i++) {
     //figure out x, y, z index of chunk using modulus and div operators on i
     //myData should have chunkLengthCubed number of indices, obviously
     myData[i] = newVal;
    }

    편집 : 더 광범위한 테스트를 통해 이것이 잘못 될 수 있음을 발견했습니다. 시나리오에 가장 적합한 사례를 사용하십시오. 일반적으로 배열은 평평해야하지만 경우에 따라 다중 색인 루프를 사용하는 것이 더 빠를 수 있습니다

편집 2 : 다중 인덱스 루프를 사용할 때 다른 방법이 아닌 z, y, x 순서를 반복하는 것이 가장 좋습니다. 컴파일러가 이것을 최적화 할 수는 있지만 그렇게하면 놀랄 것입니다. 이를 통해 메모리 액세스 및 로컬 효율성이 극대화됩니다.

for (k < 0; k < volumePitch; k++) {
    for (j = 0; j < volumePitch; j++) {
        for (i = 0; i < volumePitch; i++) {
            myIndex = k*volumePitch*volumePitch + j*volumePitch + i;
        }
    }
}
  1. 때로는 가정, 일반화 및 희생을해야합니다. 가장 좋은 방법은 대부분의 세계가 완전히 정적 인 것으로 가정하고 수천 프레임마다 만 변경하는 것입니다. 세계의 애니메이션 부분의 경우 별도의 패스로 수행 할 수 있습니다. 또한 대부분의 세계가 완전히 불투명하다고 가정하십시오. 투명한 객체는 별도의 패스로 렌더링 할 수 있습니다. 텍스처는 x 단위로만 변하거나 개체를 x 단위로만 배치 할 수 있다고 가정합니다. 무한한 세상의 유혹처럼 고정 된 세계 크기를 가정하면 예측할 수없는 시스템 요구 사항으로 이어질 수 있습니다. 예를 들어, 위의 바위에서 보로 노이 패턴 생성을 단순화하기 위해 모든 보로 노이 중심점이 약간의 오프셋 (즉, 기하학적 해시를 암시 함)으로 균일 한 격자에 놓여 있다고 가정했습니다. 랩핑되지 않은 (가장자리가있는) 월드를 가정하십시오. 이는 사용자 경험에 최소 비용으로 래핑 좌표계에 의해 도입 된 많은 복잡성을 단순화 할 수 있습니다.

내 사이트 에서 구현에 대한 자세한 내용을 읽을 수 있습니다


9
+1. 에세이를 읽는 인센티브로 상단에 사진을 포함시키는 멋진 터치. 이제 나는 그들이 필요하지 않다고 말할 가치가있는 에세이를 읽었으므로 그만한 가치가있었습니다. ;)
George Duckett

고마워요-그림은 천 단어의 가치가 있습니다. :) 내 텍스트 벽을 덜 협박하게 만드는 것 외에도 독자에게 설명 된 기술을 사용하여 합리적인 속도로 렌더링 할 수있는 복셀 수에 대한 아이디어를 제공하고 싶었습니다.
Gavan Woolery

14
나는 여전히 SE가 특정 답변을 좋아할 수 있기를 바랍니다.
joltmode 2016 년

2
@PatrickMoriarty # 15는 일반적인 트릭입니다. 컴파일러 가이 최적화를 수행하지 않는다고 가정하면 (루프를 풀 수 있지만 다차원 배열을 압축하지는 않습니다). 캐싱을 위해 모든 데이터를 동일한 연속 메모리 공간에 유지하려고합니다. 다차원 배열은 포인터의 배열이므로 많은 공간에 (잠재적으로) 할당 될 수 있습니다. 루프를 풀 때 컴파일 된 코드가 어떻게 생겼는지 생각해보십시오. 레지스터 및 캐시 스왑을 최소화하기 위해 가장 적은 var / 명령어를 생성하려고합니다. 어느 것을 더 많이 컴파일한다고 생각하십니까?
Gavan Woolery

2
GPU의 캐싱, 스레딩 및 최소화와 관련하여 여기에 언급 된 사항 중 일부는 좋지만 일부는 매우 부정확합니다. 5 : 표시 목록 대신 항상 VBO / VAO를 사용하십시오. 6 : RAM이 많을수록 더 많은 대역폭이 필요합니다. 리드 12로 : 현대 메모리에서는 EXACT 반대가 적용되며, 바이트가 저장 될 때마다 더 많은 데이터를 캐시에 넣을 가능성이 높아집니다. 14 : Minecraft에는 픽셀보다 정점이 더 많으므로 (먼 거리의 모든 큐브) 계산을 FROM이 아닌 픽셀 셰이더로 옮기십시오.

7

Minecraft가 더 효율적으로 수행 할 수있는 작업이 많이 있습니다. 예를 들어 Minecraft는 약 16x16 타일의 전체 세로 기둥을로드하고 렌더링합니다. 많은 타일을 불필요하게 보내고 렌더링하는 것이 매우 비효율적이라고 생각합니다. 그러나 나는 언어 선택이 중요하다고 생각하지 않습니다.

Java는 매우 빠를 수 있지만이 데이터 지향적 인 무언가에 대해 C ++은 배열에 액세스하고 바이트 내에서 작업하는 데 드는 오버 헤드가 현저히 줄어 큰 이점이 있습니다. 반면 Java의 모든 플랫폼에서 스레딩을 수행하는 것이 훨씬 쉽습니다. OpenMP 또는 OpenCL을 사용할 계획이 아니라면 C ++에서는 그 편리함을 찾을 수 없습니다.

이상적인 시스템은 약간 더 복잡한 계층 구조입니다.

타일 은 단일 유형으로, 재료 유형 및 조명과 같은 정보를 유지하기 위해 약 4 바이트입니다.

세그먼트 는 32x32x32 타일 블록입니다.

  1. 전체면이 솔리드 블록 인 경우 6 개면 각각에 대해 플래그가 설정됩니다. 그러면 렌더러가 해당 세그먼트 뒤의 세그먼트를 막을 수 있습니다. Minecraft는 현재 폐색 테스트를 수행하지 않는 것 같습니다. 그러나 저급 카드에 대량의 다각형을 렌더링하는 것보다 비용이 많이 들지만 더 나은 하드웨어 폐색 컬링을 사용할 수 있다는 언급이있었습니다 .
  2. 세그먼트는 액티비티 (플레이어, NPC, 물 물리, 나무 성장 등) 중 메모리에만로드됩니다. 그렇지 않으면 디스크에서 클라이언트로 직접 압축되어 전송됩니다.

섹터 는 16x16x8 세그먼트 블록입니다.

  1. 섹터는 각 수직 열에 대해 가장 높은 세그먼트를 추적하므로 해당 세그먼트보다 높은 세그먼트는 빠르게 비워 질 수 있습니다.
  2. 또한 아래쪽 폐색 된 세그먼트를 추적하므로 표면에서 렌더링해야하는 모든 세그먼트를 빠르게 잡을 수 있습니다.
  3. 섹터는 다음에 각 세그먼트를 업데이트해야 할 때 (물리, 나무 성장 등) 추적합니다. 이러한 방식으로 각 부문에 로딩하는 것만으로도 세상을 활기차게 유지할 수 있고 작업을 완료 할 수있을 정도로 긴 세그먼트에만 로딩 할 수 있습니다.
  4. 모든 엔티티 위치는 섹터를 기준으로 추적됩니다. 이렇게하면지도 센터에서 매우 멀리 여행 할 때 Minecraft에있는 부동 소수점 오류가 방지됩니다.

세계 는 무한한 분야의지도가 될 것입니다.

  1. 세계는 섹터와 다음 업데이트를 관리 할 책임이 있습니다.
  2. 세계는 선구 적으로 플레이어에게 잠재적 인 경로를 따라 세그먼트를 보냅니다. Minecraft는 클라이언트가 요청한 세그먼트를 반응 적으로 보내 지연을 유발합니다.

나는 일반적으로이 아이디어를 좋아하지만 내부적으로 어떻게 세계 의 섹터를 매핑 하겠습니까?
Clashsoft

배열은 세그먼트의 타일과 세그먼트의 세그먼트에 가장 적합한 솔루션이지만 세계의 섹터는 무한한 맵 크기를 허용하기 위해 다른 것이 필요합니다. 내 제안은 해시에 XY 좌표를 사용하여 해시 테이블 (의사 사전 <Vector2i, Sector>)을 사용하는 것입니다. 그러면 월드는 단순히 주어진 좌표에서 섹터를 찾을 수 있습니다.
Josh Brown

6

내 2 코어에서도 Minecraft가 매우 빠릅니다. 약간의 서버 지연이 있지만 Java는 제한 요소가 아닌 것 같습니다. 로컬 게임이 더 나은 것처럼 보이므로 비효율적 인 부분을 가정하겠습니다.

귀하의 질문에 관해서는, Notch (Minecraft 저자)는 기술에 대해 얼마 동안 블로그를 작성했습니다. 특히, 세계는 "청크 (chunks)"에 저장됩니다 (때때로 세계가 아직 채워지지 않아 누락 된 경우). 따라서 첫 번째 최적화는 청크를 볼 수 있는지 여부를 결정하는 것입니다. .

당신이 짐작했듯이 청크 내에서 앱은 다른 블록에 의해 가려 지는지 여부에 따라 블록을 볼 수 있는지 여부를 결정해야합니다.

또한 블록 FACES가 있으며, 보이지 않는 것으로 간주 될 수 있습니다 (예 : 다른 블록이 얼굴을 덮고 있음) 또는 카메라가 가리키는 방향 (카메라가 북쪽을 향하는 경우)으로 볼 수 있습니다. 모든 블록의 북쪽 얼굴을 보지 마십시오!)

일반적인 기술에는 별도의 블록 객체를 유지하는 것이 아니라 각 블록마다 하나의 프로토 타입 블록과 함께이 블록이 어떻게 사용자 정의 될 수 있는지를 설명하기위한 최소의 데이터 세트와 함께 블록 유형의 "청크"가 포함됩니다. 예를 들어, 맞춤형 화강암 블록은 없지만 (물론) 물에는 각 측면을 따라 얼마나 깊이 있는지를 알려주는 데이터가있어 흐름 방향을 계산할 수 있습니다.

렌더링 속도, 데이터 크기 또는 무엇을 최적화 하려는지 확실하지 않습니다. 도움이 될 것입니다.


4
"덩어리"는 일반적으로 청크로 불립니다.
Marco

잘 잡다 (+1); 답변이 업데이트되었습니다. (기억을 위해 원래 일을했고 올바른 단어를 잊어 버렸습니다.)
Olie

언급 한 비 효율성은 "네트워크"라고도하며, 동일한 엔드 포인트가 통신하더라도 동일한 방식으로 두 번 작동하지 않습니다.
Edwin Buck

4

다음은 일반적인 정보와 조언에 대한 몇 마디입니다. 음, 경험이 많은 Minecraft modder로 제공 할 수 있습니다 (적어도 부분적으로 지침을 제공 할 수 있음).

Minecraft가 느린 이유는 의심스럽고 낮은 수준의 디자인 결정과 관련이 많습니다. 예를 들어, 블록을 위치 지정으로 참조 할 때마다 게임은 약 7 if 문을 사용하여 좌표가 유효하지 않은지 확인합니다 . 또한 캐시 룩업과 erm, 바보 같은 유효성 검사 문제를 우회하기 위해 '청크'(게임이 작동하는 16x16x256 블록 단위)를 가져온 다음 블록을 직접 참조 할 수있는 방법이 없습니다 (즉, 각 블록 참조에는 내 모드에서는 블록 배열을 직접 잡고 변경하는 방법을 만들었습니다. 이로 인해 대규모 던전 생성이 재생 불가능한 게으른 속도에서 눈에 띄게 빠른 속도로 향상되었습니다.

편집 : 다른 범위에서 변수를 선언하면 성능이 향상되었다는 주장이 제거되었지만 실제로는 그렇지 않습니다. 나는이 결과를 내가 실험하고있는 다른 것과 혼동 시켰을 때 (특히, 이중으로 통합하여 폭발 관련 코드에서 이중 및 부유 사이의 캐스트를 제거하는 것은 ... 상당히 이것은 큰 영향을 미쳤다!)

또한, 내가 많은 시간을 소비하는 영역은 아니지만 Minecraft의 대부분의 성능 초크는 렌더링 문제입니다 (게임 시간의 약 75 %가 내 시스템에서 사용됩니다). 걱정이 멀티 플레이어에서 더 많은 플레이어를 지원하는 경우 (서버는 아무것도 렌더링하지 않음) 크게 신경 쓰지 않지만 모든 컴퓨터에서 재생할 수있는 정도에 중요합니다.

따라서 어떤 언어를 선택하든 구현 / 낮은 수준의 세부 사항에 매우 친밀한 태도를 취하십시오. 이와 같은 프로젝트의 작은 세부 사항조차도 모든 차이를 만들 수 있기 때문입니다. 포인터? ""가능합니다! 코드가 적고 인라이닝의 이점이 있기 때문에 작업중인 프로젝트 중 하나에 엄청난 차이가 생겼습니다.)

높은 수준의 디자인을 어렵게 만들기 때문에 그 대답을 싫어하지만 성능이 문제가되는 것은 고통스러운 진실입니다. 도움이 되었기를 바랍니다.

또한 Gavin의 대답은 반복하고 싶지 않은 세부 사항 (그리고 훨씬 더! 그는 나보다 주제에 대해 더 잘 알고 있음)을 다루며 대부분 그에 동의합니다. 프로세서와 더 짧은 가변 크기에 대한 그의 의견을 실험해야 할 것입니다. 들어 본 적이 없습니다. 그것이 사실이라는 것을 스스로에게 증명하고 싶습니다!


2

우선 데이터를 어떻게로드 할 것인지 생각해야합니다. 필요한 경우 맵 데이터를 메모리로 스트리밍하는 경우 렌더링 할 수있는 항목에 대한 자연적인 한계가 분명히 있으며 이는 이미 렌더링 성능 업그레이드입니다.

이 데이터로 무엇을하는지에 달려 있습니다. GFX 성능을 위해 클리핑 을 사용 하여 숨겨진 개체, 너무 작아서 볼 수없는 개체 등을 클리핑 할 수 있습니다.

그렇다면 그래픽 성능 기술을 찾고 있다면 인터넷에서 산을 찾을 수 있다고 확신합니다.


1

볼거리는 플라이급 디자인 패턴입니다. 나는 대부분의 답변 이이 디자인 패턴을 어떤 식 으로든 참조한다고 생각합니다.

Minecraft가 각 블록 유형의 메모리를 최소화하기 위해 사용하는 정확한 방법을 모르지만 이것은 게임에서 사용할 수있는 방법입니다. 아이디어는 프로토 타입 객체와 같이 모든 블록에 대한 정보를 보유하는 객체를 하나만 갖는 것입니다. 유일한 차이점은 각 블록의 위치입니다.

그러나 위치조차도 최소화 할 수 있습니다. 토지 블록이 한 유형 인 경우 해당 위치의 크기를 하나의 위치 데이터와 함께 하나의 거대한 블록으로 저장하지 않겠습니까?

분명히 알 수있는 유일한 방법은 직접 구현을 시작하고 성능을 위해 메모리 테스트를 수행하는 것입니다. 어떻게 진행되는지 알려주세요!

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