OpenGL: Как спроектировать эффективную систему рендеринга с использованием массивов вершин с глубиной сортировки?

Люди постоянно говорят мне использовать хотя бы Vertex Arrays. Но я думаю, что это не очень хорошая идея, так как я использую glPushMatrix() с glTranslatef/glRotatef позиционировать объект в трехмерном мире.

Итак, я должен прекратить использование glPushMatrix() и вычислите повернутые / перемещенные позиции вершин в мире "вручную", а затем поместите их данные вершин в массив вершин и затем отрендерите их все сразу?

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

Так:

  1. Я должен был бы хранить каждый объект с идентификатором поверхности текстуры.
  2. Я бы отсортировал все видимые объекты по их позиции Z (игра просматривается только сверху вниз, и все объекты плоские).
  3. Я хотел бы пройти через этот отсортированный массив:
  4. Создайте новый буфер и скопируйте в этот буфер только вершину /texcoord/color/normal:
  5. Каждый раз, когда идентификатор поверхности текстуры изменился по сравнению с предыдущим, я буду привязывать к правильному идентификатору текстуры:
  6. Загрузите данные вершины, которые я собрал.
  7. Освободите буфер, используемый для временного массива вершин.
  8. Повторите шаги 4-7, пока я не проверил все данные, которые я отсортировал в первую очередь.
  9. Освободите данные моего отсортированного массива и повторите шаги 1-9.

Я делаю это правильно?

Кроме того, как я должен проектировать структуру данных для объектов, которые я буду сортировать? Например, это хорошо использовать std::vector хранить данные вершин для каждого объекта? Или есть лучшая альтернатива? Я думал, что std::vector хранить все эти данные будет выглядеть так:

struct GameObject {
    int TexID;
    float z; // we will sort by this
    vector<VTCNStruct> VertexData; // store each point of the object here (including color/normal/texcoord points).
};

vector<GameObject> GameObjectBuffer; // push all sortable objects here

Также на шаге 4: возможно ли использовать уже существующие std::vector в этом случае? У меня была идея, что я должен использовать сырые массивы, как new float[100] для отправки массива вершин в мой графический процессор, или я могу каким-то образом использовать уже существующие отсортированные std::vector здесь как-то (эффективно) без создания нового буфера каждый раз при изменении идентификатора текстуры?

2 ответа

Просьба отказаться от glBegin/glEnd сейчас, он устарел и был удален из OpenGL-4. Вам определенно следует использовать массивы вершин, даже лучше, если вы используете объекты буфера вершин.

В непосредственном режиме (glBegin/glEnd) драйвер OpenGL должен создавать массив вершин из вызовов функций. Поскольку неясно, сколько вершин поступит, в конечном итоге будет перераспределяться память несколько раз (времена, в которые графические процессоры непосредственно выполняли вызовы между glBegin/glEnd, давно прошли).

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

Затем между рисованием объектов настраивают матрицу вида модели.

Порядок сортировки должен быть следующим:

  1. разделить на непрозрачные и полупрозрачные объекты. Сортировать непрозрачный
  2. Сортируйте opqaues по объектам GL (текстура, шейдер, материалы и т. Д.), Поскольку переключение объектов GL является наиболее дорогим.
  3. Для каждой отдельной группы объектов GL сортируйте рядом с дальним и рисуйте в этом порядке
  4. Сортировать полупрозрачные далеко и близко и рисовать в этом порядке

Вы не должны пытаться возиться с загрузкой / загрузкой буфера. Просто загрузите данные вершин один раз, а затем просто настройте индексные массивы и порядок их отправки. Не нужно беспокоиться о доступной памяти OpenGL, нет никаких ограничений. Если ваши данные не помещаются в ОЗУ графического процессора, драйвер отвечает за их замену в системной памяти - поэтому вам также следует сначала отсортировать объекты OpenGL, поскольку каждый раз, когда вы переключаете объекты OpenGL (glBindTexture, glBindBuffer и т. Д.), Драйвер может нужно поменять местами, так что вы хотите свести к минимуму эти операции обмена. Объем данных, отправляемых в графический процессор в виде индексных массивов, будет намного меньше, чем минимум, отправляемый вызовами в режиме прямого вызова:

  • Индексный массив: 16 бит на вершину (16-битные индексы размера наиболее эффективны).

  • Вызов в немедленном режиме: 4*32 бита на вершину

Вы должны сохранить один массив вершин на объект. Не используйте glTranslatef/glRotatef и т. Д. Вместо этого сверните все в одну матрицу. Используйте эту матрицу для глубокой сортировки ваших объектов. Затем нарисуйте свои объекты спереди назад, нажав матрицу преобразования объектов, нарисовав массив вершин, а затем вытолкнув матрицу преобразования.

Этот подход означает, что вы не будете постоянно создавать и освобождать массивы для хранения данных вершин.

Что касается использования std::vector, большинство реализаций используют внутри себя необработанный массив C, поэтому вы можете сделать &myVec[0], чтобы получить этот массив. Однако не пытайтесь сохранить этот указатель, так как std:: vector может изменить realloc своего массива, и тогда ваш постоянный указатель больше не будет действительным.

Другие вопросы по тегам