Hdr vs ldr, реалізація hdr rendering

Hdr vs ldr, реалізація hdr rendering

Як я і обіцяв - публікую другу статтю про деякі моменти розробки ігор в трьох вимірах. Сьогодні розповім про одну техніці, яка використовується майже будь-якому проекті ААА-класу. Ім'я їй - HDR Rendering. Якщо цікаво - ласкаво просимо під хабракат.






Але спочатку треба поговорити. Виходячи з минулої статті - зрозумів, що і аудиторія Хабрахабр поховала технологію Microsoft XNA. Робити, нібито, за допомогою його щось - все одно, що писати гри на ZX Spectrum. У приклад мені привели: "Є ж адже SharpDX, SlimDX, OpenTK! ", Навіть приводили в приклад Unity. Але зупинимося на перших трьох, все це - чистої води врапперов DX'a під .NET. а Unity так взагалі движок-пісочниця. Що взагалі з себе представляє DirectX10 +. Адже його немає і не буде в XNA. Так ось, що пригнічують к-ть ефектів, фішок і технологій реалізується саме на базі DirectX9c. А DirectX10 + вводить лише додатковий функціонал (SM4.0, SM5.0).

Взяти, наприклад, Crysis 2:

Hdr vs ldr, реалізація hdr rendering

Hdr vs ldr, реалізація hdr rendering

У цих двох скріншотах немає ніякого DirextX10 і DirectX11. Так чому люди думають, що робити щось на XNA - займатися некрофілією? Так, Microsoft перестала підтримувати XNA. але запасу того, що там є - вистачить на 3 роки точно. Більш того, зараз існує monogame. він опенсорсний, багатоплатформовий (win, unix, mac, android, ios, etc) і зберігає всю ту ж архітектуру XNA. До речі, FeZ з минулої статті написаний з використанням monogame. Ну і наостанок - статті спрямовані в цілому то на комп'ютерну графіку в трьох вимірах (всі ці положення справедливі і для OpenGL. І для DirectX), а не XNA - як можна подумати. XNA в нашому випадку всього-лише інструмент.

Гаразд, поїхали

Зазвичай в іграх використовується LDR (Low Dynamic Range) рендеринг. Це означає, що колір бек-буфера обмежений в межах 0 ... 1. Де на кожен канал приділяється по 8 біт, а це 256 градацій. Наприклад: 255, 255, 255 - білий колір, всі три канали (RGB) рівні максимальної градації. Поняття LDR несправедливо застосовувати до поняття реалістичного рендеринга, тому що в реальному світі колір задається далеко не нулем і одиницею. На допомогу до нас приходить така технологія, як HDRR. Для початку, що таке HDR. High Dynamic Range Rendering. іноді просто «High Dynamic Range» - графічний ефект, який застосовується в комп'ютерних іграх для більш виразного рендеринга зображення при контрастному освітленні сцени. В чому полягає суть цього підходу? У тому, що ми малюємо нашу геометрію (і освітлення) не обмежуючи нулем і одиницею: одне джерело світла може дати яскравість пікселя в 0.5 одиниць, а інший в 100 одиниць. Але як можна замінити на перший погляд, то наш екран відтворює якраз той самий LDR формат. І якщо ми всі значення кольору бек-буфера розділимо на максимальну яскравість в сцені - вийде той же LDR. а джерело світла в 0.5 одиниць майже не буде видно на тлі другого. І якраз для цього була придумана особлива метод званий Tone Mapping. Суть цього підходу, що ми наводимо динамічний діапазон до LDR в залежності від середньої яскравості сцени. І для того, щоб зрозуміти про що я, розглянемо сцену: дві кімнати, одна кімната indoor. інша outdoor. Перша кімната - має штучне джерело світла, друга кімната - має джерело світла у вигляді сонця. Яскравість сонця на порядок вище, ніж яскравість штучного джерела світла. І в реальному світі, при знаходженні в першій кімнаті - ми адаптуємося до цього освітлення, при вході в іншу кімнату ми адаптуємося до іншого рівня освітлення. При погляді з першої кімнати в другу - вона буде здаватися нам надмірно яскравою, а при погляді з другої в першу - чорною.

Ще один приклад: одна outdoor кімната. У цій кімнаті - є саме сонце і розсіяне світло від сонця. Яскравість сонця на порядок вище, ніж його розсіяне світло. У разі LDR значення яскравості світла були б рівні. Тому, використовуючи HDR можна домогтися реалістичних відблисків з різних поверхонь. Це дуже помітно на воді:







Hdr vs ldr, реалізація hdr rendering

Або на відблисках з сурфейса:

