Главная Новости Статьи Книги Обзоры Проекты Обо мне
Поиск:

меню
главная
новости
статьи
книги
обзоры
проекты
обо мне
 

 
Parallel-Split Shadow Maps

    Сплиты и матрицы

 Вот как будут построены наши матрицы вида и проэкции для каждого сплита.

К примеру обсудим как будут построены матрицы для первого сплита. Тут я изобрел свой велосипед, Вы можете содрать его, либо воспользоватся методами из других примеров.
Суть такая : Сперва мы строим матрицу вида света. Она всегда смотрит в центр сплита, и всегда смещена от него на некоторое расстояние в направлении света. Строится она одинаково для всех сплитов :

float dist=150; //расстояние до источника
//Сперва создадим матрицу вида
D3DXVECTOR3 DirL = D3DXVECTOR3(LightDir.x,LightDir.y,LightDir.z);//направление света
D3DXVECTOR3 TarG = D3DXVECTOR3(f.center.x,f.center.y,f.center.z);//центр сплита
D3DXVECTOR3 LightPos = TarG+DirL*dist;
D3DXVECTOR3 Lighttarget = TarG;
D3DXVECTOR3 Lightup(0.0f, 1.0f, 0.0f);
D3DXMatrixLookAtLH(&View, &LightPos, &Lighttarget, &Lightup);

Расстояние до источника равно 150. Я выбрал это значение потому-что оно подходит до моего примера. У меня масштаб 1 – это 1 метр. Обьектов выше 150 метров у меня не будет. И даже выше 50, наверняка не будет.

Теперь нужно построить матрицу проекции. Для этого мы спроецируем все точки пирамиды фруструма на видовую матрицу и найдем екстремумы(максимумы и минимумы значений)

float minX=0;
float minY=0;
float minZ=0;
float maxX=0;
float maxY=0;
float maxZ=0;

D3DXVECTOR4 trans0;
D3DXVECTOR4 transform0(f.point[0].x,f.point[0].y,f.point[0].z,1);
D3DXVec4Transform(&trans0,&transform0,&View); //View – матрица света текущего фрустума

minX=trans0.x;maxX=trans0.x;
minY=trans0.y;maxY=trans0.y;
maxZ=trans0.z;

for (int i=0; i<8; i++) //По всем восьми точкам пирамиды фрустума
{
D3DXVECTOR4 trans;
D3DXVECTOR4 transform(f.point[i].x,f.point[i].y,f.point[i].z,1);
D3DXVec4Transform(&trans,&transform,&View); //View – матрица света текущего фрустума

if (minX>trans.x) minX=trans.x;
if (maxX<trans.x) maxX=trans.x;
if (minY>trans.y) minY=trans.y;
if (maxY<trans.y) maxY=trans.y;
if (maxZ<trans.z) maxZ=trans.z;
}

minZ=0.1; //минимальная глубина (возможно лучше увеличить вплоть до 1)

float W = (abs(maxX-minX)); //значения в два раза больше нужного
float H = (abs(maxY-minY));

D3DXMatrixOrthoLH( &Proj, W,H, minZ, maxZ);

Вот и все. Поясню. Мы спроецировали точки пирамиды на плоскость света, которая удалена от центра фрустума на расстояние dist. От «центра» этой плоскости я нахожу минимальные и максимальные значения по X,Y чтобы понять – какого размера делать проекционную матрицу. Делаю ее в два раза больше – чтобы не было диких артефактов.
В моем примере W,H для первого сплита равны – 70*70, для второго 250*250, третьего 3000*3000. Многовасто? Ага )) Вот и так думаю (( Наверняка третий сплит не нужен… И так уйдет в туман. Ну это даже хорошо… Уже разобрался, попозже опишу как.
Т.к. в игре есть туман скрывающий дальность – так вот туман скрывает пиксели начиная уже с 500 – а т.к. тени в тумане быть не может – то мы грубо ограничим максимальную плоскость в 500.0f.
updateSplitDist(f, 0.25f, 500.0f);
Теперь поменяем значение лямбды - float split_weight=0.7f;
И небольшой фейк в расчете матриц – когда создаем матрицу вида для третьего сплита – делаем его центр – в x,z центра фрустума, а y = y камеры. Т.е. небольшой фейк. И можно немного дистанцию увеличить – до 200. minZ=10.0; - для более щадящей проекционной матрицы/ В итоге – все супер.

В итоге получаем вкусные сплиты.
Если у Вас возникают артефакты по краям сплита при определенном взгляде камерой, покрутите параметр при задавании f.fov. Т.е. по умолчанию мы задавали как :
f[0].fov   = (float)D3DX_PI / 4 ;
f[1].fov   = (float)D3DX_PI / 4 ;
f[2].fov   = (float)D3DX_PI / 4 ;
Просто прибавьте число к fov. Я добавил 0.4 – это расширило поле зрение с 45 градусов до 67. Что в свою очередь изменило точки фрустума, и расширило проекцию тени. Этот параметр подбираем по своей сцене.

Shadow Map Flickering (Дрожание теней)
Итак почти все готово, но если у вас проблемма фликеринга – дрожания теней, то возможное решение показано дальше. Почему же дрожат тени? Все это потому что матрица света (видовая и проекционная) перестраиваются каждый кадр. Значения флоат плавают, а в видеокарте появляются так называемые sub-texel неточности.
Как этого избежать? Достаточно создать матрицу коррекции и умножить на нее световую матрицу.
Вот так она создается :

//Shadow Map Flickering - дрожание суб-текселей
D3DXVECTOR3 ptOriginShadow(0,0,0);
D3DXVec3TransformCoord(&ptOriginShadow, &ptOriginShadow, &matLVP);

// Находим тексель. 0.5f т.к. x,y в диапазоне от -1 до 1
// а нам нужно от 0..1
float texCoordX = ptOriginShadow.x * SM_SIZE * 0.5f;
float texCoordY = ptOriginShadow.y * SM_SIZE * 0.5f;

// округляем тексели
float texCoordRoundedX = floor(texCoordX);
float texCoordRoundedY = floor(texCoordY);

// Разность между округленными значениями и обычными нужны для сдвига теневой
// матрицы чтобы избежать суб-тексельного сдвига
float dx = texCoordRoundedX - texCoordX;
float dy = texCoordRoundedY - texCoordY;

// Переводим dx, dy в гомогенные координаты
dx /= SM_SIZE * 0.5f;
dy /= SM_SIZE * 0.5f;

D3DXMATRIX xRounding;
D3DXMatrixTranslation(&xRounding, dx, dy, 0);

D3DXMatrixMultiply(&matLVP,&matLVP,&xRounding);

<< Назад

 

 

 

Copyright CSS PSV 2009 Kherson
Hosted by uCoz