절차 적 텍스처 생성 속도 향상


14

최근에는 절차 적으로 생성 된 태양계에서 일어나는 게임을 시작했습니다. 약간의 학습 곡선 (스칼라, OpenGL 2 ES 또는 Libgdx와 함께 작동하지 않았 음) 후에 절차 적 질감을 가진 단일 행성 주위를 회전하는 기본 기술 데모가 있습니다.

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

내가 겪고있는 문제는 텍스처 생성의 성능입니다. 내가하고있는 일에 대한 빠른 개요 : 행성은 구로 변형 된 큐브입니다. 각면에 anxn (예 : 256 x 256) 텍스처가 적용되며, 조각 쉐이더로 전송되는 하나의 8n xn 텍스처에 번들로 제공됩니다. 마지막 두 공간은 사용되지 않고 너비가 2의 거듭 제곱인지 확인하기 위해서만 있습니다. 텍스처는 현재 'Simplex' 에 링크 된 2012 년 버전의 단면 노이즈 알고리즘의 최신 버전을 사용하여 CPU에서 생성됩니다. 소음이 밝혀졌다 '. 알고리즘을 테스트하는 데 사용하는 장면에는 행성과 배경의 두 구가 있습니다. 둘 다 6 옥타브의 3D 심플 렉스 노이즈로 구성된 그레이 스케일 텍스처를 사용합니다. 예를 들어 텍스처 크기로 128x128을 선택하면 노이즈 함수에 대한 128 x 128 x 6 x 2 x 6 = 약 120 만 호출이 있습니다.

지구상에서 가장 가까운 것은 스크린 샷에 표시되는 것에 관한 것이며 게임의 목표 해상도는 1280x720이므로 512x512 텍스처를 선호합니다. 실제 텍스처는 기본 노이즈보다 더 복잡하다는 사실과 결합하십시오 (낮과 밤 텍스처는 햇빛을 기반으로 한 조각 쉐이더와 블렌드 마스크에 혼합됩니다. 대륙, 지형 색상 변화에 노이즈가 필요합니다) , 구름, 도시 조명 등) 우리는 512 x 512 x 6 x 3 x 15 = 7 천만 개의 소음이 지구를 요구한다는 것을보고 있습니다. 마지막 게임에서는 행성 사이를 여행 할 때 활동이있을 것이므로 여행 중에 배경의 질감을 계산할 수 있기 때문에 5 ~ 10 초, 아마도 20 초의 대기 시간이 허용됩니다.

테스트 장면으로 돌아가서 내 PC의 성능은 끔찍하지는 않지만 최종 결과가 약 60 배 나빠질 것이라는 점을 고려하면 여전히 느립니다.

128x128 : 0.1s
256x256 : 0.4s
512x512 : 1.7s

스칼라에서 그렇게하는 것이 훨씬 나빴 기 때문에 이것은 성능에 중요한 모든 코드를 Java로 옮긴 후에입니다. 그러나 내 전화 (Samsung Galaxy S3)에서 이것을 실행하면 더 문제가 발생합니다.

128x128 :  2s
256x256 :  7s
512x512 : 29s

이미 너무 길어서 최종 버전에서 몇 초가 아닌 몇 분이 걸릴 것이라는 사실조차 고려하지 않았습니다. 분명히 무언가가 이루어져야합니다. 개인적으로, 나는 아직 그중 어느 것에도 관심이 없지만 몇 가지 잠재적 인 길을 봅니다.

  • 텍스처를 미리 계산하지 말고 조각 셰이더가 모든 것을 계산하도록하십시오. 한 시점에서 배경을 픽셀 쉐이더가있는 전체 화면 쿼드로 사용하고 휴대 전화에서 약 1fps를 얻었 기 때문에 아마도 실현 가능하지 않습니다.
  • GPU를 사용하여 텍스처를 한 번 렌더링하고 저장 한 다음 저장된 텍스처를 사용하십시오. 거꾸로 : GPU가 부동 소수점 계산에서 더 빠르기 때문에 CPU에서 수행하는 것보다 빠를 수 있습니다. 단점 : 단순한 소음 (예 : 가스 행성 소용돌이, 달 분화구 등)의 함수로 (쉽게) 표현할 수없는 효과는 Scala / Java보다 GLSL에서 코딩하기가 훨씬 더 어렵습니다.
  • 많은 양의 노이즈 텍스처를 계산하여 애플리케이션과 함께 제공하십시오. 가능하다면 이것을 피하고 싶습니다.
  • 해상도를 낮추십시오. 나에게 4 배의 성능 향상을 사면 실제로 충분하지 않으며 많은 품질을 잃습니다.
  • 더 빠른 노이즈 알고리즘을 찾으십시오. 누구든지 내가 모두 귀를 가지고 있지만 심플 렉스는 이미 펄린보다 빠릅니다.
  • 낮은 해상도 텍스처와 적은 노이즈 옥타브를 허용하는 픽셀 아트 스타일을 채택하십시오. 원래이 스타일로 게임을 구상했지만 현실적인 접근 방식을 선호하게되었습니다.
  • 나는 무언가를 잘못하고 있으며 성능은 이미 1-2 배 더 좋을 것입니다. 이 경우 알려 주시기 바랍니다.

누구 든지이 문제에 관한 제안, 팁, 해결 방법 또는 기타 의견이 있으면 듣고 싶습니다.

