Различные форматы вершин и vbo

Я использую цикл рендеринга следующим образом:

  1. Потерять данные и сопоставить буфер.
  2. Запишите команду и запишите сгенерированную вершину в буфер.
  3. Отменить сопоставление буфера.
  4. Перебирайте команды, которые могут изменять состояния, связывать текстуры или рисовать.

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

struct OneSizeFitAllVertex
{
    float pos[3];
    float uv0[2];
    float uv1[2];
    float col[4];
};

При использовании более простого шейдера, который использует, например, только положение и цвет, я бы записал только тот атрибут, который мне нужен, в отображаемой памяти, а код шейдера просто проигнорировал бы все неиспользуемые атрибуты.

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

Простые объекты, отображаемые с помощью простого шейдера, будут использовать SimpleVertex:

struct SimpleVertex
{
    float pos[3];
    float col[4];
};

В то время как другие, мультитекстурированные объекты, будут отображаться с использованием мультитекстурного шейдера и использования MultitextureVertex:

struct MultitextureVertex
{
    float pos[3];
    float uv0[2];
    float uv1[2];
}; 

Как мне обрабатывать эти разные форматы?

Должен ли я записать все вершины разного формата в один и тот же сопоставленный буфер и изменить свои AttribPointers перед рисованием? Это сэкономит немного места.

Должен ли я отображать разные буферы для каждого формата вершин? Возможно, более эффективным.

Или я должен сохранить универсальный формат вершин? Это проще.

Мне любопытно узнать, что является лучшей практикой в ​​​​этой ситуации. Спасибо.


person Regular Guy    schedule 02.06.2017    source источник
comment
Помимо моей любимой рекомендации о том, что делает вашу жизнь проще и быстрее выполняет работу, если переход к множественным структурам вершин приведет к дублированию данных (например, вы будете использовать одни и те же значения позиции в двух отдельных массивах), это было бы плохо. Одна неясная вещь: используете ли вы разные шейдеры для одних и тех же данных (например, если у вас есть объект и вы переключаетесь между разными представлениями, переключая шейдеры), или каждый шейдер имеет свои собственные данные. Однако, если все ваши шейдеры используют данные из одного и того же массива, ваш подход будет самым простым.   -  person radical7    schedule 07.06.2017


Ответы (1)


Может быть много вариантов в зависимости от базовой архитектуры вашей системы, но давайте предположим, что вы используете дискретный графический процессор (например, AMD или NVIDIA) с выделенной графической памятью.

Вы не упоминаете, чередуете ли вы свои атрибуты в массиве структур (AoS) (возможно, что-то вроде следующего):

struct Vertex {
    float position[3];
    float normal[3];
    float uv[2];
    ...
};

или сгруппировать похожие атрибуты вместе (обычно это называется структурой массивов или (SoA))

struct VertexAttributes {
    float positions[N][3];
    float normals[N][3];
    float uv[N][2];
    ...
};

Это актуально, учитывая ваш подход к отображению буфера. Когда вы отображаете весь буфер, GPU, скорее всего, потребуется скопировать свою версию буфера в CPU, который предоставит вам указатель для обновления значений. Когда вы отключите отображение буфера, драйвер скопирует буфер обратно в графический процессор. С макетом AoS и вашей техникой вы будете касаться небольших подразделов всего буфера, а поскольку драйвер графического процессора не знает, какие биты памяти вы обновили, остается только скопировать все это обратно в GPU. В зависимости от размера это может оказать существенное влияние на нескольких уровнях (плохое использование кеша ЦП при чтении, потребление большой пропускной способности шины ЦП-ГП и т. д.). К сожалению, нет хороших альтернатив, если вы обновляете только небольшую часть атрибутов вершин. Однако, если вы обновляете все атрибуты, этот подход подходит (хотя часто рекомендуется использовать glBufferSubData или подобные команды, которые сохраняют считывание обратно из графического процессора в ЦП).

И наоборот, если вы используете подход SoA, отображение всего буфера вызовет аналогичные проблемы, но ситуация может быть лучше. Так как значения для определенного атрибута непрерывны в памяти, вы можете использовать что-то вроде glMapBufferRange для отображения только в той памяти, которая вам нужна (но опять же, я бы по-прежнему рекомендовал использовать glBufferSubData по ранее указанным причинам). Учитывая ваш текущий сценарий, это то, что я бы порекомендовал.

person radical7    schedule 06.06.2017
comment
Я использую AoS, но это не относится к делу. Мой вопрос о том, что мне делать, если у меня есть программа, использующая struct vert_uv { float pos[3]; float uv[2] }; и struct vert_col { float pos[3]; число с плавающей запятой[4] };? Извините за плохое объяснение, я постараюсь обновить свой вопрос, когда найду способ выразить его более четко. - person Regular Guy; 06.06.2017