Прискорення процесу формування текстурних текстур


14

Нещодавно я почав працювати над грою, яка проходить у генерованій процедурою сонячній системі. Після трохи кривої навчання (не працював ні з Scala, OpenGL 2 ES, ні з Libgdx), у мене є основна демонстрація технологій, де ви обертаєтесь навколо однієї процедурно текстурованої планети:

введіть тут опис зображення

Проблема, з якою я стикаюся, - це продуктивність текстури. Короткий огляд того, що я роблю: планета - це куб, деформований у сферу. З кожної сторони застосовується текстура anxn (наприклад, 256 x 256), яка поєднується в одну текстуру 8n xn, що надсилається до фрагменту шейдера. Останні два пробіли не використовуються, вони знаходяться лише там, щоб переконатися, що ширина є потужністю 2. Текстура в даний час генерується в центральному процесорі, використовуючи оновлену версію алгоритму шуму симплекса 2012, пов'язану в папері "Simplex" шум демістифікований '. Сцена, яку я використовую для тестування алгоритму, містить дві сфери: планету та фон. Обидва використовують текстуру сірого масштабу, що складається з шести октав шуму симплекс-3D, тому, наприклад, якщо ми виберемо 128x128 як розмір текстури, є 128 x 128 x 6 x 2 x 6 = близько 1,2 мільйона викликів функції шуму.

Найближче до планети ви дізнаєтесь про те, що показано на скріншоті, і оскільки цільова роздільна здатність гри - 1280x720, це означає, що я вважаю за краще використовувати текстури 512x512. Поєднайте, що фактично фактури, звичайно, будуть складнішими, ніж основний шум (Буде денна та нічна текстура, змішана у фрагменті шейдера на основі сонячного світла, та дзеркальна маска. Мені потрібен шум для континентів, зміна кольору місцевості , хмари, вогні міста та ін.), і ми дивимось на щось на зразок 512 x 512 x 6 x 3 x 15 = 70 мільйонів шумових дзвінків лише для планети. У фінальній грі будуть подорожувати планети, тому зачекати 5 або 10 секунд, можливо, 20, було б прийнятним, оскільки я можу обчислити текстуру у фоновому режимі під час подорожі, хоча, очевидно, чим швидше, тим краще.

Повертаючись до нашої тестової сцени, продуктивність на моєму ПК не надто жахлива, хоча все ще занадто повільна, враховуючи, що кінцевий результат буде приблизно в 60 разів гіршим:

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

Це після того, як я перемістив весь критичний для продуктивності код на Java, оскільки спроба зробити це в Scala було набагато гірше. Однак, якщо це працює на моєму телефоні (Samsung Galaxy S3), проте, є більш проблемний результат:

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

Вже занадто довго, і це навіть не враховує те, що в остаточній версії це буде хвилини замість секунд. Очевидно, що щось потрібно зробити. Особисто я бачу кілька потенційних проспектів, хоча я ще не особливо захоплююсь жодною з них:

  • Не заздалегідь розраховуйте текстури, але нехай шейдер фрагмента все обчислює. Напевно, це неможливо, бо в один момент у мене був фон як повноекранний квадроцикл з піксельним шейдером, і я отримав близько 1 кадрів в секунду на своєму телефоні.
  • Використовуйте графічний процесор, щоб повторно відобразити текстуру, зберегти її та використовувати з цього часу збережену текстуру. Вгору: може бути швидше, ніж робити це на процесорі, оскільки GPU повинен бути швидшим при розрахунках з плаваючою комою. Зворотна сторона: ефекти, які неможливо (легко) виразити як функції симплексного шуму (наприклад, газові вихори планети, місячні кратери тощо), в GLSL набагато складніше зафіксувати, ніж у Scala / Java.
  • Обчисліть велику кількість шумових текстур і відправте їх із додатком. Я хотів би цього уникнути, якщо це можливо.
  • Опустіть роздільну здатність. Мені купується 4-кратне підвищення продуктивності, що насправді недостатньо, я втрачаю багато якості.
  • Знайдіть більш швидкий алгоритм шуму. Якщо у кого є такий, я все вуха, але симплекс вже повинен бути швидшим за перлін.
  • Прийняти піксельний стиль мистецтва, що дозволяє текстури меншої роздільної здатності та меншу кількість октав шуму. Хоча я спочатку передбачав гру в цьому стилі, я віддав перевагу реалістичному підходу.
  • Я роблю щось не так, і виступ повинен бути вже на один-два порядки кращим. Якщо це так, будь ласка, повідомте мене про це.

Якщо у когось є якісь пропозиції, поради, вирішення чи інші коментарі щодо цієї проблеми, я хотів би їх почути.

У відповідь на 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 як таке, але вам слід зателефонувати dispose () на вашу піксельну карту після того, як ви закінчите з цим.
мотлох

Відповіді:


10

Ви можете комбінувати підходи (2) та (3) так:

  • По-перше, використовуйте GPU, щоб створити ряд шумових текстур і зберегти їх. Це буде ваш "кеш-шум"; ви можете це зробити лише один раз під час першого запуску.
  • Щоб створити текстуру в грі, комбінуйте кілька текстур з кешу - це повинно бути справді швидким. Потім, якщо потрібно, додайте до цього спецефекти, наприклад, вихори.
  • Крім того, ви можете попередньо генерувати деякі текстури "спеціальних ефектів" і просто їх змішувати, щоб отримати остаточний результат.

+1 Я думаю, що найкращим способом можна створити купу текстур та упакувати їх за допомогою гри, щоб поєднувати чи застосовувати прості афекти.
TheNickmaster21

2

Процедурна генерація текстури - це ab * * мофо в термінах обчислення. Це те, що воно є.

Найкраща реалізація я знайшов Simplex Noise - це Стефан Густавсон .

Крім покращення фактичного часу на обчислення (насправді досить важко обійти той факт, що ви просто вимагаєте багато з комп’ютера під час обчислення процедурних текстур 1024x1024), одним із найкращих способів скоротити сприйнятий час очікування є наявність ваш додаток виконує якомога більше фонових ниток.

Тож почніть генерувати текстури під час запуску гри на фоновому потоці , поки користувач все ще спостерігає за параметрами та меню або переглядає трейлер рівня рівня.

Інша річ, яку слід врахувати, - просто запам’ятати на диск кілька сотень генерованих текстур і довільно вибрати одну з них під час завантаження. Більше диска, але менше часу на завантаження.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.