Главная
Блог разработчиков phpBB
 
+ 17 предустановленных модов
+ SEO-оптимизация форума
+ авторизация через соц. сети
+ защита от спама

OpenGL в Qt 5.1 – Часть 1 и 2

Anna | 24.06.2014 | нет комментариев

Эта статья является перевод статьи OpenGL in Qt 5.1 — Part 1 и Part 2

Часть 1

Эта статья является первой в серии. Она покажет, как применять OpenGL в Qt 5. В этой статье будет короткий экскурс в историю поддержки OpenGL в Qt. После этого мы перейдем к изложению первой части новых вероятностей, которые вошли в Qt 5.1. Дальнейшие статьи будут содержать изложение большего числа вероятностей, а так же будут приведены несколько примитивных примеров того, насколько легко дозволено применять Qt для работы с OpenGL.

(Дюже) Короткая история Qt и OpenGL

Qt имеет длинную историю поддержки рисования с поддержкой OpenGL. Множество разработчиков Qt имеют представления о QGLWidget и, допустимо, о некоторых OpenGL-based движках. Это разрешает трудиться с «сырым» OpenGL либо применять комфорта которые предоставляет API QPainter. В дополнение к этому, Qt предоставляет несколько пригодных оберток вокруг разных OpenGL объектов: QGLShaderProgram,QGLFramebufferObjectQGLBufferи пр.

При проектировании Qt 5, эти QGL* классы были помечены как «завершенные» («done») и новейшие QOpenGL* классы стали частью модуля QtGui (примечание переводчика — прежде был QtOpenGL). Поводом этих изменений является то, что новая визуализация в Qt Quick 2 основана на OpenGL, и в реальное время является стержневой частью графических предложений Qt. Помимо того, новые QOpenGL* классы могут быть использованы в качестве непосредственной замены ветхих QGL* классов. Теперь рекомендуется применять QOpenGL* классы из библиотеки QtGui.

Qt 5.0 предоставляет (в основном) то же подмножество функциональности OpenGL которое предоставлял Qt 4.8. Это требовалось, в том числе, для функциональных вероятностей, нужных в Qt Quick 2. В дополнение к функциональности Qt 4.8, Qt 5.0 предоставляет средства для легкого создания собственных окн и контекстов OpenGL на всякий платформе. Сейчас не требуется возиться с особенностями разных платформ, Дабы сделать контекст, тот, что может поддерживать OpenGL Core profile. Вы можете легко применять QOpenGLContext и спасти себя от седины!

В Qt 5.1 наши приключения начинаются с демонстрации все большей и большей функциональности OpenGL так, Дабы сделать применение OpenGL и Qt простым, изящный и, верю, веселым! С этой целью, KDAB вложила существенные средства для растяжение границ Qt и OpenGL.

Функции, функции везде!

OpenGL представляет из себя, дерзко говоря, что-то как бы боли, при работе с разными платформами. Одна из основных причин этой боли является надобность назначить адрес точки входа динамически, во время выполнения, а не во время сборки (когда это в состоянии сделать линковщик). Скажем, на Microsoft Windows, адрес всякий функции, введенной в OpenGL начиная с версии 1.1, должен быть назначен во время выполнения. То есть это необходимо сделать примерно для всех функции, используемых в современном OpenGL!

Для решения этих задач Qt предоставляет пару пригодных утилит: QOpenGLContext::GetProcAddress() иQOpenGLFunctions. 1-й может быть использован для ручного назначения точек входа, в то время как конечный является классом, способы которого являются отображением всеобщего подмножества функций OpenGL 2 иOpenGL ES 2. Эти помощники отменны, настоль, насколько это допустимо. Задача в том, что QOpenGLFunctions лимитирован в предоставляемых способах (подмножества OpenGL 2 и OpenGL ES 2). А ручное предназначение точек входа дюже изнурительная работа, чреватая ошибками. В качестве альтернативы дозволено применять функции из сторонних библиотек, решающие эти задачи, таких как Glew либо GLee. Правда эти решения тоже вызывают задачи для приобретения нужной функциональности и комфортной работой с Qt (скажем, контроль порядка хидеров).

Используйте QOpenGLContext::versionFunctions()! Это скромная маленькая функция — ваш проводник в «утопию» адресов (точек входа) функций OpenGL:) Эта функция может быть использована для приобретения указателя на объект (с способами для всякой функции), в нужной версии и профиле OpenGL. Давайте взглянем на примитивный пример. Скажем, у нас есть подкласс QWindow, тот, что осуществляет рендер. Мы хотим сделать OpenGL 4.3 Core profile и пользоваться всеми предоставляемыми функциями. Сделать это дюже легко:

