Страницы

вторник, 9 августа 2011 г.

Advanced Ambient Lighting

Амбиентному освещению традиционно уделяется наименьшее внимание в начале изучения программирования графики и шейдеров. Большинство примеров подставляют константное значение амбиентного света в результирующую формулу освещения. Тем не менее, в современных играх амбиентное освещение играет очень важную роль в формировании финальной картинки. С увеличением мощности железа появляются новые техники для эмуляции рассеянного освещения, такие как Global Illumination, Ambient Occlusion, Radiance Transfer, Indirect Lighting.
В этом примере будут рассмотрены несколько техник амбиентного освещения и будет показано, как они влияют на результат финальной картинки.
Вышеописанный случай константного амбиента, формула L = Ld + La, где L - результат вычисления, Ld - диффузная составляющая, La - амбиентная составляющая.
При детальном рассмотрении картинки заметно, что карта нормалей совсем потерялась на неосвещенных участках - они выглядят плоскими.
Чтобы сделать рельеф нормалей заметным, подсветим его дополнительным источником света, направленным сверху. В шейдере это выглядит так:
// vertex shader
OUT.SkyVector = mul(float3(0, 1, 0), TBN);

// pixel shader
float ambientPart = 0.5 + 0.5 * dot(IN.SkyVector, BumpNormals);


т.е. мы передаем из вершинного шейдера в пиксельный направление на вектор {0,1,0} - SkyVector в пространстве TBN. В пиксельном шейдере вычисляем результат амбиента как обычный источник света и смещаем результат в диапазон [0, 1]. Дальше делаем с результатом все то же самое, что и с обычной константой амбиента. Результат:

Этот результат можно еще улучшить. Представим себе дневное освещение в реальном мире. В солнечную погоду часть солнечных лучей преломляется в атмосфере и дает голубоватый оттенок. В пасмурную погоду оттенок ближе к серому. При закатном солнце - розово-оранжевый.
Более того, снизу объекты подсвечиваются светом, отраженным от поверхности, на которой они находятся. Если это асфальт или земля - оттенок темно-серый. Если это трава - зеленый. Вода - синий.
Используем это в шейдере. Передадим туда значения "верхнего" и "нижнего" освещения. Используем уже имеющийся результат для интерполяции между ними:
float3 ComputeAmbientColor(float3 skyVector, float3 normalVector)
{
float factor = 0.5 + 0.5 * dot(skyVector, normalVector);
return lerp(g_AmbientBottom.xyz, g_AmbientTop.xyz, factor);
}


Результат (только ambient):

Результат (diffuse + ambient):

Результат (с текстурой):




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