최근에는 절차 적으로 생성 된 태양계에서 일어나는 게임을 시작했습니다. 약간의 학습 곡선 (스칼라, 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;
}