Техника и ожидания скорости для маркировки текста opengl

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

Текстовые метки рисуются путем создания прямоугольника с координатами текстуры для каждого символа и текстурирования прямоугольников с использованием растрового изображения мелкого шрифта (каждый символ имеет размер примерно 5x13 пикселей в текстуре).

В тестовом файле у меня есть около 158 000 точек, заданных по долготе и широте, поэтому это пространство долгота/широта является моим «модельным пространством». Я читаю точки и создаю для них буфер вершин opengl. Затем каждая точка получает метку, которая обычно состоит из трех или четырех символов. Итак, скажем, 3,5 символа в среднем. Точки рисуются в экранных координатах (режим ортопроекции). Для каждого персонажа я создаю прямоугольник координат текстуры, чтобы получить правильные пиксели для персонажа, и я создаю прямоугольник в экранных координатах, в который будет нарисован персонаж. Каждый из этих двух наборов прямоугольников помещается в буфер вершин. Итак, 158k * 3,5 * 8 = 4,4 миллиона точек, или 8,8 миллиона отдельных чисел координат для прямоугольников чертежа, а также 8,8 миллионов чисел для координат текстуры.

Когда приходит время рендеринга, мне нужно (по крайней мере, я считаю, что это единственный способ сделать это) обновить экранные координаты всех этих прямоугольников рисования, чтобы они соответствовали текущему положению на экране всех точек модели. Это означает, что для каждой из 158 точек модели я должен вычислить спроецированные (экранные) координаты из модельных (мировых) координат точки, а затем установить четыре угловых координаты для каждого из трех или четырех символов прямоугольников для точки . Так что в основном я обновляю все 8,8 миллиона этих чисел при каждом рендере. Для обновления этих чисел требуется около 0,3 секунды на рендеринг.

ВОПРОС НОМЕР ОДИН: Звучит ли это как правильный/необходимый способ обработки маркировки точек в opengl? Было бы идеально, если бы был какой-то способ сказать: «автоматически визуализировать в этот набор прямоугольных точек, которые связаны с этой точкой модели, но обрабатываются как смещения экрана от проецируемой точки модели». Тогда мне не пришлось бы обновлять прямоугольники отрисовки при каждом рендеринге. Но ведь такого нет?

ВОПРОС НОМЕР ВТОРОЙ: Помимо времени, необходимого для обновления всех этих прямоугольников экрана перед каждым рендерингом, сам рендеринг занимает около 1 полной секунды, когда на экране отображаются все 158 тыс. просто пытаюсь понять скорости здесь). По мере того, как я увеличиваю масштаб и на экране отображается все меньше и меньше точек/меток, время рендеринга становится пропорционально короче. Я просто пытаюсь понять, звучит ли на моем среднем/современном ноутбуке со средним/современным графическим процессором эта полная секунда как разумное количество времени для рендеринга этих 158k * 3,5 = 553k текстурированных четырехугольников. Я знаю, что люди говорят о том, что «миллионы треугольников» не являются препятствием, но мне интересно, что с текстурированием скорость, которую я вижу, является разумной / ожидаемой.

Спасибо за любую помощь.

Добавлен код ниже. Обратите внимание, что я хотел бы избавиться от вызова position_labels при каждом рендеринге.

SCREEN_VERTEX_DTYPE = np.dtype(
    [ ( "x_lb", np.float32 ), ( "y_lb", np.float32 ),
      ( "x_lt", np.float32 ), ( "y_lt", np.float32 ),
      ( "x_rt", np.float32 ), ( "y_rt", np.float32 ),
      ( "x_rb", np.float32 ), ( "y_rb", np.float32 ) ]
)
TEXTURE_COORDINATE_DTYPE = np.dtype(
    [ ( "u_lb", np.float32 ), ( "v_lb", np.float32 ),
      ( "u_lt", np.float32 ), ( "v_lt", np.float32 ),
      ( "u_rt", np.float32 ), ( "v_rt", np.float32 ),
      ( "u_rb", np.float32 ), ( "v_rb", np.float32 ) ]
)

# screen_vertex_data is numpy array of SCREEN_VERTEX_DTYPE
# texcoord_data is numpy array of TEXTURE_COORDINATE_DTYPE
# not shown: code to fill initial vals of screen_vertex_data and texcoord_data
self.vbo_screen_vertexes = gl_vbo.VBO( screen_vertex_data )
self.vbo_texture_coordinates = gl_vbo.VBO( texcoord_data )

...

# then on each render:

