Передача списка значений в fragment-шейдер

Я хочу отправить список значений в шейдер fragmentа. Это, возможно, большой (пару тысяч наименований) список одноточечных поплавков. Фрейд-шейдер нуждается в произвольном доступе к этому списку, и я хочу обновить значения из ЦП на каждом фрейме.

Я рассматриваю свои варианты того, как это можно сделать:

  1. Как однородная переменная типа массива («равномерный float x [10];»). Но, похоже, здесь есть ограничения, на моем графическом процессоре отправка более нескольких сотен значений происходит очень медленно, и мне придется жестко закодировать верхний предел в шейдере, когда я бы предпочел изменить его во время выполнения.

  2. Как текстура с высотой 1 и шириной моего списка, затем обновите данные с помощью glCopyTexSubImage2D.

  3. Другие методы? В последнее время я не соблюдал все изменения в спецификации GL, возможно, существует какой-то другой метод, специально предназначенный для этой цели?

В настоящее время существует 4 способа сделать это: стандартные 1D-текстуры, текстуры буферов, равномерные буферы и буферы хранения шейдеров.

1D Текстуры

С помощью этого метода вы используете glTex(Sub)Image1D чтобы заполнить 1D текстуру вашими данными. Поскольку ваши данные представляют собой всего лишь массив поплавков, ваш формат изображения должен быть GL_R32F . Затем вы получаете доступ к нему в шейдере с помощью простого вызова texelFetch . texelFetch принимает координаты texelFetch (отсюда и название), и он отключает всю фильтрацию. Таким образом, вы получаете ровно один тексель.

Примечание: texelFetch . Если вы хотите использовать предыдущие версии GL, вам нужно будет передать размер шейдеру и нормализовать координату текстуры вручную.

Основными преимуществами здесь являются совместимость и компактность. Это будет работать на аппаратном обеспечении GL 2.1 (с использованием обозначений). И вам не нужно использовать форматы GL_R32F ; вы можете использовать половинные поплавки GL_R16F . Или GL_R8 если ваши данные являются разумными для нормализованного байта. Размер может означать много для общей производительности.

Основным недостатком является ограничение размера. Вы ограничены наличием 1D текстуры максимального размера текстуры. На аппаратном обеспечении GL 3.x это будет около 8 192, но гарантировано будет не менее 4096.

Унифицированные буферные объекты

Как это работает, вы объявляете единый блок в своем шейдере:

 layout(std140) uniform MyBlock { float myDataArray[size]; }; 

Затем вы получаете доступ к этим данным в шейдере, как к массиву.

Вернувшись в C / C ++ / etc, вы создаете объект буфера и заполняете его данными с плавающей запятой. Затем вы можете связать этот буферный объект с единым блоком MyBlock . Более подробную информацию можно найти здесь.

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

Семантически это лучше, потому что это не просто плоский массив. Для ваших конкретных потребностей, если все, что вам нужно, это float[] , это не имеет значения. Но если у вас более сложная структура данных, семантика может быть важной. Например, рассмотрим массив огней. Свет имеет положение и цвет. Если вы используете текстуру, ваш код, чтобы получить положение и цвет для определенного света, выглядит следующим образом:

 vec4 position = texelFetch(myDataArray, 2*index); vec4 color = texelFetch(myDataArray, 2*index + 1); 

С равномерными буферами он выглядит так же, как и любой другой равномерный доступ. Вы назвали элементы, которые можно назвать position и color . Итак, вся семантическая информация есть; легче понять, что происходит.

Для этого существуют ограничения по размеру. OpenGL требует, чтобы реализации обеспечивали как минимум 16 384 байта для максимального размера единых блоков. Это означает, что для массивов с плавающей точкой вы получаете только 4096 элементов. Заметим еще раз, что это минимум, необходимый для реализации; некоторые аппаратные средства могут предлагать гораздо большие буферы. Например, AMD предоставляет 65 536 своих аппаратных средств classа DX10.

Буферные текстуры

Это своего рода «супер-1D-текстура». Они эффективно позволяют вам получить доступ к буферному объекту из блока текстуры . Хотя они одномерные, они не являются 1D текстурами.

Их можно использовать только с GL 3.0 или выше. И вы можете получить к ним доступ только через функцию texelFetch .

Главное преимущество здесь – размер. Буферные текстуры могут быть довольно гигантскими. Хотя спецификация в целом консервативна и требует не менее 65536 байт для текстур буфера, большинство реализаций GL позволяют варьировать их в мегабайтах . Действительно, обычно максимальный размер ограничен доступной памятью GPU, а не аппаратными ограничениями.

Кроме того, текстуры буфера хранятся в объектах буфера, а не более непрозрачных объектов текстуры, таких как 1D-текстуры. Это означает, что вы можете использовать некоторые методы streamовой передачи буфера для их обновления.

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

Объекты буфера хранения шейдеров

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

