javascript之WebGL 中更好的文本质量

java哥 阅读:792 2023-11-05 18:46:41 评论:0

我正在寻找一种在 WebGL 中绘制更好质量(任意)文本的方法。目前我在 2D Canvas 上使用位图字体渲染并将它们块传输到 WebGL 上下文中。

此处描述了此方法 http://delphic.me.uk/webgltext.html

这是我现在知道的在 WebGL 中绘制任意 unicode 文本的唯一解决方案。这种方法的问题是这些是位图字体,在较小的字体大小上看起来块状。我主要使用 18 的字体大小,与桌面质量字体相比,结果非常块状。

我知道threeJS有一个字体库可以生成更好看的文本,但是我不想使用threeJS,因为我有自己的包装器,它可以很好地满足我的需要并且不想增加threeJS的额外开销。

那么如何在 WebGL 中创建质量更好的文本呢?是否有在 Javascript 中提取文本形状以提高质量的方法?

请您参考如下方法:

在使用 Fonts 一段时间后,我可以看到 6 种在 WebGL 中做字体的方法,各有优缺点:

字体作为几何体

  • 获取像 Google 那样的开源字体(Open SansRoboto 非常流行)
  • 使用 OpenType 或类似工具 ( https://nodebox.github.io/opentype.js/ )
  • 读出字体的曲线
  • 对字符曲线进行三 Angular 剖分。我最喜欢的是 Earcut,因为它非常快 https://github.com/mapbox/earcut
  • 将每个字符的多边形绘制为普通的 WebGL 三 Angular 形。

  • 好处
  • 非常快,如果您有很多文本并且需要全功能字体,这是您最好的选择。通过矩阵运算在 GPU 上完成字体缩放。

  • 缺点
  • 渲染字体的质量取决于您的 WebGL 浏览器启用的抗锯齿。 Safari 使用 8x8 AA,看起来不错,但所有其他浏览器只使用 4x4,这可能看起来很块。此外,移动浏览器根本不启用 AA,这使得字体在您的移动设备上看起来非常块状。


  • Canvas

    这也称为“按需动态纹理”。仅将文本字形渲染到(屏幕外) Canvas 并将其作为 WebGL 纹理 blit 到屏幕上,如下所述: http://delphic.me.uk/webgltext.html

    好处
  • 质量不错。

  • 缺点
  • 速度,取决于您需要在什么时间渲染多少文本,这可能会正常工作。特别是如果您只需要呈现静态文本。

  • 游戏帝国时代 III 使用这种方法。

    位图字体

    如果您希望为有限的字符集和固定的字符大小(游戏)提供最高速度和最佳质量,最好创建自己的位图,其中包含要使用的字符,并根据需要将它们 blit 到屏幕上。你可以在互联网上找到很多这样的字符位图。

    这既快速又简单,但限制了您可以使用的语言和字符,但您不会介意例如在游戏中。

    好处:
  • 简单易懂
  • 创建纹理图集很简单
  • 可以使用彩色字体

  • 缺点
  • 放大时看起来非常模糊
  • 必须预渲染所有使用的字形
  • 需要每个字体大小的纹理
  • 需要 texture bin packing以获得最佳纹理使用。 Example


  • 有符号距离字段字体

    Valve 的 Chris Green 写了一本关于使用符号距离场制作纹理的“书”。您需要阅读 2007 年 SIGGRAPH 白皮书 "Improved Alpha-Tested Magnification for Vector Textures and Special Effects

    通常字体纹理图集看起来像 this . SDF 纹理看起来 like .是的,当“按原样”渲染时,SDF 纹理图集看起来很模糊。那是因为 8 位 channel 已被编码为:
  • 0 -> -1.0(外部)
  • 255 -> +1.0(内部)

  • 好处:
  • 单个纹理可用于将字体非常小(6 像素)缩放到非常大(200 像素+)而不会降低质量,
  • 抗锯齿通常是“免费的”。

  • 缺点:
  • 必须预渲染所有使用的字形,
  • 由于 S-L-O-W 预处理(~15 秒),对 SDF 纹理的预处理通常是“离线”完成的,
  • 没有多少人了解如何生成高质量的抗锯齿。黑客如 smoothstep()fwidth()由于人们在纹理空间而不是屏幕空间中进行插值,导致质量较差的缩放结果。 WebGL 的 fwidth() 也无济于事使用错误的常量。
  • 单 channel SDF 不会像 Valve 在他们的论文中暗示的那样保留边缘。他们的解决方案是使用多 channel SDF,但没有提供任何细节。见 Chlumsky Viktor Master thesis或者他的开源msdfgen
  • 如果以小尺寸使用 SDF 字体,则需要“单元格填充”或几个像素的边框。
  • 仅支持单色字体

  • 总而言之,如果您有多种字体,那么 SDF 字体在大小(只需要 1 个)和质量(在小号和大号下看起来都很棒)方面都是一个巨大的胜利

    如何创建 SDF 纹理

    现在有一个易于使用的 npm 模块,可以生成 SDF 纹理和元数据 available here .

    自卫队演示
  • Michaelangel007's SDF vs Bitmap着色玩具演示
  • Konstantin Käfer's MapboxGL SDF Demo
  • SDF subpixel antialiasing demo
  • Multi-Channel SDF shadertoy by P Malin
  • Multi-Channel SDF Texture


  • GPU 上的字体

    人们正在探索在 GPU 上存储三次曲线,并通过让智能片段着色器完成所有繁重的渲染工作来完全绕过纹理。

    blog有截至 2017 年 2 月的字体渲染摘要。

    好处
  • 正常和大尺寸的高质量字形

  • 缺点
  • 高着色器成本和复杂性
  • 小尺寸有质量问题

  • GPU 字体演示
  • 威尔多比的 Vector Texture


  • Canvas 叠加

    对于我当前的项目,我使用 HTML5 2D Canvas 来渲染文本和其他 2D 基元,并使用 WebgGL Canvas 上的透明度将其覆盖。我对由此产生的速度感到惊讶,它在速度和质量方面击败了这里描述的所有其他方法,非常好。

    只要您的文本是静态 2D 并且您不需要任何 3D 转换,这就是我的建议。在我的项目中,这比我以前使用的方法(Font as Geometry)快大约 2 倍。


    标签:JavaScript
    声明

    1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。

    全民解析

    全民解析

    关注我们