Publish @Zeux: GDC Report – A Survivor Reborn: Tomb Raider on DX11 – Jason Lacroix (Crystal Dynamics)

Итак, скандалы, интриги, расследования и как оно на самом деле – @Zeux транслировал с доклада про волосы Лары Крофт.


People in this conversation:
Arseny Kapoulkine (Zeux) Add
CREAT Studios, Saber3D, Sperasoft (EA Sports – FIFA). Currently making kids happy at ROBLOX. http://zeuxcg.org/
Sergey Gonchar Add
Graphics Programmer (Flash Stage3D, interested in Direct3D11)
Yury Degtyarev Add
Graphics programmer at Transas New Technologies

Zeux: A Survivor Reborn: Tomb Raider on DX11 – Jason Lacroix (Crystal Dynamics)
Поехали дальше! Доклад про те самые волосы которые с таким чувством обсуждали давеча на DTF.
Сначала автор очень вкратце рассказал про то что они там делали для DX11 из мелочей, я ничего интересного особо не отметил – так что все пропустим, сразу про волосы!
Волосы делались в сотрудничестве с AMD и с тем самым конфетти – как я понял, сначала людям из Crystal Dynamics выдали AMD/конфетти демку про волосы, а потом они на ее основе начали собственно писать код в продакшен.

Значит, есть Лара, ей нужна какая-то важная визуальная фича.
Почему-то в качестве важной визуальной фичи выбраны волосы.
Волосы нужно хорошо симулировать физически, и красиво рендерить.
Стандартные референсы – ATi Ruby, NVidia Nalu, etc. – плюс недавно вышедшая Alice: Madness Returns
Говорят, в Алисе волосы очень черные, что хорошо скрывает артефакты блендинга, Ларе такие не пойдут…

Значит, начинаем с физической симуляции и представления для рендера.
Из трейлера Лары, который делали в movie отделении Square Enix (компания одна и та же, так что можно было взять ассеты), были взяты волосы, сделанные в каком-то пакете для Maya
Волосы представляли собой набор guide кривых – если не ошибаюсь, порядка 7к
Поскольку эти кривые хотели использовать прямо в качестве рендер геометрии, то надо было чтобы эти кривые покрывали всю голову без дырок
Поэтому большинство кривых размножили, слегка сдвинув результирующие.
Получилось порядка 20к кривых, каждая кривая состояла из 16 (15?) сегментов

Кривые были разбиты на 4 группы (шапка, хвост, и еще какие-то 2), для каждой группы были слегка свои параметры симуляции
Был физический солвер из двух кусков, один задавал global pose constraints, второй задавал local constraints, я в физике симуляции волос не разбираюсь так что деталей не знаю!
Были коллизии только с телом героини
Которое было представлено набором капсул.
Вся симуляция была на GPU, в CS, в несколько этапов:

Сначала один CS который применяет global constraints, потом второй CS, etc., последний – делает collision response с теми капсулами.
Примеры параметров симуляции например – мокрые волосы или нет
Это влияло на то, насколько сильно гасится движение волос, и могло применяться по-разному к разным частям – например, хвост мокрый, а шапка волос нет.
С симуляцией волос была куча проблем
Были нестабильности (система взрывалась), некорректные collision capsules с предыдущей версии ассета, неполные collision capsules, приводящие к тому что волосы могли проникнуть сквозь тело, итп

Были хаки под cinematics в игре -
В одном из синематиков Лара висит вниз головой
Из-за особенностей симуляции после применения constraints к хвосту он никак не хотел виснуть вниз
Поэтому в синематике ассет волос подменялся на другой ассет
Наверное с другими параметрами or something

люс был синематик в котором были очень резкие движения
Для него пришлось писать дополнительный CS который бы это обрабатывал отдельно…
В общем симуляцию тюнили до +2 недель после релиза (патч…)
Плюс симуляция (как и рендер) не учитывала того факта что волосы вообще-то у героини должны быть в грязи, в крови итп

