Після багатьох років роботи з Flash і ігровими движками використовують його як оболонку, де все необхідно створювати вручну, а можливості обмежені парою-трійкою пунктів, новина про роботу з Unity викликала у мене ейфорію.
"Працювати в Unity так приємно і зручно: багато можливостей для самореалізації "- говорили мені, але все виявилося не так просто. У цій статті не буде співатися хвалебних од Unity. Ця стаття про суворі реалії, обмеження і складнощі, з якими зіткнулася наша маленька команда інді-розробників при створенні своєї першої, але досить великої гри Death Point.
Початок був багатообіцяючим. Всього за місяць ми зібрали прототип в якому був невеликий рівень, персонаж і пара охоронців займаються своїми неабиякими справами.
Перші проблеми виникли в той момент, коли я нарешті вирішив спробувати на зубок систему освітлення. З огляду на те, що ми хотіли досягти кроссплатформенності для гри, було принципово важливо підтримувати однаково високий рівень графіки на будь-якому пристрої. Саме тут я вперше загруз у слабкій, як потім стало зрозуміло, документації Unity.
Освітлення
Наші сили були вкрай обмежені і я вибрав тайлову систему. Гнучка, що дозволяє досягти хорошого візуального розмаїття, обмеженими засобами. Дивлячись на свій бляклий рівень, я зі сльозами на очах штудіював документацію по Unity в пошуках відповіді «налаштування і робота системи освітлення». Документація виявилася досить поверхневою і не давала повного розуміння про роботу системи. Врятувала мене документація Unreal Engine, що розкриває багато аспектів того, як це працює в ігровому движку і дає безліч відповідей на питання «як потрібно робити». Ще пара трійка хороших статей і я був готовий творити.
Першим ударом стала неймовірна різниця між тим, що Unity відображає в редакторі при real-time render і bake. Красиві тіні після запікання перетворювалися на кашу, джерела світла забивали один одного, їх яскравість і колір змінювалися кардинально.
По суті, все налаштування освітлення звелося до запам'ятовування того чи іншого джерела світла при різних конфігураціях. Виставив параметр - включив рендер. Не сподобався результат - змінюєш налаштування і рендериш знову.
Потім, відсутність HDR у варіації для мобільних пристроїв. Скрізь є галочки, що дозволяють його включити, але в результаті виходило тільки моторошне місиво кольорів від джерела світла на об'єктах, Unity просто не розуміє як зберігати light-map в HDR. Як наслідок, текстура висвітлюється до рівня albedo, і на краях освітленої області з'являється «райдужний» градієнт.
Найперший тест запеченого світла
Після нетривалої війни, в якій я безумовно програвав, вирішено було піти обхідними шляхами. Щоб домогтися ефекту засвічення, я просто почав переробляти всі матеріали, щоб карти albedo були світлими, і навіть невеликі значення в інтенсивності джерела світла давало ефект засвічення.
Після багатьох маніпуляцій - гарний градієнт з малюнком
У цій же картинці можна побачити, що перед лампою стоїть 3d об'єкт з прозорою текстурою, що виконує роль маски. Все тому, що розробники не додали можливість виставляти для baked джерела світла текстурну маску cookies (форму світлової плями).
Окремим пунктом можна згадати directional-lightmap - прекрасне технічне рішення, але через мобільну спрямованість ми не стали його використовувати. Самі lightmap не мало важать, а directional-lightmap подвоюють це значення. Ми не могли жертвувати такою кількістю ресурсів, так як гра повинна була добре працювати і на мобільних пристроях. Причина в тому, що весь ефект directional-lightmap видно тільки у випадку, коли джерело світла потрапляє в камеру. А таких моментів у top-down грі дуже мало.
Розмір має значення
Черговим неприємним моментом стала система кешування, яка працює з помилками, що дуже сильно впливає на кінцевий результат. Якщо запікати світло з попереднім очищенням попереднього результату, то отримуєш те ж саме. Але якщо запікати поверх наявного результату, то кожен наступний відрізняється від попереднього, нехай небагато, але після декількох ітерацій різниця була колосальна. Ні, це не те що б поганий результат, але вкрай не передбачуваний.
Коли прийшов час зрозуміти, на що ж ми здатні, був зібраний досить значний рівень. І тут знову удар нижче пояса від розробників Unity - в консоль падають помилки про те, що неможливо запекти lightmap, і рівень покривається чорними смугами. Занадто великий рівень і занадто високі налаштування освітлення. Шкода.
Ось він, той рівень, з хорошим освітленням, налаштованими reflection пробами, light-probe для освітлення динамічних об'єктів і з частково реалізованим геймплем. Виглядає добре, грається відмінно. Погляд падає на кількість drawcalls - провал, повний провал... їх близько двох тисяч.
Звідки такі цифри? На сцені всього сотня унікальних об'єктів, а клони повинні батчитися внутрішньою системою. Тиждень пошуків не увінчалися успіхом, кілька здогадок і припущень, тонна тестів і копирсання в налаштуваннях, - все марно. У якийсь момент Unity геть відмовлялася об'єднувати однакові об'єкти при рендері, відмальовуючи їх по одному.
Наш гейм-дизайнер виявився розумним хлопцем і хорошим програмістом. Всього за кілька тижнів він написав свою систему оптимізації, яка угруповала і об'єднувала міші, створювала атласи з усіх об'єктів використовуваних в грі і ще купу різних речей яких я не розумію =). У підсумку ми домоглися прийнятного для мобільного додатку кількості drawcalls. При максимальній деталізації сцени їх було не більше 250. Все ще багато, але хороший телефон справляється на ура, видаючи ті самі, заповітні, 60 FPS.
Після введення системи оптимізації кількість drawcalls стабільно тримається в районі 100.
Власний шейдер
І все ж, з картинкою було щось не так. Результат був далекий від очікуваного і, тим більше, бажаного. Я не відразу побачив, що моделі виглядають занадто різними в 3D-coat, де я роблю текстури, і Unity вважаючи, що вся справа в оточенні цього об'єкта.
Незважаючи на величезну роботу виконану розробниками Unity з оптимізації стандартного шейдера, тести на пристроях показали, що все не дуже добре. Так, швидко, але не так як хотілося б. Так PBR (Physically based rendering), але виглядає дивно. Неправильна робота reflection sphere на мобільних пристроях при використанні graphics API 2.0 стала однією з головних причин написання свого шейдера. Перемикатися на 3.0 ми не хотіли через провал у продуктивності.
Я не хотів використовувати сферичні відображення; виглядає дивно і негарно, персонаж рухається і відображення рухаються разом з ним. Коли в грі камера дивиться в землю, варіантів з окрасою картинки мало, і відображення на поверхнях предметів відіграють дійсно важливу роль.
Взагалі PBR в Unity це історія PC ігор, на якому він відчуває себе добре при realtime освітленні. У випадку з baked освітленням і відсутністю спрямованих лайтмап, PBR стає купою сміття, яка не розуміє, як поводитися. Так, хтось може сказати «Чому не можна було просто використовувати мобільний шейдер в якому зашита cubemap?». Відповідь проста: у нас дуже багато відбиваючих поверхонь в закритих приміщеннях. Відображення не просто красиво блищать, вони підкреслюють форму і дають краще розуміння і уявлення про властивості матеріалу з якого створений предмет, вони задають правильну атмосферу того приміщення, де знаходиться персонаж. Як бонус, ви можете побачити те, що не повинні були б бачити: фасади будівель (які ми не відображаємо в основній камері, що б вони не заважали геймплею), стелі приміщень, та й просто різну фурнітуру і вивіски, які знаходяться в недоступному з цього ракурсу камери місцях. Все світиться, блищить і відбивається, створюючи цільну атмосферу і оживляючи цей цифровий світ.
Без відображення
З відображеннями
Фейкові відображення можуть дати прийнятний результат, все буде так само блищати, але гравця складно обдурити, він може не розуміти, але буде відчувати, що тут щось не так! У підсумку, вся ілюзія, до якої так довго прагнули, буде зруйнована.
При написанні власного шейдера ми, спочатку, просто скопіювали рішення юніті - розбили шейдер на 2 частини: «металік» і «спекулар». Я далеко не відразу зрозумів, що це саме та частина, де все дуже погано. Це рішення не дозволяє створювати складні об'єкти, що поєднують металеві і глянцеві поверхні. Щось одне на вибір.
У сумі, з нашою системою оптимізації, яка збирала всі текстури в кілька атласів і використовувала один шейдер для всього, картинка виглядала дивно. Все або віддавало металевими відтінками, або навпаки здавалися простим глянцем. Приблизно так, як це було в DOOM 3, коли всі поверхні нещадно блищали.
В Unity є приголомшлива можливість робити композитні шейдери, які залежно від вхідних параметрів, викидають невикористовувані блоки. Тобто. якщо в шейдері є 7 вхідних параметрів, але використовується тільки 3, то при компіляції він створить шейдр-версію в якій буде тільки 3 параметри.
Було створено 2 версії шейдера, один debug, який оперував усіма можливими текстурними картами окремо, і final, що оперує чотирма зібраними текстурними атласами.
У підсумку у нас вийшла пристойна кількість вхідних даних на debug версії шейдера:
1. Вибір між прозорістю/непрозорістю
2. відключення Normal Map (її відсутність на одному об'єкті дає невеликий приріст продуктивності, але відключення у великих кількостях дає помітний результат). У нашому випадку це має сенс, оскільки камера розташована досить далеко і не завжди розумно її використовувати, часто додавання АТ (ambient occlusion) в albedo показувало помітно кращий результат.
Далі йдуть різні текстурні карти, які я можу тут же в шейдері домножити на який-небудь колір або значення.
3. Albedo
4. Specular
5. Roughness
6. AO
7. Emission
8. Metal
9. Normal map
У процесі компіляції все це різноманіття вміщується в 4 текстури.
Що полегшує мені життя як художнику - у мене є гнучкий інструмент, що дозволяє отримати мені будь-який бажаний результат і в той же час досить легкий щодо продуктивності, особливо, якщо я використовую не всі можливості. Завдяки всьому перерахованому вище я міг дозволити собі не замислюватися про розміри текстур і, при необхідності, домножувати значення кольору на текстури, що прискорювало мою роботу, адже мені не треба було лізти в графічний редактор і зберігати все заново.
Ще одним плюсом була система обчислення розміру 3D моделі в світі, що дозволяло мені не замислюватися про розміри текстур. Не треба було мучитися і вибирати між неякісною текстурою в 64px або надміру деталізованою в 128px. Система просто вжимає текстуру до дійсно необхідного розміру і упаковує її в атлас. У підсумку, для гравця, все має однаково якісну деталізацію.
Зони vision персонажа. Туман війни
Розповім про одне з найбільш геніальних і простих рішень, які ми використовували в нашому проекті.
Туман війни реалізований через point-light, що знаходиться в персонажі і рендериться окремою камерою при максимально простому шейдері, який враховує тільки освітлення. Результатом є буфер видимості персонажа, що використовується для пост-процесу туману війни і в якості маски в інших шейдерах. Наприклад, у зоні видимості охоронців.
Ув'язнення
В результаті, ми створили потужний і гнучкий інструмент, що дозволяє нам отримувати як максимально красиву графіку на PC, так і її трішки полегшену, але все ще виглядаючу відмінно версію, на мобільних пристроях.
Є думка вивантажувати готовий рівень назад в 3D редактор і вже там запікати світло. Це дасть величезний приріст як у швидкості підготовки сцени, так і в якісному результаті. Повірте, це буде дивно. М'які і жорсткі тіні, підсвічування, якісні градієнти. Думаєте тільки FrostBite може показати всі можливості красивого рендера на межі вашого заліза? Наша команда можемо Вам показати, як повинні виглядати дійсно красиві ігри на всіх пристроях, при все тих же, смішних технічних вимогах до заліза. Але, скільки б не було ідей, на їх реалізацію потрібен час. Вихід в STEAM може дати нам можливість реалізувати всі наші амбіції, але для цього нам потрібна Ваша допомога!
детальніше з проектом можна ознайомитися тут:
steamcommunity.com/sharedfiles/filedetails/?id=752096160
Стаття за авторством Кротова Сергія, за підтримки Челушкіна Андрія і Ковалевського Віталія.















