OpenGL: Как спроектировать эффективную систему рендеринга с использованием массивов вершин с глубиной сортировки?
Люди постоянно говорят мне использовать хотя бы Vertex Arrays. Но я думаю, что это не очень хорошая идея, так как я использую glPushMatrix()
с glTranslatef/glRotatef
позиционировать объект в трехмерном мире.
Итак, я должен прекратить использование glPushMatrix()
и вычислите повернутые / перемещенные позиции вершин в мире "вручную", а затем поместите их данные вершин в массив вершин и затем отрендерите их все сразу?
Но все это станет еще более запутанным, когда я использую различные текстуры поверхностей для всех объектов на экране, которые также отсортированы по глубине.
Так:
- Я должен был бы хранить каждый объект с идентификатором поверхности текстуры.
- Я бы отсортировал все видимые объекты по их позиции Z (игра просматривается только сверху вниз, и все объекты плоские).
- Я хотел бы пройти через этот отсортированный массив:
- Создайте новый буфер и скопируйте в этот буфер только вершину /texcoord/color/normal:
- Каждый раз, когда идентификатор поверхности текстуры изменился по сравнению с предыдущим, я буду привязывать к правильному идентификатору текстуры:
- Загрузите данные вершины, которые я собрал.
- Освободите буфер, используемый для временного массива вершин.
- Повторите шаги 4-7, пока я не проверил все данные, которые я отсортировал в первую очередь.
- Освободите данные моего отсортированного массива и повторите шаги 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, давно прошли).
Использование массива вершин никогда не будет менее производительным, чем непосредственный режим. Можно поместить геометрию нескольких объектов в один массив вершин, вы можете разделить их через списки индексов вершин. Списки индексов вершин также могут храниться в буферных объектах.
Затем между рисованием объектов настраивают матрицу вида модели.
Порядок сортировки должен быть следующим:
- разделить на непрозрачные и полупрозрачные объекты. Сортировать непрозрачный
- Сортируйте opqaues по объектам GL (текстура, шейдер, материалы и т. Д.), Поскольку переключение объектов GL является наиболее дорогим.
- Для каждой отдельной группы объектов GL сортируйте рядом с дальним и рисуйте в этом порядке
- Сортировать полупрозрачные далеко и близко и рисовать в этом порядке
Вы не должны пытаться возиться с загрузкой / загрузкой буфера. Просто загрузите данные вершин один раз, а затем просто настройте индексные массивы и порядок их отправки. Не нужно беспокоиться о доступной памяти OpenGL, нет никаких ограничений. Если ваши данные не помещаются в ОЗУ графического процессора, драйвер отвечает за их замену в системной памяти - поэтому вам также следует сначала отсортировать объекты OpenGL, поскольку каждый раз, когда вы переключаете объекты OpenGL (glBindTexture, glBindBuffer и т. Д.), Драйвер может нужно поменять местами, так что вы хотите свести к минимуму эти операции обмена. Объем данных, отправляемых в графический процессор в виде индексных массивов, будет намного меньше, чем минимум, отправляемый вызовами в режиме прямого вызова:
Индексный массив: 16 бит на вершину (16-битные индексы размера наиболее эффективны).
Вызов в немедленном режиме: 4*32 бита на вершину
Вы должны сохранить один массив вершин на объект. Не используйте glTranslatef/glRotatef и т. Д. Вместо этого сверните все в одну матрицу. Используйте эту матрицу для глубокой сортировки ваших объектов. Затем нарисуйте свои объекты спереди назад, нажав матрицу преобразования объектов, нарисовав массив вершин, а затем вытолкнув матрицу преобразования.
Этот подход означает, что вы не будете постоянно создавать и освобождать массивы для хранения данных вершин.
Что касается использования std::vector, большинство реализаций используют внутри себя необработанный массив C, поэтому вы можете сделать &myVec[0], чтобы получить этот массив. Однако не пытайтесь сохранить этот указатель, так как std:: vector может изменить realloc своего массива, и тогда ваш постоянный указатель больше не будет действительным.