Лучший подход к построению динамической цепочки фильтров на основе шейдеров OpenGL ES 2.0

Я работаю на iOS 6 (7 тоже, если хотите, и все будет по-другому) и GL ES 2.0. Идея состоит в том, чтобы CAEAGLLayer имел динамическую цепочку фильтров на основе шейдеров, которая обрабатывает его свойство содержимого и отображает конечный результат. Фильтры могут быть добавлены / удалены в любой точке цепочки.

До сих пор я придумал реализацию, но мне интересно, есть ли лучшие способы сделать это. Моя реализация примерно так выглядит:

  1. Базовый класс фильтра, от которого наследуются конкретные фильтры, создавая шейдерную программу (вершина / фрагментная комбинация) для любого фильтра / образа, который они реализуют.
  2. Подкласс CAEAGLLayer, который реализует цепочку фильтров и к которым добавляются фильтры. Алгоритм высокоуровневой обработки:

     // 1 - Assume whenever the layer's content property is changed to an image, a copy of the image gets stored in a sourceImage property.
     // 2 - Assume changing the content property or adding / removing an image unit triggers this algorithm.
     // 3 - Assume the whole filter chain basically processes a quad with position and texture coordinates thru a VBO.
     // 4 - Assume all shader programs (by shader program I mean a vertex and fragment shader pair in a single program) have access to texture unit 0.
     // 5 - Assume P shader programs.
    
     load imageSource into a texture object bound to GL_TEXTURE2D and pointing to to GL_TEXTURE0
     attach bound texture object to GL_FRAMEBUFFER GL_COLOR_ATTACHMENT0 (so we are doing render-to-texture, which will be accessible to fragment shaders)
     for p = program identifier 0 up to P - 2:
        glUseProgram(p)
        glDrawArrays()
    
     attach GL_RENDERBUFFER to GL_FRAMEBUFFER GL_COLOR_ATTACHMENT0 (GL_RENDERBUFFER in turn has its storage set to the layer itself);
     p = program identifier P - 1 (last program in the chain)
     glUseProgram(p)
     glDrawArrays()
    
     present GL_RENDERBUFFER onscreen
    

Этот подход, кажется, работает до сих пор, но есть несколько вещей, которые меня интересуют:

Лучший способ реализовать добавление / удаление фильтров:

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

  1. Присоединение / отсоединение шейдерных пар и повторное связывание одной составной программы вместо добавления / удаления программ. Руководство по программированию OpenGL ES 2.0 говорит, что вы не можете этого сделать. Однако, поскольку настольный GL допускает использование нескольких шейдерных объектов в одной программе, мне все равно любопытно, будет ли это лучше, если ES поддержит его.
  2. Хранение фильтров в текстовом формате (их код внутри функции, отличной от main) и вместо этого скомпилируйте их все в монолитную пару шейдеров (с добавлением main конечно) каждый раз, когда фильтр добавляется / удаляется.

Лучший способ реализовать кэширование для каждого фильтра:

Прямо сейчас, добавление / удаление любого количества фильтров в любой точке цепочки требует повторного запуска всех программ для визуализации окончательного изображения. Было бы хорошо, однако, если бы я мог как-то кэшировать выходные данные каждого фильтра. Таким образом, удаление, добавление или обход фильтра требует только запуска фильтров после точки вставки / удаления / обхода в цепочке. Я могу думать о наивном подходе: на каждом проходе программы, привязывать различные объекты текстуры к GL_TEXTURE0 и к GL_COLOR_ATTACHMENT0кадрового буфера. Таким образом, я могу сохранить вывод каждого фильтра. Однако создание новой текстуры, привязка и изменение вложения кадрового буфера один раз для каждого фильтра кажется неэффективным.

1 ответ

Я не могу сказать много о проблеме кэширования вывода фильтра, но что касается переключения фильтра... Расширение EXT_separate_shader_objects предназначено для решения этой самой проблемы и поддерживается на каждом устройстве, работающем под управлением iOS 5.0 или более поздней версии. Вот краткий обзор:

  1. Существует новый удобный API для компиляции шейдерных программ, который также заботится о том, чтобы сделать их "разделяемыми":

    _vertexProgram = glCreateShaderProgramvEXT(GL_VERTEX_SHADER, 1, &source);
    
  2. Программные конвейерные объекты управляют состоянием вашей программы и позволяют смешивать и сопоставлять уже скомпилированные шейдеры:

    GLuint _ppo;
    glGenProgramPipelinesEXT(1, &_ppo);
    glBindProgramPipelineEXT(_ppo);
    glUseProgramStagesEXT(_ppo, GL_VERTEX_SHADER_BIT_EXT, _vertexProgram);
    glUseProgramStagesEXT(_ppo, GL_FRAGMENT_SHADER_BIT_EXT, _fragmentProgram);
    
  3. Смешивание и сопоставление шейдеров может затруднить привязку атрибутов, поэтому вы можете указать это в шейдере (также для вариантов):

    #extension GL_EXT_separate_shader_objects : enable
    layout(location = 0) attribute vec4 position;
    layout(location = 1) attribute vec3 normal;
    
  4. Униформа устанавливается для шейдерной программы, к которой они относятся:

    glProgramUniformMatrix3fvEXT(_vertexProgram, u_normalMatrix, 1, 0, _normalMatrix.m);
    
Другие вопросы по тегам