Шейдерные буферы хранения, концептуально говоря, представляют собой альтернативную форму буферной текстуры. Таким образом, ограничения размера для буферов хранения шейдеров намного больше, чем для однородных буферов. Минимальный минимум OpenGL для максимального размера UBO составляет 16 КБ. Минимальный минимум OpenGL для максимального размера SSBO составляет 16 МБ . Поэтому, если у вас есть оборудование, это интересная альтернатива UBOs.

Просто не забудьте объявить их как только для readonly , так как вы не пишете им.

Потенциальным недостатком здесь является производительность снова, по сравнению с UBOs. SSBO работают как операция загрузки изображения / хранения через текстуры буфера. В основном, это (очень хороший) синтаксический сахар вокруг образа imageBuffer . Таким образом, чтение из них, скорее всего, будет выполняться со скоростью чтения с помощью readonly imageBuffer .

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

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

Это звучит как хороший прецедент для объектов текстурного буфера . Они не имеют большого отношения к регулярным текстурам и в основном позволяют получить доступ к памяти объекта буфера в шейдере как простой линейный массив. Они похожи на 1D-текстуры, но не фильтруются и доступны только с помощью целочисленного индекса, который звучит так, как вам нужно, когда вы называете его списком значений. И они также поддерживают гораздо большие размеры, чем 1D текстуры. Для его обновления вы можете использовать стандартные методы объекта буфера ( glBufferData , glMapBuffer , …).

Но, с другой стороны, они требуют, чтобы аппаратное обеспечение GL3 / DX10 использовалось и даже было сделано kernelм в OpenGL 3.1, я думаю. Если ваше оборудование / драйвер не поддерживает его, то ваше второе решение будет выбранным методом, а скорее использует 1D текстуру, чем ширину x 1 2D-текстуры). В этом случае вы также можете использовать неровную 2D-текстуру и некоторую магию индекса для поддержки списков, больших, чем максимальный размер текстуры.

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

EDIT: В ответ на комментарий Nicol о единых буферных объектах вы также можете посмотреть здесь немногое сравнение двух. Я все еще склоняюсь к TBOs, но не могу понять, почему, только потому, что я понимаю, что это лучше подходит концептуально. Но, может быть, Николь может дать больше информации в этом вопросе.

Один из способов – использовать единые массивы, как вы упомянули. Другой способ сделать это – использовать 1D «текстуру». Найдите GL_TEXTURE_1D и glTexImage1D. Я лично предпочитаю этот способ, так как вам не нужно жестко указывать размер массива в шейдерном коде, как вы сказали, и opengl уже имеет встроенные функции для загрузки / доступа к 1D-данным на графическом процессоре.

Я бы сказал, наверное, не номер 1 .. у вас есть ограниченное количество регистров для шейдерной формы, которая зависит от карты. Вы можете запросить GL_MAX_FRAGMENT_UNIFORM_COMPONENTS, чтобы узнать свой лимит. На новых карточках он достигает тысячи, например Quadro FX 5500 имеет 2048, по-видимому. (http://www.nvnews.net/vbulletin/showthread.php?t=85925). Это зависит от того, какое оборудование вы хотите использовать, и о том, какую другую форму вы хотите отправить в шейдер.

Номер 2 можно заставить работать в зависимости от ваших требований. Извините за неопределенность здесь, надеюсь, кто-то другой может дать вам более точный ответ, но вы должны четко указать, сколько вызовов текстур вы делаете в более старых моделях шейдерных моделей. Это также зависит от того, сколько fragmentов текстур вы хотите сделать для каждого fragmentа, вы, вероятно, не захотите пытаться читать 1000 элементов на каждый fragment, опять же, в зависимости от модели шейдера и требований к производительности. Вы можете упаковывать значения в RGBAs текстуры, предоставляя вам 4 чтения на вызов текстуры, но с произвольным доступом в качестве требования это может вам не помочь.

Я не уверен в номере 3, но я бы предположил, что, возможно, посмотрел на БПЛА (неупорядоченные виды доступа), хотя я считаю, что это только DirectX, без приличного эквивалента openGL. Я думаю, что есть расширение nVidia для openGL, но опять же вы ограничиваете себя довольно строгой минимальной спецификацией.

Маловероятно, что передача 1000 единиц данных в ваш шейдер fragmentа является наилучшим решением вашей проблемы. Возможно, если вы дадите более подробную информацию о том, чего вы пытаетесь достичь, вы можете получить альтернативные предложения?

  • Как сделать снимок экрана в OpenGL
  • загрузчик объектов в opengl
  • Когда я запускаю .jar, я получаю сообщение «Нет lwjgl in java.library.path»
  • Использование OpenGl с C #?
  • OpenGL 2 Texture Внутренние форматы GL_RGB8I, GL_RGB32UI и т. Д.
  • OpenGL определяет положение вершин в пикселях
  • Как использовать GLUT / OpenGL для рендеринга в файл?
  • Как скомпилировать для Windows на Linux с gcc / g ++?
  • Путаница между порядком матрицы C ++ и OpenGL (строка-майор против столбца-майора)
  • Как найти точку мыши в сцене, используя SceneKit?
  • Преобразование вращения кватерниона в матрицу вращения?
  • Давайте будем гением компьютера.