【问题标题】:Monogame rendering text using Graphics.DrawString (instead of SpriteBatch.DrawString)Monogame 使用 Graphics.DrawString(而不是 SpriteBatch.DrawString)渲染文本
【发布时间】:2023-09-20 01:30:01
【问题描述】:

使用Graphics.DrawString 将一堆(相当静态的)文本渲染到屏幕外位图,将其转换为Texture2D 一次,然后简单地调用SpriteBatch.Draw,而不是使用内容管道,有什么缺点吗?并使用SpriteFont 渲染文本?这基本上是一页文本,绘制在“一张纸”上,但用户也可以选择调整字体大小,所以这意味着我必须包含不同大小的 spritefonts。

由于这是一个仅限 Windows 的应用程序(不打算移植它),我将可以访问所有字体,就像在普通的旧 WinForms 应用程序中一样,我相信使用Graphics.DrawString(甚至TextRenderer) 比使用精灵字体。

此外,似乎性能可能会更好,因为SpriteBatch.DrawString 需要在每次迭代中“渲染”整个文本(即分别发送每个字母的顶点),而通过使用位图我只做一次,所以它应该CPU 方面的工作量会稍微少一些。

  1. 有没有我在这里没有看到的问题或缺点?
  2. 是否可以使用 spritefonts 获得 alpha 混合文本?我看过周围提到的 Nuclex 框架,但它没有被移植到 Monogame AFAIK。

[更新]

从技术方面来说,它似乎工作得很好,文本渲染比通过 sprite 字体好得多。如果它们是水平渲染的,我什至会得到 ClearType。可能存在的一个问题是字体精灵表(可能是?)在纹理内存方面比为一页文本创建单独的纹理更有效。

【问题讨论】:

    标签: monogame drawstring spritebatch spritefont


    【解决方案1】:

    没有

    似乎没有任何缺点
    事实上,您似乎正在遵循文本呈现的标准方法。

    与渲染带纹理的四边形相比,“正确”渲染文本的处理速度相对较慢,即使 SpriteFonts 剪切了所有样条字形,但如果您正在渲染一页文本,那么您仍然可以堆积大量三角形。

    每当我查看 GL/XNA 的不同文本渲染解决方案时,人们往往会推荐您的方法。一次将你的文字墙绘制到可重复使用的纹理上,然后渲染该纹理。

    您可能还想考虑将 RenderTarget2D 作为可移植的可能解决方案。
    举个例子:

    // Render the new text on the texture
    LoadPageText(int pageNum) {
        string[] text = this.book[pageNum];
        GraphicsDevice.SetRenderTarget(pageTarget);
        // TODO: draw page background
    
        this.spriteBatchCache.Begin();
        for (int i = 0; i < text.Length; i++) {
             this.spriteBatchCache.DrawText(this.font, 
                 new Vector2(10, 10 + this.fontHeight * i), 
                 text[i], 
                 this.color);
        }
        this.spriteBatchCache.End();
        GraphicsDevice.SetRenderTarget(null);
    }
    

    然后在场景渲染中,可以spriteBatch.Draw(..., pageTarget, ...)来渲染文字。 这样,您的页面只需要 1 个纹理所有,只要记住如果字体发生变化也要重绘。

    要考虑的其他事项是您的 SpriteBatches 排序模式,有时这可能会在渲染许多三角形时影响性能。

    在第 2 点,正如我在上面提到的,SpriteFonts 是预渲染的纹理,这意味着透明度被烘焙到它们的 spritesheet 上。因此,默认库似乎没有使用透明度/抗锯齿。

    如果您将它们渲染为两倍大并在黑色上渲染为白色,并使用 SourceColor 作为 Alpha 通道,然后将它们按比例缩小并与 Color.Black 混合,您可以将其取回。

    【讨论】:

      【解决方案2】:

      请尝试用指针混色:

      MixedColor = ((Alpha1 * Channel1) + (Alpha2 * Channel2))/(Alpha1 + Alpha2)
      

      【讨论】: