Подтвердить что ты не робот

Передача списка значений в фрагмент-шейдер

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

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

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

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

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

4b9b3361

Ответ 1

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

1D Текстуры

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

Примечание: texelFetch - 3.0+. Если вы хотите использовать предыдущие версии 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/С++/etc вы создаете объект буфера и заполняете его данными с плавающей запятой. Затем вы можете связать этот объект буфера с единым блоком MyBlock. Более подробную информацию можно найти здесь.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Ответ 2

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

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

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

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

Ответ 3

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

Ответ 4

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

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

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

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