def render( self ):
    self.position_labels()

    gl.glEnable( gl.GL_TEXTURE_2D )
    gl.glBindTexture( gl.GL_TEXTURE_2D, self.font_texture )

    gl.glEnableClientState( gl.GL_VERTEX_ARRAY )
    self.vbo_screen_vertexes.bind()
    gl.glVertexPointer( 2, gl.GL_FLOAT, 0, None )

    gl.glEnableClientState( gl.GL_TEXTURE_COORD_ARRAY )
    self.vbo_texture_coordinates.bind()
    gl.glTexCoordPointer( 2, gl.GL_FLOAT, 0, None )

    # set up an orthogonal projection
    gl.glMatrixMode(gl.GL_PROJECTION)
    gl.glPushMatrix()
    gl.glLoadIdentity()
    window_size = application.GetClientSize()
    gl.glOrtho(0, window_size[ 0 ], 0, window_size[ 1 ], -1, 1)
    gl.glMatrixMode(gl.GL_MODELVIEW)
    gl.glPushMatrix()
    gl.glLoadIdentity()

    vertex_count = np.alen( self.character_coordinates_data ) * 4
    gl.glDrawArrays( gl.GL_QUADS, 0, vertex_count )

    # undo the orthogonal projection
    gl.glMatrixMode(gl.GL_PROJECTION)
    gl.glPopMatrix()
    gl.glMatrixMode(gl.GL_MODELVIEW)
    gl.glPopMatrix()

    self.vbo_texture_coordinates.unbind()
    gl.glDisableClientState( gl.GL_TEXTURE_COORD_ARRAY )

    self.vbo_screen_vertexes.unbind()
    gl.glDisableClientState( gl.GL_VERTEX_ARRAY )

    gl.glBindTexture( gl.GL_TEXTURE_2D, 0 )
    gl.glDisable( gl.GL_TEXTURE_2D )

def position_labels( self ):
    window_size = application.GetClientSize()
    world_size = ( rect.width( application.world_rect ), rect.height( application.world_rect ) )

    world_to_screen_factor_x = float( window_size[ 0 ] ) / float( world_size[ 0 ] )
    world_to_screen_factor_y = float( window_size[ 1 ] ) / float( world_size[ 1 ] )

    wr_lower_left = application.world_rect[ 0 ]
    shift_pixels_x = ( wr_lower_left[ 0 ] + 180.0 ) * world_to_screen_factor_x
    shift_pixels_y = ( wr_lower_left[ 1 ] + 90.0 ) * world_to_screen_factor_y

    # map to screen coordinates
    self.character_coordinates_data.screen_x = ( self.character_coordinates_data.world_x + 180.0 ) * world_to_screen_factor_x - shift_pixels_x
    self.character_coordinates_data.screen_y = ( self.character_coordinates_data.world_y + 90.0 ) * world_to_screen_factor_y - shift_pixels_y

    screen_vertex_data = self.vbo_screen_vertexes.data

    screen_vertex_data.x_lb = self.character_coordinates_data.screen_x + self.character_coordinates_data.screen_offset_x
    screen_vertex_data.y_lb = self.character_coordinates_data.screen_y + self.character_coordinates_data.screen_offset_y - self.character_coordinates_data.screen_height
    screen_vertex_data.x_lt = screen_vertex_data.x_lb
    screen_vertex_data.y_lt = screen_vertex_data.y_lb + self.character_coordinates_data.screen_height
    screen_vertex_data.x_rt = screen_vertex_data.x_lb + self.character_coordinates_data.screen_width
    screen_vertex_data.y_rt = screen_vertex_data.y_lb + self.character_coordinates_data.screen_height
    screen_vertex_data.x_rb = screen_vertex_data.x_lb + self.character_coordinates_data.screen_width
    screen_vertex_data.y_rb = screen_vertex_data.y_lb

    self.vbo_screen_vertexes[ : np.alen( screen_vertex_data ) ] = screen_vertex_data

person M Katz    schedule 05.05.2011    source источник
comment
Фиксированная функция или программируемый конвейер?   -  person genpfault    schedule 06.05.2011
comment
Фиксированная функция. Добавил код выше.   -  person M Katz    schedule 06.05.2011


Ответы (1)


Режим проецирования и экранные координаты — две разные вещи. Конечно, вы можете выбрать параметры проекции так, чтобы единицы OpenGL соответствовали пикселям экрана, но это не обязательно. Просто для уточнения.

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

Второй вопрос: все сводится к скорости заполнения, а не к обработке вещей, которые невидимы. Интересно, что, хотя графические процессоры могут обрабатывать миллионы треугольников в секунду, они лучше всего справляются с этим, если обрабатываются легко усваиваемыми фрагментами, то есть, если они поступают партиями, все они помещаются в кеши. Я обнаружил, что пакеты от 1000 до 3000 вершин работают лучше всего. Также некоторое влияние оказывает общий размер доступной текстуры, а не только часть, к которой вы фактически обращаетесь. Однако ваши цифры звучат разумно для неоптимизированного метода рисования.

person datenwolf    schedule 06.05.2011
comment
Что касается вашего ответа на вопрос 2, можете ли вы дать приблизительное представление об ускорении, которое вы получаете, группируя вершины? - person M Katz; 06.05.2011
comment
Это сильно зависит от рассматриваемого графического процессора; Я видел коэффициенты ускорения от 1,5 до 10. Вы сказали, что создали буфер вершин. Хотите показать нам код, как вы его делаете и как он передается в OpenGL? - person datenwolf; 06.05.2011
comment
Что касается вашего ответа на вопрос 1, я пытаюсь понять, действительно ли для меня существует решение для решения математических задач. Чтобы быть конкретным, представьте двухсимвольную метку в мире (модели) с координатой 100. Предположим, что при начальном отрисовке символ 1 сопоставляется с координатой x 30 на экране, а символ 2 сопоставляется с координатой x 36 (они всегда должны отображать 6 пикселей). отдельно). Затем предположим, что пользователь перемещает 20 единиц мира x и увеличивает масштаб в 2 раза. Есть ли способ отрисовать символы в нужном месте без ручного пересчета и обновления их экранных координат? - person M Katz; 06.05.2011