Реализация процессора эффектов постобработки. Часть 1 - Разработка фреймворка
Страница: 1 2 3
Содержание
Предисловие
Введение
Разработка фреймворка для реализации пост-эффектов
Описание проблемы и предлагаемое решение
Архитектура фреймворка
Синтаксис файла пост-эффекта
Реализация фреймворка с использованием библиотеки OpenGL
Список источников
Предисловие
Эффекты постобработки в играх за
последнее время успели не только стать стандартом, но и сделать огромный шаг
вперед. От простого сепия и bloom, до использования информации о глубине сцены для получения реалистичных солнечных
шафтов. Порой даже трудно отличить, выполняется ли тот или иной эффект в
процессе рендеринга трехмерной сцены, либо же это результат постобработки. Эта
статья описывает один из подходов к реализации пост-эффектов в интерактивных
приложениях использующих ускоренную трехмерную графику.
Все, о чем написано в этой статье – мое личное мнение,
основанное как на личном опыте, так и на доступных в Интернете материалах. Я ни
в коем случае не претендую на последнюю инстанцию, но надеюсь, что описанный
мною подход, пусть и не является чем-то революционным, но зато поможет в
решении проблемы интегрирования пост-эффектов в приложения, использующие ускоренную
трехмерную графику.
Читать такую большую статью - труд, старвнимый, пожалуй, с написанием самой статьи, поэтому,
прежде чем приступать к ее чтению, позвольте вас хоть как-то на это простимулировать.
Во второй части статьи вы можете найти
бинарник (10 Мб) приложения
демонстрирующего несколько различных пост-эффектов. Все эффекты выполняются в одном приложении, но не фиксирваны в его коде.
Каждый пост-эффект описывается в специальном конфигурационном файле.
Для полноценного функционирования демки необходимо железо уровня не ниже GeForce 6600.
Введение
В этой статье я попытаюсь осветить
проблему интегрирования пост-эффектов в приложения использующие трехмерную
графику, а также создать несколько популярных пост-эффектов на основе предлагаемого
решения – фреймворка для реализации пост-эффектов. В первой части статьи будет рассмотрена
архитектура такого фреймворка и способ описания пост-эффектов на основе
специальных конфигурационных файлов. Также будут рассмотрены некоторые аспекты
реализации постобработки средствами C++ и OpenGL. Во второй части статьи речь
пойдет о реализации некоторых пост-эффектов на основе разработанного фреймворка.
Как можно будет заметить, последний существенно упрощает создание и внедрение пост-эффекта
в приложение. Жесткая фиксация пост-эффекта в коде графического движка по
сравнению с описанием пост-эффекта в текстовом файле, или в специальном
редакторе, создает дополнительные трудности, не обладает достаточной гибкостью
и сильно зависит от программиста. Использование специальных файлов для конфигурации
пост-эффектов позволяет забыть обо всех тонкостях работы с 3D API и
сосредоточиться непосредственно на создании пост-эффекта, в результате чего
создание нового эффекта с нуля занимает всего несколько минут.
Статья будет интересна как начинающим, так и людям, профессионально
занимающимся 3D программированием. От читателя требуется знание принципов ООП и языка C++,
а также знание хотя бы одного 3D API. Если вы еще не знакомы с
OpenGL или Direct3D, то прежде чем читать эту статью, вам лучше
обратиться к соответствующей литературе по этим 3D API, например [OGL] или [Lun].
Если вы уже знакомы с библиотекой OpenGL 1.2, но не знакомы с расширениями OpenGL,
то вам следует ознакомиться с использованием расширений, например по книге [Bor05].
Также необходимо знание одного из высокоуровневых языков программирования шейдеров,
желательно GLSL. Начинать знакомство с GLSL лучше с чтения "Оранжевой книги" [Rost05],
а HLSL с чтения [Lun]. По языку Cg тоже имеется информация, например на сайте nVidia
[NVCg] или в книге [CG].
Разработка фреймворка для реализации пост-эффектов
Описание проблемы и предлагаемое решение
Современные тенденции развития игрового бизнеса зачастую подразумевают разбиение
процесса разработки игры на несколько параллельных этапов. Каждый, кто занят в
проекте, занимается своим делом и отвечает за определенный пункт, как и при любом
техпроцессе, идет четкое разделение труда. С точки зрения такого разделения нам
необходимо знать, кому следует доверить задание пост-эффектов. Думаю очевидно,
что за этот пункт должны отвечать дизайнеры пост-эффектов или дизайнеры уровней,
но никак не программисты. Именно дизайнеры в нужный момент вправе задавать и создавать
различные пост-эффекты, а также настраивать их параметры.
За примером далеко ходить не надо. Представим, что на некотором игровом уровне есть
некая зона, например зона с повышенной радиоактивностью (привет Сталкеру =), и нужно
показать, что при входе в эту зону картинка на экране искажается, имитируя спазмы у
персонажа, например потемнение в глазах, двоится изображение и т.п. В этом случае
проще в редакторе уровня предусмотреть возможность задания дизайнером как размеров
и положения такой зоны, так и действующий в ней пост-эффект. Тут же можно будет задать
одним из параметров степень проявления этого эффекта в зависимости от расстояния
до эпицентра радиации. Рядом можно предусмотреть редактор пост-эффектов, в котором
описывается весь процесс "фильтрации" изображения, сколько и каких проходов надо
выполнить для получения желаемого результата. Таким образом, мы добиваемся того,
что задание пост-эффекта почти ничем не отличается от задания материала для модели в
сцене. Необходимая гибкость будет обеспечена за счет того, что пост-эффекты станут
еще одним типом контента игры, наподобие fx файлов Microsoft DirectX. Остается
самое интересное - написать фреймворк, в котором будут работать заданные дизайнером
пост-эффекты.
Итак, для начала определимся, что же такое фреймворк и что он из себя представляет.
Фреймворк - довольно широкое понятие, и если верить wikipedia.org он обозначает
"простую концептуальную структуру, используемую для решения сложной проблемной
задачи". Или, что более подходит в нашем случае: Software Framework - каркас программной
системы (или подсистемы). Он может включать вспомогательные программы, библиотеки
кода, язык сценариев и другое ПО, облегчающее разработку и объединение разных
компонентов большого программного проекта. Теперь
определимся, что же будет представлять собой фреймворк для реализации пост-эффектов.
Очевидно, это будет набор C++ классов для реализации постобработки, а также парсер
текстовых файлов, однозначно определяющих сам пост-эффект. Конечно, для полноценного
фреймворка сюда нужно добавить, пожалуй, редактор пост-эффектов. Редактор нужен для
того, чтобы дизайнер эффекта не копался в текстовом файле, а имел возможность
мгновенно наблюдать воочию то, что у него получается. Но написание специализированного
редактора пост-эффектов выходит за рамки данной статьи.
Следует отметить, что подобные фреймворки для реализации пост-эффектов давно
не являются чем-то революционным. Если посмотреть в ресурсных файлах не очень
старых игр, например XPand Rally Extreme, сделанной на движке Chrome Engine,
то можно найти файлы, отвечающие за постпроцессинг. В Chrome Engine эти файлы
имеют расширение .ppfx. Из названия видно сходство с .fx файлами, но очевидно
это их собственный формат, не имеющий к DirectX никакого отношения.
Конечно, для создания эффектов и настройки их параметров проще использовать
специально предназначенные для этого решения, например, такие как Render
Monkey или FX Composer. Но нам в нашем приложении надо каким-то образом
"переваривать" созданный где-либо эффект, и применять его к исходному изображению.
Вот для этого и нужен настоящий фреймворк, он получает на вход файл пост-эффекта
и "преобразует" его в набор взаимосвязанных 3D API объектов внутри приложения,
или, иначе говоря, формирует сам пост-эффект. Затем созданный пост-эффект используется
для обработки исходного изображения.
Архитектура фреймворка
Рассмотрим последовательность процесса постобработки с точки зрения OpenGL и на основании этого попытаемся построить гибкую архитектуру фреймворка, определим необходимые классы и взаимосвязи между ними, построим UML диаграмму классов.
Судя из названия пост-эффект должен применяться к уже отрендеренной сцене. Следовательно, перед постобработкой следует непосредственно сама обработка, или непосредственно рендеринг трехмерной сены. Сначала вся 3D сцена, или ее часть, над которой необходимо выполнить постобработку, выводится в буфер в видеопамяти с последующим использованием полученного изображения как текстуры для постобработки, иначе говоря, производится рендеринг 3D сцены в текстуру Render To Texture (RTT).
Без аппаратной поддержки RTT, в OpenGL таким буфером может быть обычный задний буфер,
располагающийся в видеопамяти ускорителя, с последующим копированием изображения в текстуру.
При имеющейся аппаратной поддержке RTT это может быть либо PBuffer с привязкой к текстуре,
либо же Frame Buffer Object (FBO, объект буфера кадра, расширение GL_EXT_framebuffer_object).
Использование FBO дает серьезные преимущества по сравнению с остальными методами.
Плюс ко всему, при использовании FBO возможен вывод одновременно в несколько цветовых буферов,
так называемый Multiply Render Targets (MRT, расширение GL_ARB_draw_buffers).
Более подробно узнать про использование FBO можно на официальном сайте OpenGL [OGLEXT],
сайтах изготовителей видеокарт, а также из статей [FBO], [GD06a] и [S3DFBO].
Если версия OpenGL не поддерживает расширение Frame Buffer Object, то от идеи
реализации на ней пост-эффектов отказываться не стоит, несложные эффекты она потянет,
но, например, рендеринг в несколько целей - нет.
Поддержка FBO (расширение GL_EXT_framebuffer_object) имеется на видеокартах NVIDIA,
начиная с FX5200 и на ATI, начиная с Radeon 9500. Главное чтобы были установлены свежие
драйвера, так как в некоторых случаях наличие и возможности этого расширения напрямую
зависят от версии драйвера.
После того как мы получили текстуру с исходным изображением, можно выполнить постобработку.
Осуществляется она выводом прямоугольника (Screen Quad) в текущий фреймбуфер с
применением этой текстуры и соответствующего пост-эффекту шейдера.
Далее все зависит от конкретного эффекта. Если эффект несложный,
например необходимо перевести изображение в монохром и добавить
эффект старой кинопленки, то можно осуществлять вывод этого изображения
сразу в задний буфер. Если эффект более сложный, например bloom, и требует
нескольких проходов по изображению/ям, то будут необходимы дополнительные
буферы для ввода - рендер-таргеты (render-targets). Таким образом, количество
и форматы рендер-таргетов зависят от конкретного эффекта, и задаются они на
этапе выполнения. Описать формат и размер рендер-таргета можно при задании
пост-эффекта в текстовом файле.
Итак, определимся с необходимыми классами. Во-первых, желательно
абстрагироваться от конкретного 3D API и завернуть все обращения к
функциям 3D API в отдельные классы-обертки. Благодаря этому система
станет удобнее и гибче, можно использовать один и тот же код загрузки,
настройки и применения пост-эффекта независимо от выбранного 3D API.
Во-вторых, надо определиться с тем, какие объекты будут присутствовать
в процессе постобработки.
Нам понадобятся следующие основные абстрактные классы-обертки,
реализации которых будут уникальны для каждого 3D API. В скобках
указаны имена соответствующих классов.
- текстура (Texture) - представляет собой текстурный объект,
может быть двумерной или кубической текстурой, доступны
функции привязки bind() и задания параметров;
- шейдер (ShaderProgram) - представляет собой шейдерный объект,
доступны функции установки значений uniform переменных, функция
привязки, компиляции и пр.;
- фреймбуфер (FrameBuffer) - или рендер-таргет - объект для
вывода изображения, к нему могут быть привязаны текстуры.
В OpenGL реализуется на основе расширений PBuffer и FBO;
- рендерер (View) - основной класс для настройки состояния
конвейера рендеринга, создания окна, установки видеорежима, а также создания
объектов текстур, шейдеров, фреймбуферов и пр. Этот класс также управляет
установками текущих матриц преобразований, очисткой содержимого буферов,
сменой переднего и заднего буфера и т.п. За изменением состояния рендерера
следит специальный менеджер RendererState, любое обращение к 3D API,
которое изменяет состояние рендерера, должно происходить через этот менеджер.
Это необходимо как для уменьшения кол-ва обращений к драйверу, так и для
отслеживания состояния рендерера. Присутствует также абстрактный класс
RendererConfig, который получает и хранит информацию о возможностях
видеокарты: количестве текстурных модулей, максимальном размере текстур,
количестве MRT и т.п. RendererState и RendererConfig имеют свои
реализации для OpenGL API;
- проход фильтрации (PostprocFilterPass) - содержит все
необходимые в процессе постобработки объекты, такие как текстуры,
фреймбуфер и шейдер для обработки, обеспечивает взаимосвязь между
ними, а также устанавливает и настраивает эти объекты перед постобработкой.
Результатом применения прохода фильтрации к входному изображению/ям,
располагающимся во входных текстурах, является картинка во фреймбуфере,
которую в последствии можно использовать как входную текстуру для
последующей обработки в другом проходе фильтрации;
- фильтр (PostprocFilter) - это совокупность поочередно
применяемых проходов фильтрации, с возможностью регулировать их
последовательность. Или иными словами - это и есть сам пост-эффект,
а точнее его представление в программе;
- компилятор или загрузчик пост-эффектов (PostprocCompiler) - модуль,
отвечающий за загрузку файлов пост-эффектов. На вход получает имя
загружаемого файла, а на выходе - указатель на готовый экземпляр класса
PostprocFilter. Состояние последнего однозначно определяется набором
проходов фильтрации описанным в файле.
Также понадобятся следующие вспомогательные классы:
- менеджер ресурсов (ResourceMgr) и загрузчики таких ресурсов как
текстуры, шейдеры GLSL, материалы и меши (последние 2 типа ресурсов
для фреймворка избыточны, они служат лишь для функционирования
демки пост-эффектов);
- файловая система, система ввода, лог, таймер, системные переменные;
- математическая библиотека;
- набор классов рендерера: вершинные и индексные массивы,
полигональная сетка, материал, uniform-переменные,
источники освещения, пирамида видимости, камера, шрифт,
биллбоард, ограничивающий параллелепипед и прочие необходимые
для работы фреймворка и демки классы.
Далее будут рассмотрены реализации некоторых из этих классов для OpenGL.
Если у вас уже есть часть необходимого функционала в виде готовых
классов текстур, фреймбуфера, шейдеров и пр., или же вы используете
сторонний движок, то конечно желательно использовать готовый функционал,
а сам фреймворк и остальные необходимые классы можно сделать надстройкой
над существующим движком. В прилагаемых исходниках присутствует
весь необходимый функционал. Здесь можно посмотреть реализацию
классов текстур, шейдеров GLSL и многое другое, останавливаться
на этих классах я не буду, а вот реализацию фреймбуфера (рендер-таргета)
а также основных классов отвечающих за постобработку опишу.
На рисунке 1 показана возможная UML диаграмма классов архитектуры фреймворка.

