Добавлен alexprey,
опубликован
Программирование
Язык:
C++
Давненько я вам не писал про свои разработки в плане графики. Не так ли? Так вот решил продолжить цикл записей. Кто не в теме, о чем я, бегом читать это. Ну ладно начнем.
Начало
Всем доброго времени суток, дорогие читатели =)
Все началось с того, что я дошел до такой стадии написания кода, что понял, что где то в архитектуре у меня косяк. Да это тяжело признавать, но лучше раньше, чем поздно. В общем пришлось очень много думать над новой архитектурой всего приложения. В общем пересмотрев еще кучу уроков, сдк и различных документаций, до меня доперло, где пошел косяк.
Все началось с того, что я дошел до такой стадии написания кода, что понял, что где то в архитектуре у меня косяк. Да это тяжело признавать, но лучше раньше, чем поздно. В общем пришлось очень много думать над новой архитектурой всего приложения. В общем пересмотрев еще кучу уроков, сдк и различных документаций, до меня доперло, где пошел косяк.
В общем пришлось долго и упорно переписывать кучу кода (ушло примерно дня 2 на написание и еще с пол дня наверное на отладку косяков линковки). Ну в общем после этого я сразу почувствовал разницу между текущем кодом и прошлым. С новой архитектурой как ни странно удалось даже повысить фпс. Однако был переписан еще не весь код нужных частей, но на данный момент они являются не столь важными в плане создания.
После всего этого решил доделать последний тип источника освещения, это фонарь (SpotLight). Вооружившись бумагой, ручкой, а так же здравым смыслом (хотя какой может быть здравый смысл, когда берешься за матан, хоть и элементарый =)), пытался разобраться по какому принципу меняется освещенность каждого из участка освещаемой зоны. Формула получилась довольно таки большой. Кому интересно, добро пожаловать под спойлер.
После всего этого решил доделать последний тип источника освещения, это фонарь (SpotLight). Вооружившись бумагой, ручкой, а так же здравым смыслом (хотя какой может быть здравый смысл, когда берешься за матан, хоть и элементарый =)), пытался разобраться по какому принципу меняется освещенность каждого из участка освещаемой зоны. Формула получилась довольно таки большой. Кому интересно, добро пожаловать под спойлер.
раскрыть
float alpha = acos(dot(pointDirection, lightDirection)/(length(pointDirection)*length(lightDirection)));
float phi = Light[Idx][2][3];
float theta = Light[Idx][1][3];
if (alpha <= phi)
{
float intensive = dist / (A[0] + A[1]*dist + A[2]*pow(dist, 2.0f));
if (alpha >= theta && alpha <= phi)
intensive *= 1.0f - (alpha - theta) / (phi - theta);
float3 ldir = normalize(position - input.position);
return float4(
Color[0] * intensive +
Color[1] * intensive * dot(ldir, input.normal)
, 0.0f);
}
Результат действия данного шейдера можно увидеть на скриншоте слева. На нем изображены 3 источника освещения данного типа. Два расположены на стене, имеют зеленый цвет, а так же смотрят в противоположные стороны. Так же они еще и вращаются вокруг оси Z. Получается что-то типа сирены =). В общем выглядит достаточно забавно. Еще один источник направлен на стену и дает слегка сероватое освещение. Его направление регулируется с помощью стрелок на клавиатуре, так же забавно смотрится.
Еще немного о добавленном
Кроме того решил я еще побаловаться и с пост проссингом. Решил написать простенький пост эффект в несколько проходов. Первый проход разбивает картинку на кусочки в виде шахматной доски. А второй проход заменяет черный на белый. Было огромное разочарование в том, что я не увидел результата второго прохода. Пришлось подумать над данной проблемой. В результате пересмотрев код выполнения пост процессинга в коде приложения заметил, что я постоянно обрабатывал одну и ту же текстуру, исходную, с исходной сценой. А при выводе второго прохода на экран, точки просто не проходили тест на глубину. В общем получилась такая вот биллебердистика. Что пришлось сделать. Создать новую временную текстуру куда бы рисовалась картинка, а потом её копировать в исходную текстуру. Да идея хорошая, но к сожалению копирование текстуру из одного участка памяти в другой занимает достаточное кол-во FPS. В общем после этого все встало на свои места и пост процессинг шел как надо, но вот FPS был жутко низок. Но заметив одну слабость в этом алгоритме, мне удалось поднять чуточку FPS (В этом алгоритме я использовал Z-буффер, и каждый раз его очищал перед новым проходом. Эту проблему решил отключением данного буфера на время рендера очередного квада с текстурой результата работы пост эффекта). Главное уже работает, а оптимизировать еще смогу, к тому же уже даже представляю как это можно переделать, но пока что не до этого.
Тени
Да я хотел бы уделить достаточно текста этому пункту. На это было потрачено очень много нервов, а так же времени. Начнем с того, что все началось с выбора техники построения теней. Первое, что мне попалось это обычные Shadow Maps. В принципе технология простая и не требует длительного процесса написания. Но нервов было потрачено больше, нежели написано строк кода. Начнем с того, что требуется вывести буфер глубины сцены в текстуру. Окей ладно, это просто. Устанавливаем нашу текстуру в качестве рендер таргета и рендерим туда нашу сцену. Дальше надо было спроецировать полученную текстуру на нашу сцену. В принципе как я заметил это делается не так уж и сложно (по формуле). Однако результат меня не обрадовал <_<
На первом скриншоте сразу видно, что определение глубины сцены считается полностью не верно. Но на втором скриншоте прекрасно видно, что вычисляется правильно, но вот проекция данной текстуры идет не правильная. В общем потратив на эту проблему пару суток, какая то магия помогла мне в этой проблеме. Сам даже не понял как удалось её решить.
На скриншотах выше я изложил все стадии построения теней. Как видите смотрятся они не очень. Имею слишком много артефактов, но ведь тени есть =) И это очень круто. После стольких мучений такой прогресс =) Но получаемый FPS был низок (30 кадров). Это просто грустно. Но я опять таки нашел то слабое место, благодаря которому я снова поднял FPS до вполне хорошей отметки 150. Вся проблема заключалась опять же в копировании текстуры.
Подробнее об алгоритме
И так, что же это за алгоритм такой и в чем его суть. Сам долго вникал как это все происходит. И так для начала немного терминов:
- Z-буфер или Буфер глубины - удаление пикселя от зрителя
- Берется каждый источник света и в его позицию устанавливается камера
- Производится рисование всей сцены в текстуру (DepthTexture - текстура глубины), где в пиксельном шейдере мы пишем заместо цвета точки, глубину вершины. Код ниже.
- Устанавливается стандартная камера обзора
- Производится рисование всей сцены уже на экран, при этом в наш шейдер передаются данные о камере источника света (матрица проекции и вида)
- в пиксельном шейдере мы перемножаем координаты нашего пикселя на матрицу вида и проекции источника света.
- получаем данные глубины для этой точки
- если глубина точки, что на экране больше, чем глубина точки в текстуре глубины для данного источника, то получаем, что данный пиксель не освещен этим источником света, т.к. есть какой либо объект препятствующий ему
//Получение глубины для заданной точки
float4 Pos = ...;
float depthValue = Pos.z / Pos.w;
//Проекция текстуры
float2 projectTexCoord;
projectTexCoord.x = (lightViewPos.x / lightViewPos.w) / 2.0f + 0.5f;
projectTexCoord.y = -(lightViewPos.y / lightViewPos.w) / 2.0f + 0.5f;
if (saturate(projectTexCoord.x) == projectTexCoord.x && saturate(projectTexCoord.y) == projectTexCoord.y)
{
//получение глубины от источника света
float lightDepthValue = DepthTexture[Idx].Sample(ClampSamplerState, projectTexCoord).r;
}
Ну ладно решили эту проблему и бог с ним. Покопавшись в различных архивах, статьях я понял что вся проблема заключается в неточном буфере глубины. Как оказывается данный буфер имеет не линейную формулу, что приводит к большим погрешностям. Но наткнулся на одну очень забавную формулу, которая помогла преобразовать формулу глубины в линейный вид.
Данная формула давала в результате очень хороший вид даже сразу видно, где должны быть тени. И причем достаточно точно. Однако когда я получал значение глубины для остальных источников, меня ждало лишь разочарование. В полученной картинке я не мог увидеть ни какой логики, поэтому пришлось отказаться от данной задумки. Возможно позже найду замену данной технологии.
Итоги
Ну ладно на этом наверное все. Подведу ка я итоги проделанной работы. Что я имею на данном этапе развития:
- Sky-Box небо
- Освещение от 8 источников света, с просчетом теней от зонального источника света
- Рисование сцены в текстуру с последующей пост обработкой
- Хитрую иерархию сцены
- Технология рисования большого кол-ва копий одного и того же объекта за один проход
Всем удачи, и спасибо, что прочитали =)
Чтобы оставить комментарий, пожалуйста, войдите на сайт.
Ну по себе знаю делать велосипеды без цели довольно интересное занятие, да и еще и опыт повышает и дает обширные знания по языку в целом. Было бы интересно прочитать про тени, но ничего конкретного так и не увидел. Объяснил бы что к чему и как.
у современных мониторов в любом случае частота обновления порядка 70.
Про тени - поищи в GPU Gems, уж не знаю в каком. И вообще GPU Gems - лучший источник информации по 3D, правда английском.
Отредактирован alexprey
Fps - показывает сколько раз картинка была обработана карточкой