OpenGL Qs

Сразу предупреждаю: с OpenGL я пытаюсь работать всего неделю (был еще подход к снаряду, но началось лето и я переключился на МонголиеКарелии), поэтому вопросы у меня, вероятно, глупые и вообще про разное.

Вопросов, собственно, три:

1. Допустим, у меня очень простая сцена, два треугольника, образуют прямоугольник, на них натягиваю текстуру (собственно, картинку, которую хочу показать).

Вопрос: есть ли смысл связываться с VBO или 6 пар вызовов glTexCoord2f()/glVertex2f() не будут заметно медленнее? А если прямоугольник не один, а, к примеру, 64 (картинка 8000x8000, а на предельный размер текстуры я заложусь как 1024x1024), будет ли заметный выигрыш, если загнать все в буфер(ы) и дергать рисование меняя только индексы?

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

2. В Qt5 есть (старые) QGL*-объекты, есть новые QOpenGL*.

Кто бы рассказал, в чем разница....

3. Ну и вообще, Qt-шные примеры, что из комплекта, что найденные в сети, какие-то частично безумные. То слишком простые, то вроде простой - а шейдеры там внезапно "#version 330", то - очень хороший пример boxes из поставки - но переусложненный, начинаешь от него куски отпиливать и все разваливается.

Нет ли каких-то Qt-OpenGL tutorials, которые бы с одной стороны были бы "современными" (в смысле используемых Qt-интерфейсов), интересными (не банальный QGLWidget::paintGL()), но и не слишком сложными.

Comments

А можно вопрос - а в книжка-то нашлась? Без QT, а для общего развития.

PS: из общих соображений - если что-то можно хранить в карте, то лучше там. И рисовать оттуда. Но это, естественно, КО :)

Мне понравились оффициальные книжечки красная и оранжевая.
http://www.amazon.com/dp/0321481003/ref=as_li_tf_til?tag=flazx-20&camp=1...

http://www.amazon.com/dp/0321637631/ref=as_li_tf_til?tag=flazx-20&camp=1...

Книжка то нашлась и не одна. Ну то есть по OpenGL3 - я уже писал, но я в процессе понял, что на 3-ю версию лучше не рассчитывать, поэтому банально OpenGL Superbible, 4-е издание по Opengl 2.1

> есть ли смысл связываться с VBO или 6 пар вызовов glTexCoord2f()/glVertex2f() не будут заметно медленнее?
Заметно медленнее не будет. Может на телефоне разница и была бы, но и то сомнительно.
На десктопе даже фуфельная карточка может много сотен раз показать полно экранную текстурку.
По скольку VBO сидит в памяти то драйверу не нужно пересылать несколько сотен байт геометрии. Но это копейки даже если делать каждый кадр.

VBO не связаны особым образом с шейдерами. Просто glTexCoord2f это старый API, вроде даже deprecated с opengl3.0. но я вроде об этом писал в старом топике.

Туториалы хорошие Nehe. Там вроде постепенно все рассказывают.
Про qtopengl не вкурсе, но думаю разницу быстрее глянуть в сорцах.

Я уже в той стадии непонимания, когда собственно OpenGL-ные примеры (которые у всех на GLUT) уже примерно понимаю (в том объеме, который мне нужен, про освещение, к примеру, вовсе не читал) и ступор вызывает уже взаимодействие с Qt.

Ну вот, к примеру, у меня как и у этого чувака не работает glDrawRangeElements с нулевым последним параметром (указателем на буфер индексов). Значит какой-то буфер я не прибиндил (или как-то так).

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

Так не используйте кьютишные врапперы. Они обычно тонкие, но там наверняка баги есть. Хотя вот OpenCL врапперы мне очень понравились.
В opengl много способов сделать одно и тоже. И с этим сложно что то сделать.

Не, ну как-то глупо их не использовать, если Qt используется во всей остальной программе.
Там же всякие хорошие вещи сделаны, memory management, работа со встроенными типами (вроде QImage), хочется их. Без них - да, тоже можно, но только с большой тоски.