Рис. 1. UML диаграмма основных классов фреймворка
Небольшие пояснения к диаграмме. Классы текстура (Texture)
и шейдер (ShaderProgram) имеют одно сходство - это ресурсы
приложения, и их желательно хранить унифицировано в менеджере
ресурсов (ResourceMgr). Чтобы реализовать такую возможность
эти классы наследуются от абстрактного класса Resource. Ниже
будет представлено более подробное описание некоторых классов.
Теперь немного о том, как это работает.
После создания окна, настройки видеорежима, инициализации
OpenGL рендерера и загрузки 3D сцены, выполняется загрузка и
компиляция файла пост-эффекта:
PostprocFilter * pp_filter;
PostprocCompiler PostProcComp;
...
pp_filter = PostProcComp.compile("post_proc_fx_file.ppf");
При успешной загрузке файла пост-эффекта загрузчик PostProcComp
создаст экземпляр класса PostprocFilter в соответствии с заданными
в файле установками и возвратит указатель на пост-эффект pp_filter.
Далее от нас требуется совсем немного. Сперва перед отрисовкой 3D
сцены нужно вызвать метод pp_filter->begin(&camera); а после
отрисовки - вызвать pp_filter->end(); где camera - объект камеры.
Тогда все то, что будет отрисовываться между этими вызовами
будет автоматически выводиться во входной рендер-таргет объекта
pp_filter, и над этим изображением впоследствии будет выполняться
постобработка. Чтобы выполнить пост-обработку нужно вызвать у
пост-эффекта метод pp_filter->process(). При этом внутри этой
функции автоматически происходит вся постобработка сцены. Обобщая
вышесказанное, привожу следующий фрагмент кода:
...
pp_filter->setSequence("SEQ1");
pp_filter->getFilterPass(0)->getShader()->setUniform("time",timer->getTime());
PostprocFilterPass * pass = pp_filter->getFilterPass("DOWNLUMADAPT0");
if (pass)
pass->getShader()->setUniform("dtime",FPS_counter->getFrameInterval());
pp_filter->begin(&camera);
skyBox->Render(view,&camera);
for (unsigned i=0; i < meshes.size(); i++)
{
meshes[i]->render(view,&camera);
}
water->render(view,camera);
pp_filter->end();
pp_filter->process();
...
Все! Управляться с фреймворком действительно очень просто!
При работе с пост-эффектом даже не нужно задумываться, каким
образом происходит обработка. Фреймворк выступает здесь как
черный ящик, в который следует положить файл пост-эффекта, вызвать
в нужном месте несколько функций и получить на выходе обработанное изображение.
В итоге мы приходим к такой картине: все параметры эффектов,
наличие и форматы рендер-таргетов, последовательность и число
проходов, используемые шейдеры и текстуры - описываются в текстовом
файле как некий сценарий или скрипт и однозначно определяют пост-эффект.
Теперь настало время определиться с примерным синтаксисом такого макроязыка.
Синтаксис файла пост-эффекта
Ниже приведен пример такого файла для конфигурации пост-эффекта.
Комментарии обозначаются символом "!" и комментируют всю строку целиком.
!----------------------------------
! Postproc Filter File
!----------------------------------
mm_heat.ppf
{
! описание используемых входных текстур
input_texture tex0
{
file water_n.dds
}
! описание используемых рендер-таргетов
render_target temp0
{
Width 512
Height 512
int_format RGBA8
texture 0
depth_texture 24
}
! описание используемых шейдеров
shader heat
{
file PostProc/heat.glsl
texture FullSampler 0
texture DepthSampler 1
texture DistortSampler 2
var_float time 0.0
}
! начальный рендер-таргет, в этот рендер-таргет будет
! отрисовываться 3D сцена для постобработки
begin_target temp0
pass p1
{
texture FullSampler temp0 0
texture DepthSampler temp0 depth
texture DistortSampler tex0
target backbuffer
shader heat
}
sequence seq0
{
pass p1
}
}
Сразу оговорюсь насчет выбранной терминологии.
Каждый такой файл будем называть постпроцесс-фильтром
или просто фильтром или же пост-эффектом. Рендер-таргет
или фреймбуфер с текстурой - это по сути одно и тоже. Текстура - текстура,
со своими параметрами и форматом, может быть целью для вывода, единственное,
в этом случае ее размер должен в точности совпадать с форматом того рендер-таргета
к которому она привязана (это ограничение расширения FBO).
Как видно, файл состоит из секций, каждая секция начинается с ключевого слова,
обозначающего тип секции, и имени объекта который она настраивает, а также имеет
тело, заключенное в фигурные скобки. Каждая секция также имеет набор параметров и
настраивает определенный объект. Возможно 5 видов секций, это секция входных текстур
(input_texture), секция настройки рендер-таргетов (render_target), секция настройки
шейдеров (shader), секция настройки прохода фильтрации (pass), и, наконец, секция
последовательности проходов (sequence). Все секции следует задавать именно в том
порядке, в каком они перечислены выше. Самих секций может быть сколько угодно.
Секция input_texture служит для того, чтобы обеспечить механизм объединения
различных фильтров, т.е. создать их комбинацию и обработать уже готовое изображение,
которое находится в input_texture. Также посредством этого можно указать
произвольные текстуры, которые могут использоваться при постобработке.
Произвольная текстура из файла задается внутри этой секции следующим образом:
file file_name.dds, где file_name.dds - имя файла с изображением текстуры.
Секция render_target задает объект рендер-таргета. Эти секции очень специфичны.
Какой при компиляции эффекта создастся объект - зависит от реализации OpenGL,
это может быть как объект PBuffer, так и FBO. Стоит отметить, что на уровне
взаимодействия с другими объектами PBuffer и FBO себя ведут совершенно одинаково,
так как являются реализациями одного интерфейса - FrameBuffer.
Параметры Width и Height, как можно уже догадаться, задают размеры рендер-таргета,
а int_format - внутренний формат текстур рендер-таргета. Для FBO - это формат
текстур цветовых буферов, для PBuffer - формат присоединяемой текстуры. Для каждой
реализации OpenGL есть свой список доступных форматов текстур, поэтому если
данная реализация не поддерживает какой-либо формат рендер-таргета, то можно
выбрать другой подходящий формат. Параметр int_format может принимать
следующие значения:
LUMINANCE8_ALPHA8, LUMINANCE_FLOAT32, LUMINANCE_FLOAT16, LUMINANCE8, ALPHA_FLOAT32, ALPHA_FLOAT16, ALPHA8, RGB_FLOAT32_NV, RGB_FLOAT16_NV, RG_FLOAT32_NV, RG_FLOAT16_NV, R_FLOAT32_NV, R_FLOAT16_NV, LUMINANCE_ALPHA_FLOAT32, LUMINANCE_ALPHA_FLOAT16, RGBA_FLOAT32, RGBA_FLOAT16, RGBA8, RGB_FLOAT32, RGB_FLOAT16, RGB8, INTENSITY_FLOAT32, INTENSITY_FLOAT16, RGBA_FLOAT32_NV, RGBA_FLOAT16_NV
Но это не гарантирует того, что видеокарта поддерживает какой-либо
из них, поэтому перед созданием каждого пост-эффекта нужно заранее
предусмотреть на каких видеокартах какие форматы поддерживаются.
Возможно, придется сделать несколько вариантов этого пост-эффекта
для различных видеокарт.
Параметр texture - задает связанную с рендер-таргетом текстуру.
Для FBO создается и прицепляется текстура, если таких текстур
несколько, то каждая последующая цепляется на свободный цветовой
буфер (цветовой аттачмент). Для PBuffer можно присоединить только
одну текстуру. Параметр depth говорит о надобности создавать еще и
буфер глубины для этого рендер-таргета, единственный его аргумент
- это количество бит точности буфера глубины, оно может быть 16,
24 32. Этот параметр не зависит от конкретной реализации рендер-таргета
и справедлив как для FBO, так и для PBuffer. Параметр stencil - задает
количество бит буфера трафарета. Параметр depth_texture -
указание компилятору пост-эффекта создать и присоединить к FBO
текстуру глубины, аргумент этого параметра - это количество бит
точности текстуры глубины. Задавать этот параметр имеет смысл только
при наличии расширения FBO. Еще один интересный момент при
использовании FBO - это возможность расшаривать рендербуферы между
несколькими фреймбуферами. Это в основном касается рендербуферов
с буфером глубины, когда один и тот же буфер глубины может использоваться
несколькими рендер-таргетами.
Единственное ограничение при использовании PBuffer - это то, что на
него не распространяются установки связанные с наличием нескольких
дополнительных цветовых аттачментов, или аттачмента текстуры глубины,
поэтому если какой-либо пост-эффект подразумевает вывод в несколько
буферов или использует текстуру глубины, то данный эффект просто не
скомпилируется или будет работать неправильно.
В секции shader, описывается имя файла шейдера file, задаются значения
переменных шейдера var_float и указываются используемые текстуры,
если таковые имеются:
texture FullSampler 0
где FullSampler - имя сэмплера внутри шейдера, второй параметр -
номер текстурного модуля, на который будет привязываться текстура.
Переменные шейдера, это uniform переменные в контексте GLSL. Вектора
задаются покомпонентно, через пробелы или символы табуляции. Также
доступно задание массивов uniform переменных. Синтаксис такой:
var_float _array nameVariable [array_size] value0 value1 ...
Далее необходимо указать начальный рендер-таргет, куда
будет производиться отрисовка 3D сцены, он задается как
параметр begin_target и имеет
всегда пустую секцию. Здесь указывается имя начального
рендер-таргета или имя входной текстуры для постобработки.
Секция pass задает проход фильтрации, т.е. операцию
вывода квада с соответствующими шейдером и текстурами в
определенный рендер-таргет. Параметр texture ассоциирует
с именем сэмплера шейдера, текстуру. Она может быть либо
одним из аттачментов какого-либо рендер-таргета, либо
входной текстурой. Вот небольшой пример:
texture FullSampler temp0 0
Первым после слова texture идет имя сэмплера в шейдере,
вторым - либо имя рендер-таргета с текстурой, либо имя
текстуры описываемой в секции input_texture. Следующим
параметром идет номер аттачмента рендер-таргета с которого
нужно взять текстуру. Если вместо номера аттачмента указать
слово depth, то будет использована текстура глубины
присоединенная к рендер-таргету как GL_DEPTH_ATTACHMENT_EXT.
Имя шейдера указывается параметром shader. Параметр target
задает имя рендер-таргета в который будет осуществляться
рендеринг квада, т.е. тот в котором будет формироваться
обрабатываемое в этом проходе изображение. Если параметр
target имеет значение backbuffer, то это означает, что
рендеринг квада будет осуществляться прямо в задний буфер,
т.е. фактически сразу на экран. Последний применяемый в
пост-эффекте проход обязан выводить изображение в задний буфер.
Последовательности (sequences) - это набор применяемых проходов
обработки изображения. Проходы можно чередовать внутри
последовательности и применять несколько раз один и тот
же проход. Благодаря этому можно добиться обработки изображения,
чередуя несколько раз подряд одни и те же проходы.
Этот метод называется пинг-понг, и суть его в следующем:
изображение выступает здесь как аналог мячика для пинг-понга,
оно рендерится попеременно несколько раз, сначала в одну
текстуру, затем изображение в этой текстуре рендерится в
другую текстуру затем опять в предыдущую, и так далее,
туда-сюда. Этим способом удобно пользоваться, когда необходимо
очень дешево размыть изображение. При этом размер второй
текстуры должен быть в два или несколько раз меньше чем размер
первой. Размытие осуществляется за счет билинейной фильтрации
при текстурировании, что довольно дешево, по сравнению с
другими методами блура, например методом Гаусса.
Обращаю ваше внимание еще на один момент, я не ставил перед
собой задачу разработать устойчивую к внешним деструктивным
действиям систему, поэтому если вам вздумается, не соблюдая
синтаксис писать в файле пост-эффекта всякую абракадабру, то
это будет на вашей совести. При желании вы сами можете
добавить в класс компилятора все необходимые проверки на
соблюдение синтаксиса.