Отложенный рендеринг. Способ рендеринга отложенного освещения. Введение в Deferred Rendering

В данной статье я описываю свой опыт компиляции библиотеки Qt версии 4.8.5 из исходников с помощью компилятора MinGW, идущего в составе среды программирования CodeBlocks версии 10.05 на машине с операционной системой Windows XP.

Так как для движка нужен нормальный редактор карт и объектов, то я задумался о выборе GUI библиотеки. Вполне логично, что ввиду повсеместного расхваливания qt, я не мог не попробовать ее в деле. Скачал книжки, инсталлировал версию "qt-win-opensource-4.8.4-mingw". У меня CodeBlocks версии 10.05. Создал в CodeBlock класс главного окна и модуль с main - короче, простейшее приложение с главным окном. Надо сказать, что меня ждало разочарование - компиляция и сборка qt-приложений представляет собой не банальное нажатие пункта "Build", а ряд предварительных настроек и действий, да и вообще производится в командной строке. После пары дней геморроя я скомпилировал этот небольшой проект с главным окном. Но для этого мне пришлось собрать qt из исходников. Дело в том, что ошибки никак не хотели устраняться и я где-то прочитал, что якобы версия mingw на компьютере разработчика может конфликтовать с установленным qt из-за того, что установленный qt был собран на другой версии mingw. Что ж, решил собрать.
Скачал исходники "qt-everywhere-opensource-src-4.8.5.zip". Хотя сейчас уже есть пятая версия, я решил взять предпоследнюю на тот момент. На диске "E" (это тот, что обычно D у большинства) создал папку "Qt". Положил архив туда. Распаковал его.
Делал я все на Windows XP. Так как компилятор MinGW идет в составе среды программирования CodeBlocks, я использовал его, то есть я не устанавливал его отдельно.

Установил переменные среды:
Мой компьютер-> Свойства:

Дополнительно:

Переменные среды:

Сначала добавляете путь к папке bin компилятора MinGW (у меня E:\Program Files\CodeBlocks\MinGW\bin)
Нужно выделить переменную "Path", нажать кнопку "Изменить":

Внимание! Нужно не заменять то, что там уже есть, а именно добавить , иначе что-то на вашем компе может перестать нормально работать. Появится окошко.
Пойти в конец строки поля ввода с именем "Значение переменной". Если там не стоит в конце ";" (без кавычек), то поставьте точку с запятой и вставьте путь к папке "bin" у MinGW.
У меня вот так (я показал многоточием то, что у меня уже есть путь к другим папкам)...;E:\Program Files\CodeBlocks\MinGW\bin;
То есть пути в "Path" разделяются точками с запятой. Жмете ок.
Создаете следующие переменные:

Имя переменной: MinGWDIR Значение переменной: путь к папке MinGW (у меня E:\Program Files\CodeBlocks\MinGW)
Имя переменной: QMAKESPEC Значение переменной: win32-g++
Имя переменной: QTDIR Значение переменной: путь к распакованным исходникам qt (у меня E:\Qt\qt-everywhere-opensource-src-4.8.5)

QMAKESPEC должен содержать имя профиля сборки. Для выбранного компилятора MinGW, у Qt есть два профиля: "win32-g++" - для версий компилятора GCC ниже 4.6, и "win32-g++-4.6" - для версий компилятора GCC 4.6 (он задействован в последних версиях MinGW).

Конфигурация библиотеки Qt. Запускаем командную строку и переходим в ней в папку с исходниками Qt (у меня E:\Qt\qt-everywhere-opensource-src-4.8.5). Нужно запустить конфигуратор с опциями. Я использую LGPL - версию, поэтому выбрал опцию -opensource. Так же мне не нужны демо и примеры -nomake demos -nomake examples. Мне нужны версии debug и release. Вообщем, я набил (лучше вручную, а то при вставке может запуститья не со всеми опциями, а только с теми, которые поместились в первой строке) следующее:

configure.exe -debug-and-release -qt-libpng -qt-libjpeg -opensource
-no-qt3support -qt-zlib -qt-libmng -qt-libtiff
-nomake demos -nomake examples