Hdr vs ldr, реалізація hdr rendering

Ну і контрастність сцени в цілому (зліва HDR, праворуч LDR):

Hdr vs ldr, реалізація hdr rendering

Разом з HDR прийнято застосовувати і технологію Bloom. яскраві області розмиваються і накладаються поверх основного зображення:

Hdr vs ldr, реалізація hdr rendering

Це робить освітлення ще м'якше.

Так само, у вигляді бонусу - розповім про Color Grading. Цей похід повсюдно застосовується в іграх ААА-класу.

Color Grading

Дуже часто в іграх сцена повинна мати свій колірний тон, цей колірний тон може бути загальним як для всієї гри, так і для окремих ділянок сцени. І щоб кожен раз не мати по сто шейдеров-постпроцесорів - використовують підхід Color Grading. У чому суть цього підходу?

Знамениті букви RGB - колірне тривимірний простір, де кожен канал це своєрідна координата. У разі формату R8G8B8: 255 градацій на кожен канал. Так ось, що буде, якщо ми застосуємо звичайні операції обробки (наприклад, криві або контрастність) до цього простору? Наш простір зміниться і в майбутньому ми можемо призначити будь-якого пікселя - піксель з цього простору.

Створимо просте RGB простір (хочу замінити, що беремо ми кожен 8-ий піксель, тому що якщо будемо брати все 256 градацій, то розмір текстури буде дуже великим):

Це тривимірна текстура, де на кожну вісь - свій канал.

І візьмемо якусь сцену, яку потрібно модифікувати (додавши при цьому на зображення наш простір):

Hdr vs ldr, реалізація hdr rendering

Проводимо потрібні нам трансформації (на око):

Hdr vs ldr, реалізація hdr rendering

І витягаємо наше модифікуються простір:

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

Реалізація

Ну і коротко по реалізації HDR в XNA. У XNA формат бек-буфера задається (в основному) R8G8B8A8. тому рендеринг прямо на екран не може підтримувати HDR апріорі. Для цього обходу цього - нам потрібно створити новий RenderTarget (раніше я описував роботу ційних тут) з особливим форматом: HalfVector4 *. Цей формат підтримує плаваючі значення у RenderTarget.

* - в XNA є такий формат - як HDRBlendable. це все той же HalfVector4 - але сам RT займає менше місця (тому що на альфа канал нам не потрібен floating-point).

Заведемо потрібний RenderTarget:

Створюємо новий RT з розмірами бек-буфера (дозволом екрану) з відключеним mipmap (тому що ця текстура буде малюватися на екранному квадов) з форматом сурфейса - HdrBlendable (або HalfVector4) і 24-ох бітним буфером глибини / СТЕНС-буферів 8 біт . Так само відключимо multisampling.
У цього RenderTarget важливо включити буфер глибини (на відміну від звичайного post-process RT), тому що ми будемо малювати туди нашу геометрію.

Далі - все як в LDR. ми малюємо сцену, тільки тепер не потрібно обмежуватися малюванням яскравості [0. 1].
Додамо skybox з номінальною яскравістю помноженої на три і класичний чайник Юта з DirectionalLight -Освітлення і Reflective -Поверхня.

Сцена створена і тепер нам потрібно як-небудь формат HDR привести до LDR. Візьмемо найпростіший ToneMapping - розділимо всі ці величини на умовне значення max.

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

У реальному ж житті - наше око адаптується до потрібного висвітлення: в погано освітленій кімнаті ми все одно бачимо, але до тих пір, поки перед нашими очима немає яскравого джерела світла. Це називається світловий адаптацією. І найкрутіше то, що HDR і колірна адаптація ідеально поєднується один з одним.

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

І будемо малювати кожен попередній RT в наступний RT застосовуючи деякий розмиття. Після цих циклів у нас вийде текстура 1x1. яка власне і буде містити середній колір.

Якщо це все зараз запустити, то колірна адаптація дійсно буде, але вона буде моментальною, а так не буває. Нам потрібно, щоб при погляді з різко темної області на різко світлу - спочатку відчували сліпоту (у вигляді підвищеної яскравості), а потім все приходило в норму. Для цього достатньо завести ще один RT 1x1. який і буде відповідати за поточне значення адаптації, при цьому, кожен кадр ми наближаємо поточну адаптацію до обчисленого в даний момент кольором. Причому, значення цього наближення має бути зав'язане на все тому ж gameTime.ElapsedGameTime. щоб кількість FPS не впливало на швидкість адаптації.

Ну і тепер в якості параметра max для _toneSimple можна передавати наш середній колір.

Існує маса формул ToneMapping 'a, ось деякі з них:







Схожі статті