Вступ до GPU програмування з D3D12: Частина 3 - Історія
Сучасне GPU програмування має своє коріння у обрахунку 3D графіки в реальному часі. Розуміння цієї історії допоможе вам простіше розібратись чому GPU програмування так сьогодні виглядає.
Зауважте, що ігри з цього списку можуть бути не першим прикладом відповідних методів. Як виявляється, це доволі складно яка саме гра була першою у запровадженні того чи іншого методу.
Перетворення геометрії
The Sword of Damocles (1966) використав рендеринг каркасу (wireframe rendering).
Об’єкти представлено у вигляді точок і ліній між ними. Щоб відрендерити їх, враховується позиція, поворот і масштаб кожного об’єкта разом з позицією, поворотом і полем зору камери, для того щоб зпроектувати ці точки на екран. Після чого, малюються лінії між цими точками, щоб зробити іллюзію 3D об’єкта.
Головним недоліком цього підходу є те, що це лише лінії. Ви не можете мати кілька об’єктів, які блокують видимість одне одного.
Трикутники
I, Robot (1984) використав рендеринг суцільних трикутників (solid triangle rendering).
Використовуючи трикутник замість лінії, можна задати площу екрану, яку можна заповнити. Однак це саме по собі ще не вирішує проблему оклюзії. Щоб отримати правильну оклюзію, вам також потрібно переконатися, що об’єкт, який знаходиться ближче до камери, малюється поверх об’єктів, які знаходяться далі від камери.
Ми можемо досягти цього, відсортувавши об’єкти на основі відстані до камери, щоб у результуючому списку ми спочатку мали об’єкти позаду, а в кінці – об’єкти спереду, тобто сортування ззаду наперед (back to front). Таким чином, будь-який піксель, заповнений об’єктами ззаду, може бути перезаписаний об’єктами спереду, ефективно реалізуючи оклюзію. Цей підхіт також відомий як Алгоритм художника.
Однак є ще одна проблема: вам також потрібно забезпечити правильну оклюзію між різними трикутниками об’єкта. Це означає що вам також потрібно сортувати трикутники у рамках об’єкта ззаду наперед. Але є ще один трюк, який ми можемо використати щоб зменшити кількість трикутників які нам потрібно сортувати та малювати. Для кожного трикутника ми можемо визначити напрямок до якого він буде спрямований. За допомогою цієї інформації ми можемо перевірити чи він спрямований трикутник до камери чи від неї. Ми знаємо що всі трикутники, які ми побачимо, мають бути спрямовані до камери, тому ми можемо просто пропустити всі трикутники спрямовані від камери. Цей метод відомий як back-face culling.
Текстурування
Ultima Underworld: The Stygian Abyss (1992) використав тектурування.
Текстурування дозволяє не просто заливати трикутники суцільним кольором. Це робиться шляхом проекції текстури, яку можна уявити як двовимірну сітку кольорів, на трикутник. Кожна вершина відображається в точці на цій текстурі, і коли ви малюєте трикутник, ви можете інтерполювати положення на текстурі на основі відстані до вершин трикутника, зчитувати значення з цієї точки на текстурі, а потім використовувати цей колір для пікселя.
Зауважте, що була інша гра під назвою Catacomb 3D (1991), яка використовувала подібний метод роком раніше, але вона не дозволяла рухати камеру вгору або вниз, тому це не була справжня 3D-графіка в сучасному розумінні.
Буфер глибини
Super Mario 64 (1996) використовує буфер глибини (depth buffer).
Досі вам доводилося сортувати всі об’єкти та трикутники ззаду наперед, щоб отримати правильний порядок. Але є інший спосіб. Що, якщо замість сортування трикутників ми будемо просто стежити за тим, на якій відстані кожен піксель від камери? Для цього ми створимо текстуру розміром з екран, у якій кожен піксель зберігатиме число з плаваючою комою, що відповідає відстані від камери. Таким чином ми можемо пропустити сортування об’єктів і трикутників, а також отримати додаткову інформацію про кінцеве зображення, яке ми можемо пізніше використовувати для деяких додаткових ефектів.
Ось приклад того, як може виглядати буфер глибини. Натисніть щоб переглянути повне зображення.
Також, оскільки вам більше не потрібно мати визначений порядок геометрії по відстані до камери, цей метод вам також дозволяэ рендерити у випадках перетину об’єктів і трикутників. У випадку Super Mario 64, це дозволило Маріо стрибати у воду.
Ще одна цікава річ, на яку варто поглянути, це те, як би виглядала гра, якби в ній не було правильного порядку геометрії. Оскільки для цього Super Mario 64 покладається виключно на буфер глибини, якщо його вимкнути, ви зможете спостерігати які проблеми виникають у результаті неправильного порядку рендерингу.
Освітлення
Unreal (1998) використав обрахунок освітлення (shaded lighting).
Давайте повторимо як ви малюєте трикутник: спочатку ви отримуєте свої вершини, виконуєте певні обчислення для кожної вершини, зокрема відображаючи точки на екрані, знаходите набір пікселів на екрані який належить до трикутника, і для кожного пікселя виконуєте обчислення необхідні для відображення текстури. Це добре, але чому б не виконати більше обчислень, щоб ми могли спробувати отримати більш реалістичні результати.
Що зробив Unreal, так це розрахував освітлення для кожної з вершин. Оскільки ви знаєте положення вершини в сцені, ви можете пройти циклом по джерелах світла та для кожного обчислити, скільки світла потрапляэ на поверхню поруч з вершиною. Підсумуйте результати всіх джерел світла, і ви отримаєте освітлення для заданої вершини. Потім, малюючи кожен піксель, окрім інтерполяції координат текстури ще інтерполюйте результат освітлення, і ви отримаєте базовий розрахунок освітлення. Цікавий факт: це перша гра на рушії Unreal Engine, яка і дала назву рушію.
Silent Hill 2 (2004) використав обрахунок освітляння для кожного пікселя (per-pixel shaded lighting).
Така сама ідея, як і в Unreal, але замість того, щоб обчислювати освітлення на вершину, а потім інтерполювати результати, Silent Hill 2 виконує обчислення на піксель, що забезпечує точніші результати.
На відео ви можете чітко побачити різницю між розрахунком освітлення для кожної вершини (використовується на PS2) і розрахунком освітлення для кожного пікселя (використовується на оригінальному Xbox).
Shadow Mapping (Проекція тіней)
Severance: Blade of Darkness (2001) використав shadow mapping.
Виявляється, ви можете вирішити проблему обчислення затінення використовуючи буфер глибини специфічним чином. Спочатку ви візуалізуєте сцену з точки зору джерела світла використовуючи буфер глибини. Після того як це буде зроблено, у вас буде текстура яка має всю геометрію яку можна побачити під світлом. Вся інша геометрія, яку не можна побачити з точки зору джерела світла, тоді буде у тіні. Використовуючи цю текстуру ви можете перевірити чи видно задану точку на поверхні. Буфер глибини обрахований з ціллю обрахунку затінення називають картою тіней (shadowmap).
Ось приклад того, як може виглядати карта тіней. Натисніть щоб переглянути повне зображення.
Програмуємі Шейдери (Programmable Shaders)
Технічне демо від Nvidia, Chameleon (2001) використав програмуємі шейдери
Графіка в реальному часі на практиці можлива лише тоді, коли її візуалізує GPU. Але до цього технічного демо, GPU не могли виконувати довільні обчислення. Усі розрахунки необхідні для тіней та освітлення виконувалися спеціалізованими блоками, які не могли запускати довільні програми.
Ця демо є першим прикладом “Шейдерів”. Шейдер (Shader) — це програма яка може виконуватись на GPU. Існує кілька типів шейдерів, але першими з’явилися вершинні та піксельні шейдери. Вершинні шейдери виконуються для кожної вершини, і вони обчислюють властивості вершини для якої виконуються. У той час як піксельні шейдери виконуються для кожного пікселя, і, простіше кажучи, відповідають за обчислення остаточного кольору пікселя.
Ця демо показує чого можуть досягти піксельні шейдери, зокрема, як піксельні шейдери дозволяють поверхні змінювати свої властивості на основі певних умов.
Compute Shaders (Обчислювальні шейдери)
Battlefield: Bad Company 2 (2010) використав Compute Shaders.
Compute Shaders — це новий тип шейдера. На відміну від вершинних або піксельних шейдерів, вони не прив’язані безпосередньо до графіки, вам не потрібна геометрія для їх запуску. Єдине що вам потрібно це самі шейдери та кілька копій для запуску. Це забезпечує велику гнучкість того що можна з ними робити.
Спосіб їх використання в Battlefield: Bad Company 2 досить цікавий. По-перше рендериться вся геометрія, але замість того щоб зразу обчислювати освітлення, ми зберігаємо інформацію про кожен піксель у текстурах щоб обчислити освітлення пізніше. Після рендеру всієї геометрії ми обчислюємо освітлення для усіх пікселів за допомогою Computer Shader’а. Одна з переваг цього полягає в тому, що ви можете попередньо розрахувати список джерел світла, які впливають на кожну частину екрана, оскільки до моменту візуалізації всієї геометрії ви маєте повний буфер глибини та можете обчислити межі кожної частини екрану.
Важливо відзначити, що обчислювальні шейдери також можна використовувати для виконання довільних неграфічних обчислень. Одним з прикладів є симуляції рідин. Ви можете запустити таку симуляцію в реальному часі прямо у своєму браузері - Approximate grid SPH by michael0884.
Апаратне прискорення трасування променів
Battlefield V (2018) використав Апаратне прискорене трасування променів.
До цього моменту, комп’ютерна графіка в реальному часі використовувала підхід проходу по всій геометрії, щоб з’ясувати який колір отримає кожен піксель. За допомогою трасування променів ви пішли б іншим шляхом, від пікселя до геометрії. Спочатку ви будуєте “Структуру прискорення” (Acceleration Structure), яка містить геометрію яка вас цікавить, потім ви можете її використати для обрахунку перетину геометрії та променів. Наприклад, щоб з’ясувати яку геометрію видно в даному пікселі, ви можете трасувати промінь з камери який відповідає даному пікселю, і найближча геометрія, на яку потрапляє цей промінь, є геометрією, видимою для даного пікселя.
Чому це все важливо?
Незважаючи на те, що історія графіки в реальному часі налічує багато десятиліть, усі ті самі поняття та ідеї використовуються й сьогодні. Усе, що описано тут, все ще доступне та використовується на практиці в сучасних додатках.
Джерела
Частину цієї інформації було знайдено на сайті Ultimate history of video games website.