Compute Shaders в Unity: обчислення на графічному процесорі, перший обчислювальний шейдер

Даніл Гошко — Technical Art Director, а також веде свій блог на ArtStation — Decompiled Art. Цього місяця Даніл завершив серію туторіалів, присвячених обчислювальним шейдерам в Unity. З дозволу автора ми перекладемо всю серію з трьох матеріалів українською.

Далі переклад першої статті з серії. Слідкуйте за оновленнями форуму, щоб не пропустити наступні.



Вітаю вас у статтях Decompiled Art! У цій серії блогів ми здійснимо подорож, протягом якої дізнаємося, що таке обчислювальні шейдери, створимо власні та дослідимо їх застосування в різних областях. Ми детально розглянемо не лише технічні аспекти, але й надамо приклади реального використання, щоб допомогти вам побачити потенціал цієї захопливої технології. Усі відповідні результати будуть генеруватися в Unity для демонстрації концепцій.

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

Обчислення на графічному процесорі (GPU)

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

  • Наукові симуляції: Графічні процесори широко використовуються для наукових симуляцій, таких як моделювання клімату, астрофізики та молекулярної динаміки. Ці симуляції вимагають обробки величезних обсягів даних, яку можна ефективно виконати завдяки паралельній обчислювальній потужності GPU.
  • Машинне навчання: Алгоритми машинного навчання часто потребують обробки великих обсягів даних, що можна зробити ефективніше за допомогою GPU. Графічні процесори можуть прискорювати завдання, такі як тренування нейронних мереж, що є основним компонентом багатьох застосувань машинного навчання.
  • Обробка відео: GPU можна використовувати для прискорення завдань обробки відео, таких як кодування, декодування та перекодування відео. Це забезпечує швидший час візуалізації та кращу якість результату.
  • Гральна індустрія: графічні процесори давно використовуються для ігор, оскільки можуть відтворювати високоякісну графіку та забезпечувати її плавну роботу. Однак GPU також можна використовувати для неграфічних завдань в іграх, таких як симуляції фізики та штучного інтелекту.

Останнім часом кількість обчислювальних програм GPU зросла, а список доступних рішень продовжує розширюватися. Серед фреймворків, які найбільше використовуються у цій області, є CUDA, OpenCL, DirectCompute та Metal.

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



Обчислювальні шейдери (Compute Shaders)

Вперше представлені компанією NVIDIA в 2006 році, обчислювальні шейдери — це тип програм шейдерів, які працюють на GPU та призначені для виконання обчислювальних завдань загального призначення. На відміну від традиційних графічних шейдерів, які використовуються для візуалізації зображень, обчислювальні шейдери можна застосувати для широкого кола завдань. Варто зазначити, що вони не включені за замовчуванням у пайплайн (навіть вони використовують апаратне забезпечення GPU).



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

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

Отже, основна відмінність між архітектурою CPU та GPU полягає в їхніх цілях проектування. ЦП оптимізовано для швидкого виконання різноманітних завдань, які зазвичай вимірюються тактовою частотою, але вони мають обмеження щодо одночасної обробки завдань. З іншого боку, графічні процесори спеціально розроблені для паралельної роботи.

Реалізація обчислювальних шейдерів в Unity

Обчислювальні шейдери в Unity тісно пов’язані з технологією DirectX11 DirectCompute. Використовувана мова — HLSL.

Вони сумісні з різними платформами:

  • Windows і Windows Store (з графічним API DirectX 11 або DirectX 12 і графічним процесором Shader Model 5.0)
  • macOS та iOS (з використанням Metal graphics API)
  • Android
  • Платформи Linux і Windows з API Vulkan, платформи OpenGL (OpenGL 4.3 на Linux або Windows; OpenGL ES 3.1 на Android).
  • Сучасні ігрові консолі.

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

Перший Compute Shader

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

По-перше, переконайтеся, що ваша система підтримує обчислення. В Unity цю інформацію може надати SystemInfo.supportsComputeShaders.

