Опубликован: 08.07.2007 | Доступ: свободный | Студентов: 1429 / 183 | Оценка: 4.43 / 4.02 | Длительность: 13:47:00
Специальности: Программист
Лекция 9:

Расчет освещенности. Файлы эффектов

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

struct VS_OUTPUT
{
	float4 position  : POSITION;
	float3 light     : TEXCOORD0;
	float3 normal    : TEXCOORD1;
};

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

VS_OUTPUT main_vs( VS_INPUT In )
{
    VS_OUTPUT Out;
    Out.position = mul( In.position, WorldViewProj );
    float3 pos = mul( In.position, World );
    Out.light = normalize(vecLight-pos);
    Out.normal = normalize(mul( In.normal, World ));
    return Out;
}

Глобальные переменные будут такими же как и в предыдущем случае.

float4x4 WorldViewProj;
float4x4 World;
float4   vecLight;

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

float4 main_ps(float3 light : TEXCOORD0, float3 normal : TEXCOORD1) : COLOR0
{
    float4 diffuse = {1.0f, 1.0f, 0.0f, 1.0f};
    return diffuse*dot(light, normal);  
}

Ниже приведены примеры закраски по методу Гуро (слева) и Фонга (справа).


Рассмотрим еще один очень распространенный эффект построения реалистичных изображений, называемый bump-mapping или микрорельефное текстурирование, причем без существенных вычислительных затрат. Идея этого подхода заключается в моделировании рельефной поверхности с помощью двух текстур. Одна из них представляет собой изображение некоторой поверхности, а другая – так называемая карта нормалей. Карта нормалей представляет собой текстуру, где каждый пиксель является вектором нормали. Можно сказать, что карта нормалей несет в себе информацию о неровностях в каждом пикселе изображения. Как известно интенсивность освещения зависит от угла между нормалью в точке и вектором на источник света (закон косинусов Ламберта).


В зависимости от вектора нормали интенсивность в каждом пикселе будет различной. Следует заметить, что вектор нормали (nx, ny, nz) и цвет (R, G, B) кодируется тройкой чисел. Но цвет кодируется тремя положительными величинами из отрезка [0, 1], тогда как компоненты вектора нормали могут принимать и отрицательные значения. Так как координаты нормализованного вектора лежат в диапазоне [-1, 1], то можно с помощью линейного преобразования отобразить отрезок [-1, 1] в отрезок [0, 1]. Для этого можно воспользоваться следующей формулой: N*0.5+0.5, где Nвектор нормали. Такой процесс кодировки проделывается для каждого компонента вектора. Обратное преобразование (отрезок [0, 1] отобразить в отрезок [-1, 1] ) может быть реализовано с помощью формулы 2*C–1, где Cзначение цвета. Например, вектор (1, 0, 1) будет преобразован в тройку чисел (1, 0.5, 1) – светло-фиолетовый цвет. Для получения карты нормалей из исходного изображения имеются специальные программы и алгоритмы, например, существует плагин к PhotoShop’у, с помощью которого можно получить карту нормалей. Ниже представлен пример текстуры и соответствующая ей карта нормалей.



Исходное изображение Карта нормалей

Таким образом, для вычисления интенсивности в каждом пикселе необходимо:

  • Получить цвет из исходной текстуры;
  • Получить закодированное значение вектора нормали из карты нормалей;
  • Произвести преобразование значений из цветового пространства в пространство нормалей;
  • Вычислить скалярное произведение нормали на вектор источника света;
  • Умножить полученное значение на цвет исходной текстуры.

Пиксельный шейдер, реализующий данные шаги показан ниже.

float4  Light;
sampler tex0;
sampler tex1;

struct PS_INPUT
{
  float2 uv0 : TEXCOORD0;
  float2 uv1 : TEXCOORD1;
  float4 color: COLOR0;
};

float4 Main (PS_INPUT input): COLOR0
{
  float4 texel0 = tex2D(tex0, input.uv0);
  float4 texel1 = 2.0f*tex2D(tex1, input.uv1) - 1.0f;
  return texel0*dot(normalize(Light), texel1);
};

Значение положения источника света передается в пиксельный шейдер через переменную Light. Ниже показаны примеры микротекстурирования при различных положениях источника света.



return texel0*dot(normalize(Light), texel1);


return input.color*texel0*dot(normalize(Light), texel1);