Так, про симуляцию у меня все, давайте про рендер.
Вот есть значит эти 20 тысяч кривых, по 16 сегментов в каждой.
Думали сначала рисовать линиями, но решили рисовать квадами
Каждый квад в vertex shader вытягивается по толщине волоса (задается в maya)
Плюс гарантируется что он будет размером больше пикселя
В пиксельный шейдер передаются координаты левого и правого ребра волоса, в нем считается coverage, который выводится в альфу
Техника примерно такая же как и освещенная Humus-ом техника анти-алиасинга на тонких проводах (http://www.humus.name/index.php?ID=355)

В шейдере считается лайтинг со спекуляром по Kajiya-Kay (пробовали технику Marschner из Agni Philosophy, но не успели дотюнить к релизу)
И еще кастомная настройка в зависимости от того, часть волос мокрая или сухая.
Если Лара находится в темном углу, в котором вообще нет света, то волосы полностью черные – чтобы это компенсировать, был для волос добавлен beauty light, который тем ярче чем темнее остальное освещение на волосах
И светит из точки, которая выше чем камера, по направлению на персонажа.
Это дает небольшую подсветку в темных углах на волосы, выделяя персонажа.

Итак, наконец завершающая нота
Волосы пошейдили, а как блендить-то будем?!?
Пиксельный шейдер, напоминаю, выдает корректную альфу в зависимости от coverage, но треугольников много, они не отсортированные
Ответ простой – используем Order-Independent Transparency!
По методике от AMD со связным списком фрагментов в каждом пикселе.
Общее число фрагментов было взято в константное число раз больше чем кол-во пикселей
У меня не записано, мне кажется в 12*8 но могу ошибаться.
При рендере волос значит мы добавляем в тот связный список в текущем пикселе пару (цвет с альфой, глубина)
Дальше full-screen quad для блендинга (кажется, помеченный стенсилем чтобы compositing делать только на пикселях с волосами)
По хорошему надо отсортировать все фрагменты
И сблендить
Это потенциально дорого, поэтому выбираем до 256 фрагментов в пикселе, выбираем из них максимальные 8 по глубине, их блендим в порядке сравнения глубины
Остальное блендим как получится “под” верхние 8
Эта текстурка с нодами под связный список в 1080p занимала 200 мегабайт.
Целиком волосы на какой-то разумно топовой карточке занимали по скорости в среднем 5.3 ms в игре и 8.7 ms в игре при камере для стрельбы (это симуляция + рендер)
Вот.
В качестве ключевых оптимизаций была названа упаковка атрибутов в VS output и еще какой-то ерунды по мелочи.

Yury Degtyarev: блин, сейчас это кажется чем-то невероятным, 200мб на волосню)
через пару лет на GeForce 9000 наверно будет обычным делом

Sergey Gonchar: Конечно эти волосы выглядят шикарно, но на моей GTX 670M фпс с включенными волосами падал до 10 именно в синематике, вот только теперь стало понятно почему, если учесть что синематиков в ларе очень много, то это жутко бесило. Пришлось потом отключать.

Zeux: Так, забыл простой трюк про самозатенение волос.
Тени рисуются в shadow map как обычно, но шейдер на волосах делает чуть простой магии, чтобы получить фейковое самозатенение.
Глубина в shadow map сравнивается с light-space глубиной текущего пикселя, а дальше если разница не слишком велика, то разница делится на толщину одного слоя волос (получается условно кол-во слоев между текущим пикселем и ближайшим к источнику), и из этого количества слоев получается по любой простой формуле (не помню что конкретно, может линейно, может степень какая-нибудь) фактор самозатенения.

This happened on #gamedeff

  • Arseny Kapoulkine

    Написанному верить!

  • Anon

    Тест

  • glebedev

    Летз Дискас!

  • Denis Ovod

    Саймон респект за комменты!