Программирование игр, создание игрового движка, OpenGL, DirectX, физика, форум
GameDev.ru / Программирование / Статьи / GPSM: Geometry Pitch Shadow Map

GPSM: Geometry Pitch Shadow Map

Автор:

GPSM (Geometry Pitch Shadow Map) — еще один метод рендеринга теневых карт, дающий возможность реализовать тени, покрывающие огромные территории, используя всего одну текстуру, в которую происходит рендеринг (render target). Метод GPSM даёт для теней вдали от камеры наблюдения качество хуже, а вблизи — лучше. Этот метод я изобрел для своего движка и уже использую в двух проектах, решил поделится с вами, надеюсь, кому-то пригодится.

Вступление
Теория
Практика

Вступление

Привет всем обитателям GameDev.ru!

Возможно, всем вам известны мучения выбора метода рендеринга теней для своего проекта или движка. Такой выбор я для себя когда-то сделал, и это были PCF + Cascade.

Свет, конечно же, для OutDoor, т.е. directional

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

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

Сразу бросились в глаза, еле оторвал PSM LiPSM XPSM TSM... времени было мало, со всеми по-быстрому ознакомился, погонял демки и понял, что даже если использовать подобный метод, то придется делать с нуля, чтобы обойти проблемы увиденные в этих методах.

Убив три дня на настройку своего TSM, я был у цели, но у того подобия, что я реализовал было несколько минусов, которые в конце пути я обвешал костылями, которые не спасли ситуацию... и за 5 минут «начудил» GPSM. Потестировал, дал пафосное название, и взялся за написание статьи, возможно найдутся люди, заинтересовавшийся в использовании и развитии метода.

Теория

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

FishEye | GPSM: Geometry Pitch Shadow Map

И на самом деле, это очень плохая идея, так как если есть более или менее большая плоскость, у которой только 4 точки, ее никак не округлить, в результате тень от других предметов на таком полигоне будет искаженная. Чтобы убрать округленность пришел к выводу, что надо сокращать X и Y отдельно и в некоторых случаях оно даже прямоугольник будет обрабатывать весьма хорошо.

При таком методе теневая карта будет примерно такой (видно что картинка даже не полностью заполнена, и там влезло бы еще больше/дальше):

DstTrans | GPSM: Geometry Pitch Shadow Map

без такого искажения, все что далеко — срезалось бы границами текстуры.

dsttrans+ | GPSM: Geometry Pitch Shadow Map

Здесь примерно линиями показано, как искажается геометрия по мере отдаления.

Тени от неё такие (почти PCF 16 выборок для гладкости)

DstTransResult | GPSM: Geometry Pitch Shadow Map

Важно, чтобы центр теневой карты был в точке камеры, и вид из источника должен быть ортогональным.

Практика

Если у вас реализованы простые карты теней (shadow maps), то в шейдере глубины вам надо будет добавить 2 строчки:

OUT.position = mul(IN.position,matAll); //matAll - матрица ортогонального вида с источника.
OUT.depth = OUT.position.z;
///////////////////////////////////
  OUT.position.x=OUT.position.x/(abs(OUT.position.x)+1.0f); //вот и строчки. единица отвечает за степень искажения для меня подходит 1.0f, вы можете поискать другой подходящий коэффициент
  OUT.position.y=OUT.position.y/(abs(OUT.position.y)+1.0f);
///////////////////////////////////
return OUT;

В шейдере наложения теней надо сделать то же самое с текстурными координатами:

float4 posL=mul(mul(IN.position,matWorld),matLight);
///////////////////////////////////
  posL.x=posL.x/(abs(posL.x)+1.0f); // место "1.0f" должен быть тот же коэффициент что и в шейдере глубины.
  posL.y=posL.y/(abs(posL.y)+1.0f);
///////////////////////////////////
OUT.SMCD.xyz=posL.xyw; //SMCD - текстурные координаты наложения теневой карты на сцену.
OUT.SMCD.x*=0.5f;
OUT.SMCD.y=-OUT.SMCD.y*0.5f;
OUT.SMCD.w=posL.z-0.0007f; //базис...

Вот и все «чудеса» искажения геометрии...

Чтобы избавится от сильного дрожания теней, над координатами камеры от источника света надо «пошаманить»:

camx=int(ViewPos.x)/5*5;
camy=int(ViewPos.y)/5*5;
camz=int(ViewPos.z)/5*5;

На что делить и умножать — надо смотреть относительно масштаба вашей сцены и скорости передвижения камеры.

плюсы:
+ быстро реализуется и настраивается.
+ не нужно передавать в шейдер дополнительных матриц.
+ нет дрожания теней при поворотах камеры (дрожание есть в всех PSM/TSM)
+ нет проблем, если источник поворачивать по всем осям. Дальность и качество остается почти неизменным.

минусы:
- метод новый и пока что я его не использовал в проектах, и, возможно, не знаю о дополнительных минусах. Интуиция подсказывает мне, что я буду теперь использовать только этот метод теней.
- иногда тень может шевелиться и некорректно наползать куда не стоит. Немного лечится увеличением базиса, но в некоторых случаях все же непоправимо.
- для повышения качества теней вблизи надо увеличивать саму теневую карту. Но лично для меня одной R32 1024*1024 достаточно по скорости и качеству.
- очевидно, что на теневой карте рендерится и то, что мы не видим (за спиной, например), но это как раз спасает от динамичного изменения качества и от дрожания теней.
- искажает тень на полигонах с большой площадью, поскольку он будет трансформироваться по разному в каждой четверти экрана. Но учтивая, что в современных играх уже редко где встречается большой полигон на пол экрана, то таких искажений фактически не будет. Даже если такой полигон вам крайне нужен, то можно его тесселизировать, и он тоже перестанет быть проблемой.

Недавно было обнаружено видео (Che@ter, спасибо!): http://www.youtube.com/watch?v=Pc2jhlyOh_U, и пдф к нему: http://www.cspaul.com/publications/Rosen.2012.I3D.pdf.

Видно, что все минуса моего метода здесь убраны за счет тесселяции. «Rendering time for backward analysis RTW using a tessellation shader for triangle subdivision». Поэтому проблем с большими плоскостями нет, так же как и с неточностями геометрии.

Если будут вопросы, всегда отвечу в комментариях.

30 апреля 2012

#GPSM, #ShadowMap, #тени

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