Window::Window(QScreen * screen) : QWindow(screen) {
  // Скажем Qt что мы используем OpenGL для этого окна
  setSurfaceType(OpenGLSurface);

  // Специфицируем формат и создаем платфоро-зависимый сюрфейс
  QSurfaceFormat format;
  format.setDepthBufferSize(24);
  format.setMajorVersion(4);
  format.setMinorVersion(3);
  format.setSamples(4);
  format.setProfile(QSurfaceFormat::CoreProfile);
  setFormat(format);
  create();

  // Создаем OpenGL контекст
  m_context = new QOpenGLContext;
  m_context->setFormat(format);
  m_context->create();

  // Сделаем контекст нынешним для этого окна
  m_context->makeCurrent(this);
  // Получить объект функции и назначить все точки входа
  // m_funcs объявлен как: QOpenGLFunctions_4_3_Core * m_funcs
  m_funcs = m_context->versionFunctions();

  if (!m_funcs) {
    qWarning("Could not obtain OpenGL versions object");
    exit(1);
  }

  m_funcs->initializeOpenGLFunctions();
}

С этого момента мы можем легко применять функции-члены объекта QOpenGLFunctions_4_3_Core. Скажем:

  // Установить Vertex Attrib Divisor
  // Тот, что применяется с instanced rendering
  // (введен в OpenGL 3.3)
  m_funcs->glVertexAttribDivisor(pointLocation, 1);

  // Процесс отправки через compute shader
  // (введенный в OpenGL 4.3)
  m_funcs->glDispatchCompute(512 / 16, 512 / 16, 1);

Как мы видим, довольно легко иметь всю функциональность OpenGL в своих руках для всякий платформы, которая поддерживает его. Помимо того, QOpenGLContextQOpenGLFunctions_4_3_Core и сходственные им классы минимизируют работу для применения функций путем разделение бекэндов, содержащих действующие указатели на функции. Так же, данный подход механически заботится о платформо-зависимых адресах функций (скажем, при применении нескольких потоков и контекстов либо нескольких GPU). Код для этих классов генерируется механически, с поддержкой вспомогательной утилиты, так что его дюже легко обновлять при релизе новой версии OpenGL.

Растяжения OpenGL (OpenGL Extensions)

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

Для применения OpenGL растяжений, необходимо пройти два этапа. Qt помогает с обоими этапами:

Этап 1:

Проверьте, что нынешняя реализация поддерживает требуемое растяжение. Если растяжение вводит новейший API, укажите точки входа. Дабы проверить, что растяжение поддерживается, необходимо применять способ QOpenGLContext::hasExtension(). Помимо того, Дабы получить полный список поддерживаемых растяжений можете применять OpenGLContext::extensions():

// Очередь растяжений
QList extensions = m_context->extensions().toList();
std::sort(extensions);
qDebug() << "Supported extensions (" << extensions.count() <<")";

foreach (const QByteArray &extension, extensions) {
  qDebug() << "    " << extension;
}

Этап 2:

