绘制数千个 SpriteBatch.DrawString() 时的 C# Monogame 性能
C# Monogame Performance when Drawing Thousands of SpriteBatch.DrawString()
我目前正在创建一个大地图,它由许多矩形 (33,844) 组成,它们都有一个唯一的名称(标签),我正在使用 SpriteFont 在它们上面绘制。
绘制所有矩形完全不会影响性能。但是,一旦我尝试用 DrawString() 编写他们所有的标签,我的表现就会一败涂地。
在我的脑海中,我想一次将我所有的矩形和文本绘制到一个纹理中,并且只需要不断地重新绘制整个完成的纹理。我的问题是,这是一张巨大的地图,一些矩形的坐标非常高(例如:一个插槽的 x 是 14869,y 是 23622),并且它们远远大于 Texture2D 所允许的。
因为这是一张地图,我真的只需要画一次整个东西,然后让用户scroll/move围绕它。我不需要不断地重新绘制所有单独的矩形及其标签。
有人遇到过这种情况吗?
尝试只渲染您可以在屏幕上看到的标签,如果您可以向后缩放足够远,就不要渲染它们。
Textrendering 是昂贵的,因为它基本上是创建一个矩形来绘制字体中的每个字符,然后对其应用相同的 RGBA 纹理。因此,根据您编写的字符数,矩形的数量会增加。这意味着每个角色有四个新顶点。
根据您编写的内容,您可以简单地创建一个带有文本的纹理并进行渲染,但它不会非常动态。
编辑:我需要澄清一些事情。
There's no need for me to continually redraw all of the individual rectangles and their labels.
这是错误的。你必须每一帧都画出整个东西。当然,它不会增加内存,但它仍然需要渲染很多,你需要每一帧都渲染它。
但正如我所说:尝试只渲染与屏幕边界冲突的标签和矩形,那么你应该没问题。
有两种方法可以解决您的问题。
您可以将地图渲染到 RenderTarget(2D/3D),也可以剔除屏幕外的 rectangles/text。但是,我不是 100% 确定 RenderTargets 可以达到您需要的那么大,但是您总是可以将您的地图分割成多个较小的 RenderTargets。
关于 RenderTargets 的更多信息,您可能想查看 RB Whitaker 关于它们的文章,http://rbwhitaker.wikidot.com/render-to-texture
如果您熟悉此上下文中使用的术语,剔除意味着仅呈现最终用户可见的内容。有多种方法可以实现剔除。然而,这确实需要您已经实现了一个相机(或某种类型的视图区域)并且您对相机的视口执行矩形的基本轴对齐边界框碰撞(AABB 碰撞,MonoGame 的 Rectangle 开箱即用地支持)并且仅在发生碰撞时渲染它。
剔除的基本实现如下所示:
Rectangle myRect = new Rectangle(100, 100, 48, 32);
public void DrawMapItem(SpriteBatch batch, Rectangle viewRegion)
{
if (viewRegion.Contains(myRect))
{
//Render your object here with the SpriteBatch
}
}
其中 'viewRegion' 是 camera/end-user 实际可以看到的世界区域。
也可以结合这两种方法,将地图渲染到多个渲染目标,然后剔除渲染目标。
希望对您有所帮助!
我目前正在创建一个大地图,它由许多矩形 (33,844) 组成,它们都有一个唯一的名称(标签),我正在使用 SpriteFont 在它们上面绘制。
绘制所有矩形完全不会影响性能。但是,一旦我尝试用 DrawString() 编写他们所有的标签,我的表现就会一败涂地。
在我的脑海中,我想一次将我所有的矩形和文本绘制到一个纹理中,并且只需要不断地重新绘制整个完成的纹理。我的问题是,这是一张巨大的地图,一些矩形的坐标非常高(例如:一个插槽的 x 是 14869,y 是 23622),并且它们远远大于 Texture2D 所允许的。
因为这是一张地图,我真的只需要画一次整个东西,然后让用户scroll/move围绕它。我不需要不断地重新绘制所有单独的矩形及其标签。
有人遇到过这种情况吗?
尝试只渲染您可以在屏幕上看到的标签,如果您可以向后缩放足够远,就不要渲染它们。
Textrendering 是昂贵的,因为它基本上是创建一个矩形来绘制字体中的每个字符,然后对其应用相同的 RGBA 纹理。因此,根据您编写的字符数,矩形的数量会增加。这意味着每个角色有四个新顶点。
根据您编写的内容,您可以简单地创建一个带有文本的纹理并进行渲染,但它不会非常动态。
编辑:我需要澄清一些事情。
There's no need for me to continually redraw all of the individual rectangles and their labels.
这是错误的。你必须每一帧都画出整个东西。当然,它不会增加内存,但它仍然需要渲染很多,你需要每一帧都渲染它。
但正如我所说:尝试只渲染与屏幕边界冲突的标签和矩形,那么你应该没问题。
有两种方法可以解决您的问题。
您可以将地图渲染到 RenderTarget(2D/3D),也可以剔除屏幕外的 rectangles/text。但是,我不是 100% 确定 RenderTargets 可以达到您需要的那么大,但是您总是可以将您的地图分割成多个较小的 RenderTargets。
关于 RenderTargets 的更多信息,您可能想查看 RB Whitaker 关于它们的文章,http://rbwhitaker.wikidot.com/render-to-texture
如果您熟悉此上下文中使用的术语,剔除意味着仅呈现最终用户可见的内容。有多种方法可以实现剔除。然而,这确实需要您已经实现了一个相机(或某种类型的视图区域)并且您对相机的视口执行矩形的基本轴对齐边界框碰撞(AABB 碰撞,MonoGame 的 Rectangle 开箱即用地支持)并且仅在发生碰撞时渲染它。
剔除的基本实现如下所示:
Rectangle myRect = new Rectangle(100, 100, 48, 32);
public void DrawMapItem(SpriteBatch batch, Rectangle viewRegion)
{
if (viewRegion.Contains(myRect))
{
//Render your object here with the SpriteBatch
}
}
其中 'viewRegion' 是 camera/end-user 实际可以看到的世界区域。
也可以结合这两种方法,将地图渲染到多个渲染目标,然后剔除渲染目标。
希望对您有所帮助!