Конфигуратор спросит вас, дав 4 варианта. Нужно согласиться использовать LGPL (я к сожалению не помню их названия, но там понятно).
После этого конфигуратор создаст ключевой файл qmake.exe и makefile"ы модулей библиотеки Qt. После завершения этого процесса появится сообщение:

Qt is now configured for building. Just run mingw32-make.
To reconfigure, run mingw32-make confclean and configure.

Осталось запустить компиляцию. Для этого вбейте в командной строке следующее:

Компиляция qt идет очень долго. У меня этот процесс занял 10 часов. Я начал в 11.00. В комп в последний раз заглядывал в 18.20 - процесс еще шел. Утром мне пришлось перезагрузить комп, так как комп не реагировал на нажатия кнопок мыши и клавиш клавиатуры. Через поиск я нашел, что самый последний созданный файл библиотеки был создан в 21.02.
После я решил проверить собранную библиотеку Qt. Прописал в переменных среды путь к папке "bin" у Qt (у меня E:\Qt\qt-everywhere-opensource-src-4.8.5\bin). Это нужно для запуска qmake.
Итак, я проверяю созданный в CodeBlocks пример приложения Qt с главным окном.
В командной строке я перешел в папку проекта с главным окном. Набил

Qt создала файл проекта с расширением.pro. Далее набил просто без параметров. Qt создал makefile" ы проекта. Осталось скомпилировать проект - для этого я набил в командной строке
Все. В папке Debug я увидел долгожданный экзешник, над названием которого ввиду трепки нервов с Qt я не стал долго думать:

Показать

Как оказалось, скачав лишь , создать свою программу на Qt не удастся. Помимо этого файла, в первую очередь, нужен рабочий компилятор (для данной версии это MinGW не ниже 4.4), который придется устанавливать отдельно, и скачать уже готовый. Среда разработки () оказалась поставляется вне библиотек Qt 4.8.5 и тоже требует отдельной установки. Ну и напоследок, если собираетесь запускать свою программу в отладчике по шагам (а я уверен, что рано или поздно придется это делать), то необходим еще и свой отладчик (GDB имеющийся в MinGW не подходит)
А теперь обо всем по порядку.

Последовательность действий:

1. Установка компилятора C++ MinGW .
Можно воспользоваться способом установки через . Но для новичка он труден. Я предлагаю поступить проще - скачать уже готовый настроенный и проверенный пакет всего необходимого для компиляции проектов Qt на C++.
1.1. Скачиваем: mingw_4.4.0.zip .
1.2. Создаем папку Qt в корне системного раздела (диск C:) и переносим в нее папку MinGW

2. Установка библиотек Qt 4.8.5
2.1. Скачиваем их: .
2.2. Запускаем скачанный exe-файл.
2.3. В процессе установки указываем:
путь куда установить: C:\Qt\4.8.5
путь до MinGW: C:\Qt\MinGW
остальное по умолчанию

3. Установка среды разработки Qt Creator 3
3.1. Скачиваем её: .
3.2. Запускаем скачанный exe-файл.
3.3. В процессе установки указываем:
путь куда установить: C:\Qt\Creator3
остальное по умолчанию