Layoric에 대한 응답으로 사용중인 코드는 다음과 같습니다.

//The function that generates the simplex noise texture
public static Texture simplex(int size) {
    byte[] data = new byte[size * size * columns * 4];
    int offset = 0;
    for (int y = 0; y < size; y++) {
        for (int s = 0; s < columns; s++) {
            for (int x = 0; x < size; x++) {
                //Scale x and y to [-1,1] range
                double tx = ((double)x / (size - 1)) * 2 - 1;
                double ty = 1 - ((double)y / (size - 1)) * 2;

                //Determine point on cube in worldspace
                double cx = 0, cy = 0, cz = 0;
                if      (s == 0) { cx =   1; cy =  tx; cz =  ty; }
                else if (s == 1) { cx = -tx; cy =   1; cz =  ty; }
                else if (s == 2) { cx = - 1; cy = -tx; cz =  ty; }
                else if (s == 3) { cx =  tx; cy = - 1; cz =  ty; }
                else if (s == 4) { cx = -ty; cy =  tx; cz =   1; }
                else if (s == 5) { cx =  ty; cy =  tx; cz = - 1; }

                //Determine point on sphere in worldspace
                double sx = cx * Math.sqrt(1 - cy*cy/2 - cz*cz/2 + cy*cy*cz*cz/3);
                double sy = cy * Math.sqrt(1 - cz*cz/2 - cx*cx/2 + cz*cz*cx*cx/3);
                double sz = cz * Math.sqrt(1 - cx*cx/2 - cy*cy/2 + cx*cx*cy*cy/3);

                //Generate 6 octaves of noise
                float gray = (float)(SimplexNoise.fbm(6, sx, sy, sz, 8) / 2 + 0.5);

                //Set components of the current pixel
                data[offset    ] = (byte)(gray * 255);
                data[offset + 1] = (byte)(gray * 255);
                data[offset + 2] = (byte)(gray * 255);
                data[offset + 3] = (byte)(255);

                //Move to the next pixel
                offset += 4;
            }
        }
    }

    Pixmap pixmap = new Pixmap(columns * size, size, Pixmap.Format.RGBA8888);
    pixmap.getPixels().put(data).position(0);

    Texture texture = new Texture(pixmap, true);
    texture.setFilter(TextureFilter.Linear, TextureFilter.Linear);
    return texture;
}

//SimplexNoise.fbm
//The noise function is the same one found in http://webstaff.itn.liu.se/~stegu/simplexnoise/SimplexNoise.java
//the only modification being that I replaced the 32 in the last line with 16 in order to end up with
//noise in the range [-0.5, 0.5] instead of [-1,1]
public static double fbm(int octaves, double x, double y, double z, double frequency) {
    double value = 0;
    double f = frequency;
    double amp = 1;
    for (int i = 0; i < octaves; i++) {
        value += noise(x*f, y*f, z*f) * amp;
        f *= 2;
        amp /= 2;
    }
    return value; 
}

소음 기능을 위해 현재 Java로 가지고있는 것을 게시 할 수 있습니까? 성능 향상이 있다고 말하지는 않지만 일부는 성능 향상을 위해 무언가를 발견 할 수 있습니다.
대런 리드

사용중인 코드를 원래 게시물에 추가했습니다.
FalconNL

Q 자체와 관련이 없지만 pixmap에서 dispose ()를 호출 한 후에 호출해야합니다.
junkdog

답변:


10

다음과 같이 접근법 (2)와 (3)을 결합 할 수 있습니다.

  • 먼저 GPU를 사용 하여 많은 노이즈 텍스처 를 생성 하고 저장하십시오. 이것은 "노이즈 캐시"입니다. 첫 번째 실행에서 한 번만 수행 할 수 있습니다.
  • 게임 내에서 텍스처를 생성하려면 캐시에서 몇 가지 텍스처를 결합하십시오-이것은 매우 빠릅니다. 그런 다음 필요한 경우 소용돌이와 같은 특수 효과를 추가하십시오.
  • 또는 일부 "특수 효과"텍스처를 미리 생성 한 다음 혼합하여 최종 결과를 얻을 수도 있습니다.

+1 많은 텍스처를 생성하고 게임에 패키징하여 간단한 효과를 결합하거나 적용하는 것이 최선의 방법이라고 생각합니다.
TheNickmaster21

2

절차 적 텍스처 생성은 계산 시간 측면에서 mofo의 ab * * 입니다. 그것이 무엇입니까.

내가 찾은 Simplex Noise의 가장 좋은 구현은 Stefan Gustavson 's 입니다.

실제 계산 시간의 개선을 넘어서 ( 지각 적 으로 1024x1024 절차 적 텍스처를 계산할 때 단순히 컴퓨터에서 많은 것을 요구한다는 사실을 지나치 기란 꽤 어렵습니다 ), 인지 된 대기 시간 을 줄이는 가장 좋은 방법 중 하나 는 앱은 가능한 많은 백그라운드 스레드 작업을 수행합니다.

따라서 배경 스레드에서 게임을 시작할 때 텍스처 생성을 시작 하십시오. 사용자는 여전히 옵션과 메뉴를 보거나 레벨 시작 트레일러를보고 있습니다.

고려해야 할 다른 것은 수백 개의 생성 된 텍스처를 디스크에 캐시하고로드시 이들 중 하나를 임의로 선택하는 것입니다. 디스크는 많지만로드 시간은 줄어 듭니다.

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