четверг, 16 февраля 2012 г.

Motion blur, или смазывание при движении. Получается автоматом в кино из-за большой выдержки плёнки, отлично имитируется пакетами трехмерной графики и обработки видео, но так сложна и заковыриста для реализации в играх.

При разработке игры blast-off, а точнее её прототипа, возникла необходимость использовать этот эффект. Да, я сказал "необходимость". Необходимость потому, что при больших скоростях задний план начинал настолько жутко мерцать (от быстрого движения), что глаза моментально уставали и игру хотелось закрыть.

Перед тем, как прийти к финальному на тот момент варианту было испробовано несколько вариантов, как теоретически, так и практически. О них дальше:

Давайте рассмотрим всё на примерах. Назовите мне игры с честным motion blur. Ну же, смелее. Дело в том, что их нет. Ну или почти нет. В любом случае честный motion blur будет слишком труднозатратен или будет нечестным. В чём же проблемы и почему его не делают честным? Каковы принципы смазывания.

Первое, что приходит в голову, это складывать несколько кадров воедино, рендеря картинку со смещением. Первая идея, зачастую, еще проще. Брать последние 3-5 кадров и с частичной прозрачностью накладывать друг на друга. Подход даёт самый честный motion blur, однако имеет огромнейший недостаток. Скорость.
Взять предыдущие кадры мы не можем, любой кто пробовал, поймёт почему подход провален: те кадры уже были, мы получим не смазывание при движении, а шлейф, к тому же создающий ощущение заторможенности происходящего. А это идет в разрез с тем, что хотим получить мы, а именно смазывание при движении. Результат будет выглядеть как-то так:


Печальное зрелище. При 60 кадрах мы получаем шлейф в почти 1\10 секунды. При некоторой доработке метода, мы можем решить, что можно сразу отрендерить эти 3-5 кадра, с учетом движения объектов между кадрами и получить честное смазывание и отсутствие шлейфа. И это будет правда. Но результатом такой реализации на практике станет падение кадросекунд в соответствующие 3-5 раз. В Blast-off мы не могли так жертвовать скоростью, поскольку нетбуки не давали при нашем уровне графики больше 50-60кадров, а слабенькие ноутбуки более 170 кадров в секунду. Полагаться на мощность компьютеров, выдающих более 1000-1500 кадров в секунду не хотелось, так как играть в подобные игры люди любят и, украдкой, в рабочее время на работе. На рабочих лошадках при этом ничего лучше интегрированного виде от Intel не стоит.

Мы пошли по пути наименьшего сопротивления. Решив, что самым смазываемым эффектом является, фактически, только фон, было решено смазать для начала только фон. Сделать это совсем несложно постпроцессом. Выглядит код шейдера так:

  float4 Output; 
  for (int i = 1; i < NumSamples; ++i)
  {
    float4 currentColor = tex2D(DiffuseMap, texcoord);
    texcoord -= Vec;
    Output += currentColor;
  }
  Return Output / NumSamples;

Где Vec - вектор движения фона, а NumSamples это количество псевдокадров для смазывания (в нашем случае 13. Эффект получается совсем не прожорливый и выглядит так:
Проблемой такого подхода являются динамические объекты, двигающиеся отдельно от фона. Если их поместить в тот же конвеер, что и фон, они смажутся, если же рисовать поверх, то будут слишком четкими. Тут тоже можно считерить. Отрендерив всю сцену дважы в два разных буфера, в рендертаргет как есть и во второй рендертаргет только альфой, закодировав цветом смещение. В красный смещение по Х, в зеленый по У (это называется DuDv map и о ней я расскажу в дальнейших постах). Результатом (для двигающихся объектов) будет такая картинка:

Круто? а не тут-то было. При такой скорости движения шарик должен был бы смазаться так:
Шарик А с предыдущего кадра. Контур у него четкий. А вот шарик В это то, что должно было получиться. Более длинное изображение, прозрачное по краям, без четких контуров.

Выводом из всех проведенных экспериментов можно сделать следующее. Постпроцессинг не может дать качественного и правильного motion blur. В то же время честные методы не могут дать приличной скорости работы. Возможно, что комбинирование этих двух методов поможет. Тоесть 2 картинки - прошлый и текущий кадр (практически не съест скорости в меру того, что предыдущий кадр всеравно уже отрендерен), сгенерировать DuDv карту смещений для объектов и уже на основе этих трех исходных изображений генерировать более честный эффект смазывания.

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

Комментариев нет:

Отправить комментарий