4. Установка отладчика GDB 7.7 для Qt
Почему-то Qt не нравится тот gdb, который имеется в пакете MinGW. Qt хочет свой отладчик, по своему настроенный. И его вы можете собрать используя эту статью. Я собирал именно по этой статье (разве что пришлось заменить файл из исходников - \src\dist\gdb\Makefile.mingw, на последний Makefile.mingw с сайта https://qt.gitorious.org/qt). Но я предлагаю использовать уже собранный мной GDB.
4.1. Скачиваем его: Qt_GDB7.7.zip (13 MB).
4.2. Переносим в С:\Qt папку Qt_GDB7.7 и всё ее содержимое из скачанного архива.

5. Настройка Qt
4.1. Запускаем Qt Creator
4.2. Открываем окно Параметров во вкладке Инструменты .
4.3. Выбираем в правом меню строку Сборка и запуск .
4.4. Начнем с конца. Переходим на вкладку Отладчики .
4.5. Нажимаем кнопку Добавить , даем название GDB и указываем путь:
C:\Qt\Qt_GDB7.7\gdb-i686-pc-mingw32.exe
4.6. Применить


4.7. Вкладка Компиляторы -> кнопка Добавить -> MinGW .
4.8. Указываем путь к компилятору:
C:\Qt\MinGW\bin\gcc.exe
4.9. Остальное не трогаем. Применить .


4.10. Вкладка Профили Qt -> кнопка Добавить .
4.11. Указываем путь до qmake:
C:\Qt\4.8.5\bin\qmake.exe
4.12. Изменим название на Qt 4.8.5
4.13. Остальное не трогаем. Применить .


4.14. Вкладка Комплекты . Удаляем комплект Desktop (по умолчанию) , если имеется.


4.15. Нажимаем кнопку Добавить . Присваиваем имя: Qt 4.8.5 (MinGW) .
4.16. В строках Компилятор , Отладчик и Профиль Qt указываем только что созданные параметры.
4.17. Остальное не трогаем. Применить .


4.18. ОК

Всё, теперь можно начать создавать программы использую библиотеку Qt и среду разработки Qt Creator .

Для начала можете запустить этот тестовый проект "Hello, World" (взят из книги М.Шлее)
Кстати, Qt не поддерживает русских символов в пути до проектов. А все проекты советую хранить в созданной специально для этого папке C:\Qt\Projects . Для запуска этого проекта просто распакуйте его (папку Hello и два имеющихся в ней файла) и запустите проектный файл Hello.pro с помощью Qt Creator . Среда разработки предложит настроить его. С её предложением мы согласимся, оставим как есть, и нажмем кнопку Настроить проект .



Отложенное освещение и затенение , отложенный рендеринг (англ. deferred shading ) - программная техника (методика) в трёхмерной компьютерной графике, которая обрабатывает освещение и затенение визуальной сцены. В результате работы алгоритма отложенного освещения и затенения процесс вычисления разбивается на меньшие части, которые записываются в промежуточную буферную память и объединяются потом. Главным отличием отложенного освещения и затенения от стандартных методов освещения является то, что эти методы немедленно записывают результат работы шейдера во фреймбуфер цвета. Реализации в современных аппаратных средствах по обработке графики имеют тенденцию использовать множественные цели рендеринга (англ. multiple render targets - MRT ) для избежания избыточных трансформаций вершин. Обычно, как только построены все необходимые буферы, они затем считываются (обычно как вводная текстура) из шейдерного алгоритма (например, уравнение освещения) и объединяются для создания результата. В этом случае вычислительная сложность и полоса пропускания памяти, необходимые для рендеринга сцены, уменьшаются до видимых частей, таким образом уменьшая сложность освещаемой сцены.

Первичным преимуществом отложенного рендеринга является совместимость с «грубым» и «ранним» тестированием Z-буфера, другие преимущества ещё не исследованы в должной степени. Эти преимущества могут включать более простое управление сложными ресурсами освещения, лёгкость управления другими сложными шейдерными ресурсами и упрощение программного конвейера визуализации.

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

Другим довольно важным недостатком отложенного рендеринга является несовместимость со сглаживанием. Так как стадия освещения отделена от стадии геометрии, то аппаратный анти-алиасинг не приводит к правильным результатам. Хотя первый проход, используемый при рендеринге базовых свойств (диффузная обработка, карта высот), может использовать сглаживание, к полному освещению сглаживание неприменимо. Одной из стандартных методик для преодоления этого ограничения является метод выделения границ (en:edge detection) финального изображения и затем применения размытия к граням (границам).

Методика отложенного рендеринга всё более часто используется в компьютерных играх, так как допускает использование неограниченного количества источников света и уменьшает сложность необходимых шейдерных инструкций. В частности, «Advanced Technology Group», команда специалистов компании Sony Computer Entertainment, исследовала эту область и помогает разработчикам встраивать эту технологию в графические движки. PhyreEngine, бесплатный графический движок разработки Sony Computer Entertainment, имеет поддержку отложенного освещения и затенения. Примерами игр, использующих отложенный рендеринг и разработку которых поддержала Sony Computer Entertainment, являются Killzone 2 разработки Guerrilla Games, LittleBigPlanet разработки Media Molecule и inFamous разработки Sucker Punch Productions. К играм, использующим отложенный рендеринг, но в разработке которых Sony не принимала участие, являются серия игр S.T.A.L.K.E.R. разработки GSC Game World, Dead Space разработки Electronic Arts и Tabula Rasa разработки NCSoft. Технология отложенного освещения и затенения используется в игровом движке CryEngine 3 разработки Crytek.


История

Идея отложенного освещения и затенения изначально была представлена Майклом Дирингом (en:Michael Deering) и его коллегами в работе под названием «The triangle processor and normal vector shader: a VLSI system for high performance graphics», опубликованной в 1988 году. Хотя в работе нигде не используется слово «отложенный», концепция, представленная там, только недавно нашла практическое применение в таких приложениях, как компьютерные игры.


Примечания

  1. NVIDIA SDK 9.51 - Featured Code Samples - download.nvidia.com/developer/SDK/Individual_Samples/featured_samples.html. NVIDIA (2007-01-17).
  2. Deferred shading tutorial - www710.univ-lyon1.fr/~jciehl/Public/educ/GAMA/2007/Deferred_Shading_Tutorial_SBGAMES2005.pdf. Pontifical Catholic University of Rio de Janeiro.
  3. Dead Space by Electronic Arts - nzone.com/object/nzone_deadspace_feature.html. NVIDIA.
  4. Deferred shading in Tabula Rasa - developer.nvidia.com/GPUGems3/gpugems3_ch19.html. NVIDIA.
  5. Deering, Michael; Stephanie Winner, Bic Schediwy, Chris Duffy, Neil Hunt. «The triangle processor and normal vector shader: a VLSI system for high performance graphics». ACM SIGGRAPH Computer Graphics 22 (4): 21–30.
  6. Deferred Shading - download.nvidia.com/developer/presentations/2004/6800_Leagues/6800_Leagues_Deferred_Shading.pdf (PDF). NVIDIA.
  7. Klint, Josh. «Deferred Rendering in Leadwerks Engine - www.leadwerks.com/files/Deferred_Rendering_in_Leadwerks_Engine.pdf».
скачать
Данный реферат составлен на основе статьи из русской Википедии . Синхронизация выполнена 19.07.11 17:46:16
Похожие рефераты:
  • Алгоритмы
    • Tutorial

    Привет, друг! В этот раз я опять подниму вопрос о графике в ААА -играх. Я уже разобрал методику HDRR (не путать с HDRI) и чуть-чуть поговорил о коррекции цвета. Сегодня я расскажу, что такое SSLR (так же известная как SSPR, SSR): . Кому интересно - под кат.

    Введение в Deferred Rendering

    Для начала введу такое понятие как Deferred Rendering (не путать с Deferred Shading , т.к. последнее относится к освещению). В чем суть Deferred Rendering ? Дело в том, что все эффекты (такие как освещение, глобальное затенение, отражения, DOF ) можно отделить от геометрии и реализовать эти эффекты как особый вид постпроцессинга. К примеру, что нужно, чтобы применить DOF (Depth Of Field , размытие на дальних расстояниях) к нашей сцене? Иметь саму сцену (Color Map ) и иметь информацию о позиции текселя (другими словами на сколько пиксель далеко от камеры). Далее - все просто. Применяем Blur к Color Map , где радиус размытия будет зависеть от глубины пикселя (из Depth Map ). И если взглянуть на результат - чем дальше объект, тем сильнее он будет размыт. Так что же делает методика Deferred Rendering ? Она строит так называемый GBuffer , который, обычно, в себя включает три текстуры (RenderTarget ):

    В случае с Color map , Normal map вроде все понятно, это обычные Surface.Color текстуры: пожалуй, за исключением того, что вектор нормали может лежать в пределах [-1, 1] (используется простая упаковка вектора в формат ).

    А вот ситуация с Depth map становится непонятной. Как же Depth map хранит в себе информацию о позиции пикселя, да еще и одним числом? Если говорить сильно упрощенно, трансформация примитива:

    Float4 vertexWVP = mul(vertex, World*View*Projection);

    Дает нам экранные координаты:

    Float2 UV = vertexWVP.xy;

    И некоторую информацию о том, насколько “далеко” от камеры пиксель:

    Float depth = vertexWVP.z / vertexWVP.w;

    Исходя из этого UV нам не нужен, т.к. при рисовании обычного квада на весь экран он и так известен. Поэтому стоит хранить в карте глубины не позицию пикселя, а только глубину.

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

    Float3 GetPosition(float2 UV, float depth) { float4 position = 1.0f; position.x = UV.x * 2.0f - 1.0f; position.y = -(UV.y * 2.0f - 1.0f); position.z = depth; //Transform Position from Homogenous Space to World Space position = mul(position, InverseViewProjection); position /= position.w; return position.xyz; }

    Напомню, что для построения GBuffer необходима такая методика как MRT (Multiple Render Targets ), которая рисует модель сразу в несколько Render Target (причем в каждом RT содержится разная информация). Одно из правил MRT - размерность всех Render Target должна быть одинаковой . В случае Color Map , Normal Map - Surface.Color : 32-ух битная RT , где на каждый канал ARGB приходится по 8 бит, т.е. 256 градаций от 0 до 1.

    Благодаря такому подходу мы можем применять сложные эффекты к любой геометрии, например самый популярный Screen Space эффект: SSAO (Screen Space Ambient Occlusion). Этот алгоритм анализирует буферы глубины и нормали, считая уровень затенения. Весь алгоритм я описывать не буду, он уже на хабре, скажу лишь то, что задача алгоритма сводится к трассировки карты глубины: у нас есть набор случайных векторов, направленных из считаемого “пикселя” и нам нужно найти кол-во пересечений с геометрией.

    Пример эффекта (слева без SSAO, справа с SSAO):

    Так же Deferred Shading является Screen Space эффектом. Т.е. для каждого источника света на экране (без всяких оптимизаций) мы рисуем квад в режиме Additive в так называемый RenderTarget : Light Map . И зная мировую позицию “пикселя”, его нормаль, позицию источника света - мы можем посчитать освещенность этого пикселя.

    Пример Deferred Shading (освещение выполнено отложено, после отрисовки геометрии):

    Достоинства и проблемы Screen Space эффектов
    Самый главный плюс Screen Space эффектов - независимость сложности эффекта от геометрии.

    Самый главный минус - локальность всех эффектов. Дело в том, что мы постоянно будем сталкиваться с Information Lost , во многих случаях это сильно зависит обзора, поскольку SSE зависит от смежных глубин текселей, которые могут быть сгенерированы любой геометрией.

    Ну и стоит отменить, что Screen Space эффекты выполняются полностью на GPU и являются пост-процессингом.

    Наконец SSLR

    После всей теории мы подошли к такому эффекту, как Screen Space Local Reflections : локальные отражения в экранном пространстве.

    Для начала разберемся с перспективной проекцией:

    Горизонтальный и вертикальный угол зрения задается FOV (обычно 45 градусов, я предпочитаю 60 градусов), в виртуальной камере они разные т.к. учитывается еще и Aspect Ratio (соотношение сторон).

    Окно проекции (там, где мы оперируем UV-space данными) - это, что мы видим, на то мы проецируем нашу сцену.
    Передняя и задняя плоскости отсечения это соответственно Near Plane, Far Plane , задаются так же в проекцию как параметры. Делать в случае Deferred Rendering слишком большим значением Far Plane стоит, т.к. точность Depth Buffer сильно упадет: все зависит от сцены.

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

    Float3 GetPosition(float2 UV, float depth) { float4 position = 1.0f; position.x = UV.x * 2.0f - 1.0f; position.y = -(UV.y * 2.0f - 1.0f); position.z = depth; position = mul(position, InverseViewProjection); position /= position.w; return position.xyz; }

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

    Float3 viewDir = normalize(texelPosition - CameraPosition);
    В качестве CameraPosition выступает позиция камеры.
    И найти отражение этого вектора от нормали в текущем пикселе:

    Float3 reflectDir = normalize(reflect(viewDir, texelNormal));
    Далее задача сводится к трассировке карты глубины. Т.е. нам нужно найти пересечение отраженного вектора с какой-либо геометрией. Понятное дело, что любая трассировка производится через итерации. И мы в них сильно ограниченны. Т.к. каждая выборка из Depth Map стоит времени. В моем варианте мы берем некоторое начальное приближение L и динамически меняем его исходя из расстояния между нашим текселем и позицией, которую мы “восстановили”:

    Float3 currentRay = 0; float3 nuv = 0; float L = LFactor; for(int i = 0; i < 10; i++) { currentRay = texelPosition + reflectDir * L; nuv = GetUV(currentRay); // проецирование позиции на экран float n = GetDepth(nuv.xy); // чтение глубины из DepthMap по UV float3 newPosition = GetPosition2(nuv.xy, n); L = length(texelPosition - newPosition); }

    Вспомогательные функции, перевод мировой точки на экранное пространство:

    Float3 GetUV(float3 position) { float4 pVP = mul(float4(position, 1.0f), ViewProjection); pVP.xy = float2(0.5f, 0.5f) + float2(0.5f, -0.5f) * pVP.xy / pVP.w; return float3(pVP.xy, pVP.z / pVP.w); }

    После завершения итераций мы имеет позицию “пересечения с отраженной геометрией”. А наше значение nuv будет проекцией этого пересечения на экран, т.е. nuv.xy – это UV координаты в экранном нашем пространстве, а nuv.z это восстановленная глубина (т.е. abs(GetDepth(nuv.xy)-nuv.z) должен быть очень маленьким) .

    В конце итераций L будет показывать расстояние отраженного пикселя. Последний этап - собственно добавление отражения к Color Map :

    Float3 cnuv = GetColor(nuv.xy).rgb; return float4(cnuv, 1);

    Разбавим теорию иллюстрациями, исходное изображение (содержание Color Map из GBuffer):

    После компиляции шейдера (отражения) мы получим следующую картину (Color Map из GBuffer + результат шейдера SSLR):

    Не густо . И тут стоит еще раз напомнить, что Space-Screen эффекты это сплошной Information Lost (примеры выделены в красные рамки).

    Дело в том, что если вектор отражения выходит за пределы Space-Screen – информация о Color -карте становится недоступной и мы видим Clamping нашего UV .

    Чтобы частично исправить эту проблему, можно ввести дополнительный коэффициент, который будет отражать “дальность” отражения. И далее по этому коэффициенту мы будем затенять отражение, проблема частично решается:

    L = saturate(L * LDelmiter); float error *= (1 - L);

    Результат, отражение умноженное на error (попытка убрать артефакт SSLR - information lost):

    Уже лучше, но мы замечаем еще одну проблему, что будет, если вектор отразится в направлении камеры? Clamping ’а UV происходить не будет, однако, несмотря на актуальность UV (x > 0, y > 0, x < 1, y < 1) он будет неверным:

    Эту проблему так же можно частично решить, если как-нибудь ограничить углы допустимых отражений. Для этого идеально подходит фишка с углами от эффекта Френеля :

    Float fresnel = dot(viewDir, texelNormal);
    Чуть-чуть модифицируем формулу:

    Float fresnel = 0.0 + 2.8 * pow(1+dot(viewDir, texelNormal), 2);
    Значения Френеля, с учетом Normal-маппинга (значения fresnel-переменной для SSLR-алгоритма).