Ну и тут, конечно, есть приколы. Вот если у меня Vertex buffer надо обновить (картинку повернули - размеры поменялись_, то что делать:
- старый убить, новый завести
- или как-то поапдейтить старый.

Документация не дает ответа, исходники - частично дают, но примеров очень хочется.

P.S. Лишний каммент прибил

> Вот если у меня Vertex buffer надо обновить (картинку повернули - размеры поменялись_, то что
> делать:
> - старый убить, новый завести
> - или как-то поапдейтить старый.
Не, это надо матрицами делать. В вертекс шейдере делают обычно 3 матрицы model, view, projection.
Projection - это понятно, и данном случае похоже не нужно
view - обычно это камера
model - преобразование вращения, переноса, маштабирования объекта который рендерим.
Матрицы перемножают с вертексами. Получается "картинку повернули - размеры поменялись".

Хм. Ну с поворотом - да, можно матрицами. Ну то есть сейчас у меня на картинку (background) наложен QGraphicsRectItem через который я получаю все эвенты (правую кнопку мыши нажали или там еще что), мне удобнее чтобы и он и картинка были в одних координатах, ну да и этот Item можно тоже повернуть той же матрицей. Другой способ смотреть на мир, хорошо.

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

Экспериментами выясню, конечно.

> Документация внятного ответа не дает.
А по-моему очень даже дает:
http://doc.qt.digia.com/qt/qglbuffer.html#Access-enum
http://doc.qt.digia.com/qt/qglbuffer.html#UsagePattern-enum
http://doc.qt.digia.com/qt/qglbuffer.html#write

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

Это означает, что у меня полной картины в голове нет - и я прочитать написанное не могу.
А это и есть правда, хожу по этим OpenGL-ям пока с палочкой.

Т.е. получается, что если я собираюсь обновлять буфер, то ему надо QGLBuffer::DynamicDraw, а содержимое менять, к примеру, через вызовы allocate (а не через write() - чтобы не думать о том, сколько у меня в буфере места и влезет ли). Не максимально эффективно, но если вершин в пределах пары сотен, то вроде и хрен с ним?

Верно я мыслю?

> Т.е. получается, что если я собираюсь обновлять буфер, то ему надо QGLBuffer::DynamicDraw, а
> содержимое менять, к примеру, через вызовы allocate (а не через write() - чтобы не думать о
> том, сколько у меня в буфере места и влезет ли).

Я так никогда не делал. Обновлял только текстурки. Но судя по докам, надо выставить DynamicDraw и WriteOnly. Я бы как раз использовал Write, он же не трогает остальные кусочки буффера и теоретически не обязан их заливать в видео память. Но есть ли соответствующие ф-ции в OpenGL (что бы обновить кусочек VBO) я не знаю, тут бы в исходнички заглянуть, но строить предположения веселее.

> Не максимально эффективно, но если вершин в пределах пары сотен, то вроде и хрен с ним?
Гонять туда сюда вертексы не эффективно. Кстати, тут тоже самое что и с CUDA. Ну вот 128 треугольничков, это 128 * 3 (для простоты) флоатов по 4 байта. То есть 1536 байт. Скопировать их в видео память можно за 1536байт / 5гбайт в сек. То есть очень быстро.

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

Аналогия же с CUDA должна рассматриваться более глубоко
- если каждый glVertex вызывает отдельную транзакцию с видеокартой - то это жопа (относительная, конечно, порядка микросекунды или даже половины на транзакцию)
- если glVertex вызывает переключение контекста (в драйвер), но драйвер все собирает в кучу и отправляет одной транзакцией по PCIe, то дело лучше, хотя и контекст переключается наносекунд 100, не меньше.
- если драйвер собирает все эти glVertex в пачку в юзерспейсе и транзакция одна - то время мы тратим только на вызовы библиотеки и без переключения контекстов - и это совсем ерунда.

Т.е. вот с CUDA - скопировать 100 мегабайт за один вызов (да еще и асинхронно, по DMA) - ерунда, а за 100 млн вызовов - не ерунда.

> write() - не ресайзит буфер (как я понимаю) т.е. придется думать о его размере.
> Можно, наверное, аллоцировать с очень большим запасом - и не думать.
А разве размер геометрии меняется?

Я не видел как устроены драйвера opengl, но думаю они собирают вызовы glVertex в буфер и копируют по glEnd. Ф-ции заливки чего либо, должны как мне кажется отрабатывать сразу.
Энивэй это будет быстро.

Размер геометрии меняется если меняется размер картинки (загрузили и показываем другую).
Потому что тайлы же.

Я в Qt применительно к OpenGL не особо, может там как-то все не так, но обычно экранные координаты переводят в координаты сцены с помощью gluUnProject().

Да зачем вам для 4 точек какие-то буфера для вершин применять? Может проще, э-э, захардкодить? Прямо между glBegin() и glEnd() glVertex() и фсе

Захардкодить не получится - тайлы же. Не везде предельный размер текстуры 64kx64k :).

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

А glu - грубо говоря нету (ну то есть можно собраться с, но вроде незачем). Зато есть многочисленные Qt-шные примитивы для матриц 4x4 и подобного.

Ну можно же не настолько хардкодить. Проще, наверное, псевдокодом:

void
draw_scene()
{
/* Очищаем буферы и выставляем матрицы */
glTranslate(куда-то смещаемся);
glScale(и масштабируемся);
/* ... */
glBegin(GL_TRIANGLES /* например */);
/* Здесь рисуем точки тайлов, вяжем их к текстурным координатам и т.п. */
glEnd();
/* ... */
}

В gluUnProject() строчек 20, можно и с собой потаскать. Хотя, в Qt, возможно, удобнее сделано.

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

С версии 3.0 это все deprecated. И вроде как я видел где то, что в пятой версии fixed pipeline уберут, но найти не могу.

Да, с матрицами клевее сильно!

64 текстуры (128 треугольников), наверное, даже софтварный OpenGL более-менее быстро будет рисовать. На современных видеокартах вроде бы миллионами (десятками миллионов?) полигонов в секунду меряются.

До-шейдерный OpenGL простой как грабли: выставляем матрицы, рисуем картинку. И так на каждое событие перерисовки. Если нужно помасштабировать и перенести, проще всего изменить матрицу перед отрисовкой (glTranslate, glScale), координаты точек оставить те же

Как я уже писал в мае, в софтверном OpenGL (который внутри VMWare workstation) меня вообще все потрясло.

Ну то есть там (в Qt) пример с кубиками (6 кубиков, на гранях - 6 разных текстур)
- WinXP в VMWare: первые 5 текстур куда-то подевались, отображается только результат последнего glBindTexture и только на одной грани
- Win7 в той же виртуальной машине - результат последнего bindTexture отображается на всех гранях кубика.

Смешно было.

Вопрос же мой в другом. Понятно что отрисуется "быстро".
Но будет ли значимая разница между
а) glVertex/glTexCoord на каждый угол и каждую перерисовку (а перерисовка происходит часто)
б) загонянием всего в буфер и перерисовкой буфера уже одной командой.

