метки в приложении карты OpenGL - PullRequest
4 голосов
/ 16 июня 2011

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

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

Длинная версия

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

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

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

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

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

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

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

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

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

Ответы [ 3 ]

3 голосов
/ 16 июня 2011

Итак, основная проблема («потому что в противном случае символы будут увеличиваться при увеличении и уменьшаться при уменьшении») заключается в том, что вы выполняете вычисления в координатах карты, а не в экранных координатах? А если бы вы сделали это в экранных координатах, для этого потребовалось бы больше вычислений? Очевидно, что любой рендеринг должен переводиться из координат карты в координаты экрана. Кажется, проблема в том, что вы переводите с карты на экран слишком поздно . Следовательно, вместо того, чтобы создавать по одной карте на экране для каждой точки, а затем работать с координатами экрана, вы работаете в основном с координатами карты, а затем переводите на символ в координаты экрана в самом конце. , И медленная часть в том, что вы работаете в координатах экрана, а затем вынуждены вручную переводить обратно в координаты карты, просто чтобы сообщить 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 (не нужно писать собственный программный код умножения матриц; умножение матриц на GPU чрезвычайно быстро). Но это будет более поздняя оптимизация и много работы. Я думаю, что описанные выше шаги помогут вам работать с экранными координатами и не тратить много времени просто на то, чтобы задать координаты карты OpenGL.

0 голосов
/ 01 августа 2011

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

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

0 голосов
/ 16 июня 2011

Комментарий на стороне:

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

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

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

    • Это кажется плохой идеей хранить столько текстур, но, возможно, это не так.

    или

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

    • это, конечно, будет слишком медленно.
...