1

我正在使用 opengl(固定功能管道),我正在绘制可能数十万个点,并用文本标签标记每个点。这个问题是关于我是否以合理的方式这样做,以及在速度方面我可以期待什么。

通过为每个字符创建一个纹理坐标矩形来绘制文本标签,并使用一个小的字体位图对矩形进行纹理化(每个字符在纹理中约为 5x13 像素)。

在一个测试文件中,我有大约 158,000 个点,以经度和纬度给出,所以这个 lon/lat 空间是我的“模型空间”。我读取了这些点,并为它们创建了一个 opengl 顶点缓冲区。然后每个点都会得到一个标签,通常是三个或四个字符长。所以,假设平均 3.5 个字符。这些点以屏幕坐标绘制(正射投影模式)。对于每个角色,我创建一个纹理坐标 rect 以获取角色的正确像素,并在屏幕坐标中创建一个矩形,角色将被绘制到该矩形中。这两组矩形分别被放入一个顶点缓冲区。所以这是 158k * 3.5 * 8 = 440 万个点,或 880 万个用于绘图矩形的单独坐标数,还有 880 万个用于纹理坐标的数字。

当需要渲染时,我需要(至少我相信这是唯一的方法)更新所有这些绘图矩形的屏幕坐标,以匹配所有模型点的当前屏幕位置。所以这意味着对于 158 个模型点中的每一个,我必须从该点的模型(世界)坐标计算投影(屏幕)坐标,然后为该点的三个或四个字符矩形中的每一个设置四个角坐标. 所以基本上我会在每次渲染时更新所有 880 万个这些数字。每次渲染大约需要 0.3 秒来更新这些数字。

问题一:这听起来像是在opengl中处理点标记的正确/必要方式吗?如果有某种方式说“自动渲染到这组矩形点中,这将是理想的,这些矩形点链接到这个模型点但被视为与投影模型点的屏幕偏移”。然后我就不必在每个渲染上更新绘制矩形。但是没有这样的事情,对吧?

问题二:除了在每次渲染之前更新所有这些屏幕矩形的时间之外,当所有 158k 标签都显示在屏幕上时,渲染本身大约需要 1 秒(这显然不是有用的用户体验,但我只是想了解这里的速度)。随着我放大,屏幕上实际绘制的点/标签越来越少,渲染时间也相应缩短。我只是想了解,在我的普通/现代笔记本电脑上,使用普通/现代 GPU,整整一秒听起来是否是渲染那些 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
4

1 回答 1

0

投影模式和屏幕坐标是两个不同的东西。您当然可以选择投影参数,以便 OpenGL 单位匹配屏幕像素,但这不是必需的。只是为了澄清。

问题一:OpenGL 只是一个绘图 API,没有更高级别的功能。所以,是的,让这些家伙保持同步是你的负担。幸运的是,您只需计算一次。缩放、平移、旋转等都可以通过操纵变换矩阵来完成;但是,视图中的每次更改都需要完全重绘。

问题二:这一切都归结为填充率而不是处理不可见的东西。一件有趣的事情是,虽然 GPU 可以在一秒钟内处理数百万个三角形,但如果以易于消化的块的形式提供,它们会做得最好,即,如果它是分批进入的,则所有这些都适合缓存。我发现,每个批次 1000 到 3000 个顶点效果最好。还有一些影响来自访问纹理的总大小,而不仅仅是您实际访问的部分。但是,对于未优化的绘图方法,您的数字听起来很合理。

于 2011-05-06T12:35:31.683 回答