Значимая - это "на профайлере что-то видно".

У меня на имеющих смысл размерах текстур VBO были быстрее в какие-то совершенно адские количества раз. Но это было давно и может быть уже неправда.

зачем закладываться на 1Кх1К? вроде ж все умеют 4К как минимум?

У тебя вершин было мало, текстуры были большие и VBO были быстрее, я правильно понял?

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

ну если ты помнишь, то задача у меня была ровно та же самая, только текстуры были не только большие (поменьше чем у тебя), но еще и много (24 в секунду). ВБО были очень быстрее.

Извини за занудство, но именно VBO или PBO?

Потому что я значимой разницы между VBO и просто передачей (всех 6) вертексов за раз - не вижу.
А вот glTexSubImage2D - действительно медленная. Типа, 15мс на 0.7 мегапикселя.

Пиксельные буферы еще не пробовал, вот собираюсь.

Блин, я тупой, конечно ПБО. Кто о чем, а вшивый все о бане.

А чего тебя вообще в ВБО понесло-то? Чтобы ВБО имело смысл, надо тыщи вертексов иметь, если не десятки тыщ.

Понесло - потому что интересно. Если я уж до этого места добрался, то надо все попробовать. Уже наступил на множество граблей, радуюсь.

Про PBO: ты случайно не помнишь, у тебя улучшалась "синхронная" производительность (от появления кадра в userspace до показа) или же ты просто асинхронно грузил "следующий кадр" пока показывался предыдущий (т.е. имел кольцевой буфер из PBO, по сути)?

мне же поток надо было показывать, поэтому у меня был буфер. Реально, по-моему он свелся всего к двум - один на экране, во второй гружу и распаковываю. Вернее грузил и тусовал я в чисто юзерспейсовые буфера, никакого отношения к ПБО не имеющие, а финальный проход распаковки как раз срал прямо в "теневой" ПБО. А потом флип по таймеру.

Но ведь, казалось бы, можно делать glTexImage из буфера в отдельном треде. И иметь две текстуры, которые и флипать.
Или PBO-transfer - сильно быстрее, чем трансфер текстуры?

Вопросы риторические, ибо я сам все попробую :)

там были какие-то грабли с многотредовым доступом, во-первых, и таки да, _намного_ быстрее.

>зачем закладываться на 1Кх1К? вроде ж все умеют 4К как минимум?

Оказалось интересно. Если потайлить 500x500 (надо, конечно, 512x512, но я специально некруглое беру), то вместе с PBO получается выиграть процентов 10. Т.е. приготовил первый буфер, пока он льется в карту - готовим следующий.