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

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

Аннотация: В данной лекции широко рассматриваются методы расчета освещенности с помощью шейдеров, приведены примеры расчетов, формулы, фрагменты программ на языках C++ и Pascal. Также рассмотрено понятие файла эффектов и его практическое применение для обработки графики

Расчет освещенности с помощью шейдеров

Рассмотрим теперь более подробно, как осуществляется расчет освещенности грани в библиотеке Direct3D. При закраске поверхностей объекта в идеале мы должны для каждой точки грани (полигона) вычислять интенсивность освещения методами, описанными выше. Во многих графических библиотеках, в том числе и в Direct3D прибегают к приближенным методам закраски граней трехмерного объекта, состоящего из полигонов в силу того, что процесс вычисления значения интенсивности для каждого пикселя, является вычислительно трудоемким. В компьютерной графике используют, как правило, три основных способа закраски полигональной сетки: однотонная закраска, закраска, основанная на интерполяции значений интенсивности, и закраска, построенная на основе интерполяции векторов нормали. Библиотека Direct3D располагает способами использования только двух первых методов.

При однотонной закраске вычисляется один уровень интенсивности, который используется для закраски всего полигона. При использовании закраски этого типа будет проявляться эффект резкого перепада интенсивности на всех граничных ребрах объекта.

Метод закраски, который основан на интерполяции интенсивности (метод Гуро), позволяет устранить дискретность изменения интенсивности. Процесс закраски по методу Гуро осуществляется следующими шагами:

  • определяется (вычисляется или изначально задается) нормаль к каждой вершине полигона; причем для различных полигонов, имеющих общую вершину, нормали, как правило, различаются;
  • вычисляются значения интенсивности в каждой вершине полигона, используя рассмотренные выше модели освещенности;
  • полигон закрашивается путем линейной интерполяции значений интенсивностей в вершинах сначала вдоль каждого ребра, а затем и между ребрами вдоль каждой сканирующей строки

В некоторых экзотических случаях и метод закраски Гуро не позволяет полностью устранить перепады интенсивности. В этом случае можно воспользоваться закраской по методу Фонга. Алгоритмически метод Фонга схож с методом Гуро. Но в отличие от интерполяции значений интенсивности в методе Гуро, в методе Фонга используется интерполяция векторов нормали вдоль сканирующей строки. Затем проинтерполированные вектора используются в выбранной модели освещения для вычисления интенсивности каждого пикселя. Такой подход к закраске грани требует больших вычислительных затрат, но зато позволяет практически полностью избавится от проблем резкого перепада интенсивности на границах полигонов.

Как известно, на первой стадии графического конвейера происходит преобразование координат вершины с помощью трех матриц: мировой, видовой и проекционной. Затем выполняется расчет освещенности каждой вершины. Вершинный шейдер позволяет взять на себя эти шаги. В простейшем случае минимальный набор необходимых данных для расчета освещенности – есть координаты вершины в пространстве и положение источника света. В этом случае входная структура вершинного шейдера может выглядеть следующим образом:

struct VS_INPUT
{
	float4 position  : POSITION;
	float3 normal    : NORMAL;
};

Выходная информация вершинного шейдера в случае применения метода Гуро должна содержать данные о вершине и ее цвет.

struct VS_OUTPUT
{
	float4 position  : POSITION;
	float4 color     : COLOR0;
};

Кроме этого необходимо передать в шейдер следующие данные: композитную матрицу преобразования, мировую матрицу и положение источника света. Это реализовывается через таблицу констант и методы интерфейса ID3DXConstantTable. В шейдере эти данные должны быть объявлены:

float4x4 WorldViewProj;
float4x4 World;
float4   vecLight;

Главная функция вершинного шейдера должна выполнять следующие операции:

  • Преобразование координат вершины с помощью композитной матрицы (WorldViewProj) ;
  • Преобразование нормали вершины (N) с помощью мировой матрицы;
  • Определение вектора (L), направленного из вершины примитива на источник;
  • Вычисление скалярного произведения двух нормированных векторов N и L.
  • Определение освещенности вершины.

Ниже представлен вершинный шейдер, реализующий данные шаги:

VS_OUTPUT main_vs( VS_INPUT In )
{
    VS_OUTPUT Out;
    Out.position = mul( In.position, WorldViewProj );
    float3 pos = mul( In.position, World );
    float3 light = normalize(vecLight-pos);
    float3 normal = normalize(mul( In.normal, World ));
    float4 green = {0.0f, 1.0f, 0.0f, 1.0f};
    Out.color = green*dot(light, normal);
    return Out;
}

Функция mul() производит умножение вектора на матрицу, функция normalize() осуществляет нормировку вектора, функция dot() вычисляет скалярное произведение двух векторов. Как видно из представленных строк кода, для каждой вершины мы должны определять вектор на источник света и преобразовывать нормаль, как показано ниже.


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

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

struct VS_INPUT
{
	float4 position  : POSITION;
	float3 normal    : NORMAL;
};