На втором этапе нам понадобилось бы применять нашего ветхого друга — способQOpenGLContext::GetProcAddress(). В Qt 5.1 за это отвечает модуль QtOpenGLExtensions. Данный модуль является статической библиотекой и содержит класс для всякого растяжения OpenGL (которое вводит новое API) из реестра Khronos’а. Дабы применять растяжение OpenGL используйте код, сходственный дальнейшему:

  // Проверка поддержки растяжения
  if (!m_context->hasExtension(QByteArrayLiteral("GL_ARB_instanced_arrays")) {
    qFatal("GL_ARB_instanced_arrays is not supported");
  }

  // Создаем экземпляр вспомогательного класса и разрешаем применение нужной функции
  QOpenGLExtension_ARB_instanced_arrays * m_instanceFuncs = new QOpenGLExtension_ARB_instanced_arrays();
  m_instanceFuncs->initializeOpenGLFunctions();

  // Вызов функции растяжения
  m_instanceFuncs->glVertexAttribDivisorARB(pointLocation, 1);

Как и для основных функции OpenGL, код для растяжений — генерируемый и, в итоге этого, легко обновим в грядущем.

Часть 2

Vertex Array Objects

Qt имеет QOpenGLBuffer (до этого был QGLBuffer), Дабы подмогнуть руководить разными типами буферных объектов OpenGL, таких как per-vertex attribute data и element index buffers. OpenGL также имеет особых контейнер, тип которого именуется Objects Vertex Array (VAOs), помогающий в работе с комплектов vertex buffer object(VBO).

KDAB добавил код для Qt 5.1, тот, что инкапсулирует несколько VAO с классом QOpenGLVertexArrayObject. Связывание экземпляра этого класса говорит OpenGL «запомнить» всякое состоянии спецификации вершин, которое вы хотели бы потом установить. Мы можем позднее восстановить требуемые спецификации состояния дюже стремительно, легко вторично объединяя сам VAO. Это разрешает нам дюже стремительно переключаться между состояниями вершин для «объектов», которые мы хотим отрисовать в нашей функции рендеринга:

Примечание переводчика

На мой взор взамен «связывания», уместнее было бы применять «обрусевший» глагол «биндить», тот, что, допустимо, привычнее слышать разработчикам.
void Scene::initialize() {
  // Представим что мы имеем нынешний QOpenGLContext и
  // m_shaderProgram экземпляр QOpenGLShaderProgram

  // Создаем VAO для рендеринга первого объекта
  m_vao1 = new QOpenGLVertexArrayObject( this );
  m_vao1->create();
  m_vao1->bind();

  // Установка нескольких VBO и IBO (используем QOpenGLBuffer для хранения данных,
  // указание формата, указание использования и т.д.). Это будет "запомнено"
  // с нынешним связанным VAO
  m_positionBuffer.create();
  m_positionBuffer.setUsagePattern(QOpenGLBuffer::StreamDraw);
  m_positionBuffer.bind();
  m_positionBuffer.allocate(positionData,
                            vertexCount * 3 * sizeof(float));

  m_shaderProgram.enableAttributeArray("vertexPosition");
  m_shaderProgram.setAttributeBuffer  ("vertexPosition", GL_FLOAT, 0, 3);

  m_colorBuffer.create();
  m_colorBuffer.setUsagePattern(QOpenGLBuffer::StaticDraw);
  m_colorBuffer.bind();
  m_colorBuffer.allocate(colorData,
                         vertexCount * 3 * sizeof(float));

  m_shaderProgram.enableAttributeArray("vertexColor");
  m_shaderProgram.setAttributeBuffer  ("vertexColor", GL_FLOAT, 0, 3);

  // Повторить для буферов нормалей, текстурных координат,
  // касательных, ...
  ...

  // Создаем VAO для отображения второго объекта
  m_vao2 = new QOpenGLVertexArrayObject(this);
  m_vao2->create();
  m_vao2->bind();

  // Установка нескольких VBO и IBO для дальнейшего объекта
  ...

  // "Промыть и повторить" для других объектов (см. примечание ниже)
  m_skyBoxVAO = new QOpenGLVertexArrayObject(this);
  ...
}

void Scene::render() {
  // Очищаем буферы
  m_funcs->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

  // Объединяем шейдер для первого комплект объектов
  m_phongShaderProgram->bind();
  ...

  // Переключаемся на данные о вершинах первого объекта и рисуем его
  m_vao1->bind();
  m_funcs->glDrawElements(...);

  // Переключаемся на данные о вершинах второго объекта и рисуем его
  m_vao2->bind();
  m_funcs->glDrawElements(...);

  // Мб поменять шейдер и/или текстуру и т.д.
  // И отрисовать остальные объекты
  m_skyboxShaderProgram->bind();
  ...
  m_skyboxVAO->bind();
  m_funcs->glDrawElements(...);
  ...
}

Примечание переводчика

«Промыть и повторить» — саркастический метафора, указывающая на повторение инструкций. Имеет корни от фразы «wash, rinse, repeat», которая указанна на упаковках большинства марок шампуней.

VAO были введены с версии OpenGL 3, но они требуются для OpenGL версии старше 3.1 с Core Profile. Помимо того, VAO доступны как растяжения GL_ARB_vertex_array_object либо GL_OES_vertex_array_object в OpenGL 2 и OpenGL ES 2 соответственно. Класс QOpenGLVertexArrayObject будет применять основную функциональность (если это допустимо) либо обращаться в случае необходимости к растяжению (если таковое имеется).

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

Part 3,4,5 coming soon…PS: Огромная просьба, все стилистические и грамматические ошибки, а так же неточности перевода информировать через ЛС. Все буду править по мере поступления.

 

Источник: programmingmaster.ru

 

Оставить комментарий
Форум phpBB, русская поддержка форума phpBB
Рейтинг@Mail.ru 2008 - 2017 © BB3x.ru - русская поддержка форума phpBB