надписи в картографическом приложении opengl

Краткая версия

Как я могу рисовать короткие текстовые метки в картографическом приложении OpenGL без необходимости вручную пересчитывать координаты при увеличении и уменьшении масштаба?

Полная версия

У меня есть картографическое приложение на основе OpenGL, в котором мне нужно рисовать наборы данных, содержащие до 250 тыс. Точек. Каждая точка может иметь короткую текстовую метку, обычно длиной около 4 или 5 символов.

В настоящее время я использую один текст, содержащий все символы. Для каждой точки я определяю четырехугольник для каждого символа в его метке. Таким образом, точка с меткой «Fred» будет иметь четыре четырехугольника, связанных с ней, и каждый четырехугольник использует координаты текстуры в этой единственной текстуре для рисования соответствующего символа.

Когда я рисую карту, я рисую сами точки карты в координатах карты (например, долгота / широта). Затем я вычисляю положение каждой точки в координатах экрана и обновляю четыре угловые точки для каждой из четырехугольников меток этой точки, снова в координатах экрана. (Например, если я определю, что точка рисуется в точке экрана 100, 150, я могу установить четырехугольник для первого символа в метке точки, который начинается с левой верхней точки 105, 155 и имеет ширину 6 пикселей и высота 12 пикселей, в зависимости от конкретного символа. Тогда второй символ может начинаться с 120, 155 и т. Д.) Затем, когда все эти четырехугольники символов меток расположены правильно, я рисую их, используя ортогональный экран. проекция.