У цьому прикладі я створив просту структуру інтерфейсу користувача для візуалізації підтримки обчислень.

Створіть новий C#-скрипт (у моєму випадку під назвою CheckComputeSupport) і додайте його як новий компонент до будь-якого об’єкту гри, який зараз присутній на сцені (у моєму випадку об’єкт гри під назвою CheckSupport).

Скрипт для перевірки підтримки обчислень (Computes):


public class CheckComputeSupport : MonoBehaviour
{
    [SerializeField] private TMP_Text text;
    private void Start()
    {
        if(text != null)
            text.text = "Support Compute: " + SystemInfo.supportsComputeShaders;
    }
}

Коли ви увімкнете Play mode, він покаже такий результат:



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

Створіть новий обчислювальний шейдер (у моєму випадку з назвою CS_00) і C#-скрипт (у моєму випадку з назвою GenerateRenderTexture), щоб його викликати.

Також додайте новий об’єкт Quad і встановіть на нього матеріал Unlit. Для цього я створив новий типовий шейдер Unlit і призначив йому використовувати матеріал M_Unlit_01.



Далі відкрийте новостворений C#-скрипт і вставте цей код:


using UnityEngine;




namespace CS_00
{
    [RequireComponent(typeof(Renderer))]
    public class GenerateRenderTexture : MonoBehaviour
    {
        [SerializeField] private ComputeShader computeShader;
        [SerializeField] private string kernelName = "CSMain";
        [SerializeField] private int resolution = 128;








        private RenderTexture _renderTexture;
        private int _kernelHandle;
    
        private Renderer _renderer;
        private static readonly int MainTex = Shader.PropertyToID("_MainTex");
    
        private void Start()
        {
            //GET RENDERER COMPONENT REFERENCE
            TryGetComponent(out _renderer);
        
            //CREATE NEW RENDER TEXTURE TO RENDER DATA TO
            _renderTexture = new RenderTexture(resolution, resolution, 0)
            {
                enableRandomWrite = true
            };
            _renderTexture.Create();








            //COMPUTE SHADER & RESULTING RENDERTEXTURE SETUP 
            _kernelHandle = computeShader.FindKernel(kernelName);
            computeShader.SetTexture(_kernelHandle, "Result", _renderTexture);
            _renderer.sharedMaterial.SetTexture(MainTex, _renderTexture);
        
            computeShader.Dispatch(_kernelHandle, resolution/8, resolution/8, 1);
        }








        private void OnDisable()
        {
            if (_renderTexture != null)
                Destroy(_renderTexture);
            
            _renderer.sharedMaterial.SetTexture(MainTex, null);
        }
    }
}

Для обчислювальних шейдерів (Compute Shader) використовуйте цей код:


#pragma kernel CSMain




RWTexture2D<float4> Result;




[numthreads(8,8,1)]
void CSMain (uint3 id : SV_DispatchThreadID)
{
    Result[id.xy] = float4(id.x & id.y, (id.x & 15)/15.0, (id.y & 15)/15.0, 0.0);
}

Не хвилюйтесь, у наступних статтях ми розберемо код, що використовується для обчислювального шейдера, а також код, який міститься у скрипті C#.

Додайте компонент GenerateRenderTexture до Quad gameObject (AddComponent -> GenerateRenderTexture). Це додасть новий компонент зі значеннями, встановленими за замовчуванням.

Увімкніть режим відтворення (PlayMode) та перевірте результат. Він має бути приблизно таким:



Вітаю з успішним налаштуванням та запуском вашого першого обчислювального шейдера. Як я вже зазначав, у наступній статті ми заглибимося в технічні деталі та основні структурні компоненти, які входять до складу обчислювального шейдера.

Дякую за увагу і до зустрічі!




Підписуйтеся на Telegram-канал @gamedev_dou, щоб не пропустити найважливіші статті і новини про геймдев

👍ПодобаєтьсяСподобалось5
До обраногоВ обраному4
LinkedIn
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter

Крутий матеріал! Продовжуйте, будь ласка

Підписатись на коментарі