Программирование игр, создание игрового движка, OpenGL, DirectX, физика, форум
GameDev.ru / Программирование / Статьи / Coverage Buffer из CryENGINE в деталях

Coverage Buffer из CryENGINE в деталях

Автор:

Некоторое время назад я наткнулся в замечательной презентации Secrets of CryEGNINE 3 Graphics Technology от не менее замечательных Nickolay Kasyan, Nicolas Schulz и Tiago Sousa на описание любопытной технологии, названной авторами
Coverage Buffer. (с самой презентацией можно ознакомиться тут http://www.slideshare.net/TiagoAlexSousa/secrets-of-cryengine-3-g… cs-technology)
Технология представлена как основной метод окклюжен куллинга, активно используемый начиная с Crysis 2. Так как никакого внятного пейпера по данной технологии Crytek не предоставили, а разобраться
было интересно, то пришлось плясать с тем, что имеем.


Что?

Итак, идея, по словам авторов презентации, заключается в следующем.

  • Получить depth-buffer предыдущего кадра
  • Сделать репроекцию в текущий кадр
  • Софтварно растеризуя BBox'ы объектов, проверять, видны ли они камере - и рисовать/не рисовать

Метод в принципе не содержит никаких революционных идей, отличие от основного конкурента - Software Occlusion Culling - заключается в том в том методе
нам необходимо строго поделить объекты на категории Occluder/Occludee, что не всегда возможно сделать.

пикч | Coverage Buffer из CryENGINE в деталях

Итак, еще раз кратко резюмируя, каковы плюсы этого метода?

  • + не нужно разделять объекты на occluder/occludee
  • + использование уже готового depth buffer

ну и очевидные минусы:
  • - ошибка связанная с отставанием на 1 кадр (репроекция полностью не решает данную проблему)
  • - оверхед при полной видимости объектов, связанный с манипуляциями с depth buffer (downscale, lock и т.д.)


Где?

Так уж получилось, что я гуглил в сторону Occlusion Culling не просто так, на проекте необходимо было решить довольно важную задачу, связанную с тормозами, возникающими
из-за огромного лесного массива. Речь идёт, конечно же, о горячо любимом мною Life is Feudal. (Все скрины ниже будут оттуда)

Итак, вкратце суть изначальной проблемы.

  • Есть остров, густо засаженный деревьями. Рисуется всё это действо на движке Torque3D.

  • Из оптимизаций, изначально встроенных в движок, есть неплохая система батчинга/биллбордов для дальних деревьев и инстансинга для тех, которые поближе.
    Однако, решение "рисовать/не рисовать" принимается по дефолту исключительно в результате frustum culling'а (т.е. мы смотрим, попадает ли данное дерево/батч в пирамиду,
    и, если попадает, то рисовать).

    Однако, такой подход не вполне оправдывает себя в случае действительно больших лесных массивов с десятками тысяч деревьев. В этом случае попадают во фрустум (и как следствие, рисуются)
    те деревья, которые находятся за стеной, горой и другими деревьями. Из-за этого бюджет кадра терпит катастрофические потери:  даже смотря сквозь стену в направлении центра острова,
    рисование невидимых батчей и деревьев занимало порядко 20-30мс, что, в общем-то, вплотную приближается к рассчетному бюджету кадра (33мс).

    Как результат, игроки получали драматическое падение ФПС просто глядя в направлении центра острова.

    gbr | Coverage Buffer из CryENGINE в деталяхmapsmall | Coverage Buffer из CryENGINE в деталях

    Для решения данной задачи было решено воспользоваться технологией Coverage Buffer. Если бы я писал, например, диплом, то тут бы стоило сделать обзор всех остальных методов OC, обозначить плюсы и минусы,
    и обосновать, почему был выбран именно этот.
    К счастью, это не диплом, потому ограничусь одним предложением:
    После чтения многочисленных пейперов и статей, переписки и бесед с более мудрыми и опытными коллегами было решено опробовать этот метод, тем более, что в бою он уже
    себя показал.

    Осталось лишь посмотреть, как он покажет себя в нашей ситуации.


    Когда? Как?

    Теперь перейдём к техническим деталям и, собственно, коду.

    Первая задача, которая встала - это получить depth buffer. Torque3D, который на момент написания статьи поддерживал DirectX только 9й версии, накладывал в этом
    свете определённые ограничения.

    Как получить Depth Buffer в DirectX 9?

    Ответ нашёлся на сайте Араса Пранцкевичуса (я не уверен, что правильно транслитерировал его фамилию), это главный render-guy известного движка Unity.
    http://aras-p.info/texts/D3D9GPUHacks.html
    Оказалось, что depth buffer получить в directX 9 все таки можно, но для этого нужно использовать специальный формат INTZ. Согласно официальной документации от AMD и NVidia, все видеокарты,
    выпущенные начиная с 2008го года, поддерживают данный формат (для более ранних есть RAWZ), так что можно без особых опасений пользоваться
    данным хаком.
    Ссылки на документацию: AMD и NVidia

    Код использования тривиален, взят из одного из пейперов вверху, приведу его тут чтобы, так сказать, все яйца были в одной корзине.

    + INTZ

    Дальнейшая подготовка Depth buffer'а

    • downscale до низкого разрешения (было выбрано 256х128)
    • lock + memcpy
    • reprojection

    Всё тут достаточно тривиально, downscale делается с маской max (берется максимальное расстояние, ближе к камере, дабы
    не закрыть чего лишнего), репроекция делается путём применения обратной матрицы от предыдущего кадра и новой - от текущего.
    Возникшие пробелы "замазываются" maxValue, дабы не закрыть чего лишнего.

    Итак, теперь есть depth buffer. Теперь дело за малым - софтварная растеризация ббоксов

    Софтварная растеризация

    Данную тему никак нельзя назвать нехоженной тропой, уже довольно много мусолили на разные лады. Однако внятной инструкции к
    реализации найти не так-то просто. Самый полезный материал, который я нашёл по сабжу, был тут:
    https://software.intel.com/en-us/blogs/2013/09/06/software-occlus… ling-update-2
    Это крайне полезная интеловская демка по conventional occlusion culling, которую всем рекомендую - внутри много полезного.

    Первая версия функции для софтварной растеризации была реализована в, так сказать, plain c++.
    Она работала, но довольно медленно. Спасибо комраду bazhenovc, подсказал переписать на SSE.
    Я по молодости с SSE еще не работал, но с божьей и интеловской помощью переписал на SSE, стало работать в 2-2.5 раза быстрее. Вот она, магия
    SIMD. Делюсь кодом безвозмездно, юзайте на здоровье =)
    Если вдруг кто заметит какие недочёты или возможности для оптимизации, очень прошу сообщить!

    + код

    Собственно, всё, технология Coverage Buffer в общих чертах готова.

    В процессе имплементации, правда, вскрылся еще ряд других сложностей и багов, связанных с рендером леса, на борьбу с которыми ушло в 20 раз больше времени,
    чем на сам C-Buffer, но борьба с ними не входит в тему данного поста =)

    Результаты

    Использование технологии C-Buffer для интеллектуального OC для рендеринга леса
    позволило уменьшить время рендера кадра на открытых пространствах от 5 до 20мс, а
    в закрытых - до 30мс (в случаях, когда отсекается вся растительность).
    Однако дало оверхед в 1.5-2мс при неэффективном отсечении (т.е. технология использована,
    но ничего не отсеклось).

    pic3 | Coverage Buffer из CryENGINE в деталях

    Резальты можно будет наблюдать в проекте после внимательного тестирования, в том
    числе и на совместимость с архаичными машинами. Думаю, через пару недель.

    21 октября 2015

    #coverage buffer, #CryEngine, #occlusion culling

    2001—2017 © GameDev.ru — Разработка игр