Разработка приложения для визуализации трехмерных iен с использованием карт освещения и динамического освещения

Дипломная работа - Компьютеры, программирование

Другие дипломы по предмету Компьютеры, программирование



т следующим образом:

const mat4 OrthoProjection (float left, float right,bottom, float top,zNear, float zFar)

{float tx = - (right + left) / (right - left),= - (top + bottom) / (top - bottom),= - (zFar + zNear) / (zFar - zNear);

mat4 (2 / (right - left), 0, 0, tx,

, 2 / (top - bottom), 0, ty,

, 0, -2 / (zFar - zNear), tz,

0, 0, 0, 1);

}

Теневая карта использует буфер глубины (depth buffer), для того, чтобы определить, находится ли пиксель в прямой видимости источника освещения, либо он чем-то загорожен.

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

Для того, чтобы получить буфер глубины, нам необходимо:

Создать текстуру для хранения буфера глубины

Создать Framebuffer Object (FBO) и привязать к нему текстуру

Настроить камеру и выполнить рендер iены в созданный FBO

Начнем по порядку, для начала создадим текстуру для хранения буфера глубины, делается это следующим образом:

// функция создания текстуры для хранения буфера глубины

GLuint TextureCreateDepth (GLsizei width, GLsizei height)

{texture;

// запросим у OpenGL свободный индекс текстуры(1, &texture);

// сделаем текстуру активной(GL_TEXTURE_2D, texture);

// установим параметры фильтрации текстуры - линейная фильтрация

glTexParameteri (GL_TEXTURE_2D,_TEXTURE_MIN_FILTER, GL_LINEAR);(GL_TEXTURE_2D,_TEXTURE_MAG_FILTER, GL_LINEAR);

// установим параметры оборачиваниея текстуры - отсутствие оборачивания

glTexParameteri (GL_TEXTURE_2D,_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);(GL_TEXTURE_2D,_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

// необходимо для использования depth-текстуры как shadow map

glTexParameteri (GL_TEXTURE_2D,_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);

// соаздем пустую текстуру под depth-данные

glTexImage2D (GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, width, height, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);

// проверим на наличие ошибок_CHECK_FOR_ERRORS();texture;

}

// создадим текстуру для хранения буфера глубины

GLuint depthTexture = TextureCreateDepth (DEPTH_TEXTURE_WIDTH, DEPTH_TEXTURE_HEIGHT);

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

Далее нам необходимо создать и настроить FBO, рендер iены мы будем осуществлять с его использованием, создание FBO выглядит следующим образом:

// Framebuffer Object (FBO) для рендера в него буфера глубиныdepthFBO = 0;

// переменная для получения состояния FBOfboStatus;

// создаем FBO для рендера глубины в текстуру(1, &depthFBO);

// делаем созданный FBO текущим(GL_FRAMEBUFFER, depthFBO);

// отключаем вывод цвета в текущий FBO

glDrawBuffer (GL_NONE);(GL_NONE);

// указываем для текущего FBO текстуру, куда следует производить рендер глубины

glFramebufferTexture (GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, depthTexture, 0);

// проверим текущий FBO на корректность((fboStatus = glCheckFramebufferStatus (GL_FRAMEBUFFER))!= GL_FRAMEBUFFER_COMPLETE)

{_ERROR (glCheckFramebufferStatus error 0x % X\n, fboStatus);

return false;

}

// возвращаем FBO по-умолчанию(GL_FRAMEBUFFER, 0);

После создания FBO и привязки к нему текстуры мы можем выполнить рендер всей iены с использованием этого FBO:

// установим активный FBO(GL_FRAMEBUFFER, depthFBO);

// размер вьюпорта должен совпадать с размером текстуры для хранения буфера глубины

glViewport (0, 0, DEPTH_TEXTURE_WIDTH, DEPTH_TEXTURE_HEIGHT);

// отключаем вывод цвета(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);

// включаем вывод буфера глубины(GL_TRUE);

// очищаем буфер глубины перед его заполнением

glClear (GL_DEPTH_BUFFER_BIT);

// отключаем отображение внешних граней объекта, оставляя внутренние(GL_FRONT);

// выполним рендер iены с использованием шейдерной программы и камеры источника освещения(depthProgram, lightCamera);

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

Во-вторых, рендер внутренних граней объекта помогает избежать некоторых артефактов при использовании теневой карты. Однако данный способ подходит только для замкнутых выпуклых объектов, именно такие мы и используем в этом уроке.

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

В минимальном варианте вершинный шейдер для такой шейдерной программы выглядит следующим образом:

layout (location = VERT_POSITION) in vec3 position;

// параметры преобразованийstruct Transform

{modelViewProjection;

} transform;main(void)

{

// переводим координаты вершины в однородные_Position = transform.modelViewProjection * vec4 (position, 1.0);

}

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

main(void)

{

}

Рисования итогового изображения вместе с тенью

Рисунок 3.4 Алгоритм рисования итогового изображения вместе с тенью

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

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

void CameraSetupLightMatrix (GLuint program, const Camera &camera)

{

// матрица сдвига те