Проблема в том, что процесс обновления всех этих координат четырех символов идет медленно, занимая около полсекунды для конкретного набора тестовых данных из 150 тыс. Точек (это означает, что, поскольку каждая метка имеет длину около четырех символов, их около 150 тыс. * [ 4 символа на точку] * [4 пары координат на символ] пары координат, которые необходимо установить при каждом обновлении.

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

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

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

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

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


person M Katz    schedule 16.06.2011    source источник
comment
возможный дубликат методики и ожидаемой скорости для разметки текста opengl   -  person datenwolf    schedule 16.06.2011


Ответы (3)


Итак, основная проблема («потому что в противном случае символы станут огромными при увеличении и крошечными при уменьшении») заключается в том, что вы выполняете вычисления в координатах карты, а не в координатах экрана? А если бы вы сделали это в координатах экрана, это потребовало бы дополнительных вычислений? Очевидно, что при любой визуализации необходимо преобразовать координаты карты в координаты экрана. Проблема, похоже, в том, что вы переводите с карты на экран слишком поздно. Таким образом, вместо того, чтобы выполнять одно отображение для каждой точки и затем работать с координатами экрана, вы работаете в основном с координатами карты, а затем переводите для каждого символа в координаты экрана в самом начале конец. И медленная часть заключается в том, что вы работаете в координатах экрана, а затем вам нужно вручную переводить обратно в координаты карты, чтобы сообщить OpenGL координаты карты, и он преобразует их обратно в координаты экрана! Это справедливая оценка вашей проблемы?

Поэтому решение состоит в том, чтобы продвинуть это преобразование раньше в вашем конвейере. Однако я понимаю, почему это сложно, потому что на первый взгляд OpenGL, кажется, хочет делать все в «мировых координатах» (для вас - координаты карты), но не в координатах экрана.

Во-первых, мне интересно, почему вы выполняете отдельные вычисления координат для каждого символа. Какую систему рендеринга шрифтов вы используете? Что-то вроде FreeType автоматически сгенерирует растровое изображение всей строки и не требует, чтобы вы работали с каждым символом [edit: это не совсем так; см. комментарии]. Вам определенно не нужно рассчитывать координаты карты (или даже координаты экрана) для каждого символа. Вычислите координату экрана для верхнего левого угла этикетки, и пусть ваша система визуализации шрифтов создаст растровое изображение всей этикетки за один раз. Это должно ускорить процесс примерно в четыре раза (так как вы предполагаете, что на каждой метке будет 4 символа).

Что касается работы с координатами экрана, может быть полезно немного узнать о шейдерах. Чем больше вы узнаете об OpenGL, тем больше поймете, что на самом деле это вообще не движок 3D-рендеринга. Это просто библиотека 2D-графики со встроенными очень быстрыми матричными примитивами. OpenGL на самом деле работает на самом низком уровне в координатах экрана (не в пиксельных координатах - он работает в нормализованном пространстве экрана, я думаю, из памяти от -1 до 1 как по оси X, так и по оси Y). Единственная причина, по которой кажется, что вы работаете в мировых координатах, - это матрицы, которые вы установили.

Поэтому я думаю, что причина, по которой вы работаете с координатами карты до самого конца, заключается в том, что это проще всего: OpenGL естественным образом выполняет преобразование карты в экран (используя матрицы). Вы должны это изменить, потому что вы хотите работать с экранными координатами самостоятельно, и, следовательно, вам нужно выполнить преобразование задолго до того, как OpenGL получит ваши данные. Поэтому, когда вы собираетесь рисовать метку, вы должны вручную применить матрицу преобразования «карта-экран» к каждой точке, как показано ниже:

  1. У вас есть конкретная точка (для которой нужно нарисовать подпись) в координатах карты.
  2. Примените матрицу "карта-экран", чтобы преобразовать точку в координаты экрана. Это, вероятно, означает умножение точки на матрицы MODELVIEW и PROJECTION с использованием того же алгоритма, что и OpenGL при рендеринге вершины. Таким образом, вы можете glGet GL_MODELVIEW_MATRIX и GL_PROJECTION_MATRIX для извлечения Текущие матрицы OpenGL, или вы можете вручную хранить копию матрицы самостоятельно.
  3. Теперь у вас есть метка карты в координатах экрана, вычислите положение текста метки. Это просто добавление 5 пикселей по осям X и Y, как вы сказали выше. Однако помните, что вы находитесь не в пространстве пикселей, а в нормализованном пространстве экрана, поэтому вы работаете в процентах (например, добавление 0,05 единиц, добавление 5% пространства экрана). Возможно, лучше не думать о пикселях, потому что тогда ваше приложение будет масштабироваться в соответствии с разрешением. Но если вы действительно хотите мыслить в пикселях, вам придется вычислять отношение пикселей к единицам в зависимости от разрешения.
  4. Используйте glPushMatrix, чтобы сохранить текущую матрицу, затем glLoadIdentity, чтобы установить текущую матрицу на идентичность - сообщите OpenGL не трансформировать ваши вершины. (Я думаю, вам придется сделать это как для матриц PROJECTION, так и для MODELVIEW.)
  5. Нарисуйте этикетку в координатах экрана.

Таким образом, вам действительно не нужно писать шейдер. Вы, безусловно, можете сделать это в шейдере, и это, безусловно, ускорит шаг 2 (нет необходимости писать собственный программный код умножения матриц; умножение матриц на графическом процессоре происходит очень быстро). Но это будет более поздняя оптимизация и много работы. Я думаю, что описанные выше шаги помогут вам работать с экранными координатами и не тратить много времени только на то, чтобы указать координаты карты OpenGL.

person mgiuca    schedule 16.06.2011
comment
Прошло некоторое время с тех пор, как я использовал FreeType2, но я не помню API, который позволяет преобразовывать всю строку (в какой кодировке?) В одно растровое изображение. FreeType2 в основном предназначен для получения характеристик и визуализации отдельных глифов; Макет, который представляет собой рендеринг строк, должен выполняться пользователем. Остальные ваши баллы действительны; вы можете сгенерировать сетку для текстовой строки относительно точки, и вам не нужно будет вычислять каждый символ по отдельности; вам просто нужно найти точку в пространстве окна, к которой можно привязать эту метку. - person Nicol Bolas; 16.06.2011
comment
@Nicol Bolas Моя ошибка: я привык к API более высокого уровня, таким как SDL_TTF. Действительно, FreeType позволяет создавать только один глиф за раз. Но вы все равно можете визуализировать его в растровое изображение самостоятельно, прежде чем позволить ему иметь какое-либо отношение к координатному пространству OpenGL. - person mgiuca; 16.06.2011
comment
@mgiuca, спасибо за подробный ответ. К сожалению, я не думаю, что вы обратились к моей проблеме. Я уже делаю то, что вы сказали, обрабатываю все точки, связанные с персонажами, в экранных координатах. Для каждой точки карты я просто выполняю одно вычисление от мира к экрану, чтобы получить координаты точки на экране. Но затем (и это та часть, которая занимает так много времени) мне все еще нужно обновить каждую из экранных координат для каждого квадрата символов метки точки. Дело не в том, что я делаю вычисление от мира к экрану для каждой точки четырехугольника. Но просто добавление небольших смещений пикселей (+5, +12 и т. Д.) - вот что занимает так много времени. - person M Katz; 16.06.2011
comment
@Nicol Bolas, когда вы говорите, что вам просто нужно найти точку в оконном пространстве, к которой можно привязать этот ярлык, вы имеете в виду что-нибудь особенное? Как и в моем комментарии к mgiuca, для меня привязка означает, что мне нужно только один раз выполнить вычисление от мира к экрану для точки карты, а затем координаты четырех символов - это просто небольшие смещения экрана от этого. Но все же мне нужно обновить все эти маленькие координаты четырехъядерных персонажей вручную, верно? Нет возможности их автоматически зацепить, правда? (то есть, чтобы GL обновлял их автоматически, потому что они представляют собой предустановленное смещение от этой вычисленной точки экрана) - person M Katz; 16.06.2011
comment
Эти смещения оконного пространства являются фиксированными. Они не меняются от одного рендеринга к другому. Вы генерируете их, когда впервые генерируете строку, но если содержимое метки не изменится, вам больше не нужно их трогать. У вас просто список квадроциклов. После того, как вы настроили матрицу преобразования так, чтобы вы работали в оконном пространстве, просто установите смещение для матрицы, чтобы расположить себя в месте метки и визуализировать квадраты. - person Nicol Bolas; 17.06.2011
comment
@Nicol Bolas, верно, они исправлены в том, что касается панорамирования карты: когда карта перемещается, мне нужно только обновить одну матрицу, и все они смещаются в нужное место. Но в общем случае масштабирования они не исправлены, правда? - person M Katz; 18.06.2011
comment
@ M Katz Вы хотите увеличить текст? Если нет, то нет. И если вы это сделаете, вы можете просто применить матрицу масштабирования после матрицы перевода. В конце концов, вы на самом деле не регенерируете глифы (не так ли?); вы просто увеличиваете квадрицепсы. Матрицы масштабирования прекрасно справляются с этим. - person Nicol Bolas; 18.06.2011
comment
Я думаю, что @Nicol Bolas прав. Итак, мой вопрос: Рендеринг ли вы метку на текстуру? Если нет, возможно, это решение. Вы должны построить текстуру один раз, когда этикетка станет видимой. Затем покадрово вы беспокоитесь только о том, где нарисовать текстуру на экране; вообще ничего общего со шрифтами или символами. - person mgiuca; 20.06.2011

Боковой комментарий к:

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

Freetype или нет, вы, безусловно, можете вычислить растровое изображение для каждой метки, а не для каждого символа, но для этого потребуется одно из:

  • хранение тысяч различных текстур, по одной для каждой этикетки

    • It seems like a bad idea to store that many textures, but maybe it's not.

    or

  • рендеринг каждой метки для каждой точки при каждом обновлении экрана.

    • this would certainly be too slow.
person Chris Barker    schedule 16.06.2011

Просто чтобы выполнить решение:

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

person M Katz    schedule 01.08.2011