unity UGUI源码分析(4)Text与TextMeshPro

这篇具有很好参考价值的文章主要介绍了unity UGUI源码分析(4)Text与TextMeshPro。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

这一篇博客用于分析Text的内容的更新机制,并分析text mesh pro。

首先我们分析Text的文字是如何渲染出来的。

unity UGUI源码分析(4)Text与TextMeshPro

 文章来源地址https://www.toymoban.com/news/detail-421268.html

PupulateWithErrors方法会根据字符串生成顶点数据。其实Text会根据所给定的字符串生成相关的图集,然后对图集进行采样就可以渲染出文字了。由于TextGenerator没有开源,我们从unity UI优化文档上可以找到相关步骤。

protected override void OnPopulateMesh(VertexHelper toFill)
{
    if (font == null)
        return;

    // We don't care if we the font Texture changes while we are doing our Update.
    // The end result of cachedTextGenerator will be valid for this instance.
    // Otherwise we can get issues like Case 619238.
    m_DisableFontTextureRebuiltCallback = true;

    Vector2 extents = rectTransform.rect.size;

    var settings = GetGenerationSettings(extents);
    cachedTextGenerator.PopulateWithErrors(text, settings, gameObject);

    // Apply the offset to the vertices
    IList<UIVertex> verts = cachedTextGenerator.verts;
    float unitsPerPixel = 1 / pixelsPerUnit;
    int vertCount = verts.Count;

    // We have no verts to process just return (case 1037923)
    if (vertCount <= 0)
    {
        toFill.Clear();
        return;
    }

    Vector2 roundingOffset = new Vector2(verts[0].position.x, verts[0].position.y) * unitsPerPixel;
    roundingOffset = PixelAdjustPoint(roundingOffset) - roundingOffset;
    toFill.Clear();
    if (roundingOffset != Vector2.zero)
    {
        for (int i = 0; i < vertCount; ++i)
        {
            int tempVertsIndex = i & 3;
            m_TempVerts[tempVertsIndex] = verts[i];
            m_TempVerts[tempVertsIndex].position *= unitsPerPixel;
            m_TempVerts[tempVertsIndex].position.x += roundingOffset.x;
            m_TempVerts[tempVertsIndex].position.y += roundingOffset.y;
            if (tempVertsIndex == 3)
                toFill.AddUIVertexQuad(m_TempVerts);
        }
    }
    else
    {
        for (int i = 0; i < vertCount; ++i)
        {
            int tempVertsIndex = i & 3;
            m_TempVerts[tempVertsIndex] = verts[i];
            m_TempVerts[tempVertsIndex].position *= unitsPerPixel;
            if (tempVertsIndex == 3)
                toFill.AddUIVertexQuad(m_TempVerts);
        }
    }

    m_DisableFontTextureRebuiltCal

Unity内置的Text组件可以很方便地用于在UI中显示栅格化的文本字形。但是,在使用Text时有很多大家不了解却又经常遇到的与性能相关的因素。当想UI添加文本时,要始终记得——文本字形是作为独立的面片(quad)进行渲染的,每个字符都是一个面片。这些面片通常都含有大量的空白区域围绕着字形,空白区域的大小取决于字形的形状,在放置文本时很容易就会无意中破坏其他UI元素的批处理。

UI文本的网格重建是个重点问题。当Text组件发生变化时,必须重新计算用于显示实际文本的多边形。当Text组件或它的任意级别的父节点被禁用或启用时,也需要进行重新计算。

在含有大量文字标签的UI上,这一行为可能导致问题,例如排行榜页面和统计数据页面。因为在Unity中,最常见的显示和隐藏UI的方法是启用/禁用含有UI的GameObject,含有大量文本组件的UI通常在显示时会导致帧率降低。

那么Text时如何生成图集的?

当全部可现实字符集很大或者在运行时期不确定时,可以用动态字体来显示文本。在Unity的实现中,这些字体在运行时根据Text组件中出现的字符构建一个字形图集(glyph atlas)。

被加载的每个不同的Font对象会维护它自己的纹理集,即使它与其他字体属于同一个字体族。例如,在一个文本控件中使用Arial字体,并且将字体样式(Font Style)设置为粗体(Bold),在另一个文本控件中使用Arial Bold字体,这两个控件会产生一样的输出,但是Unity会维护两个不同的纹理集——一个给Arial,另一个给Arial Bold。

从性能角度看,要理解的最重要的一件事就是,动态字体为每种不同的结合(尺寸、样式&字符)在其纹理集中维护了一个字形。也就是说,如果一个UI中含有两个Text组件,都显示了字符“A”,那么:

  • 如果两个Text组件尺寸相同,那么字体图集中会有一个字形。
  • 如果两个Text组件尺寸不同,那么字体图集中会有两个不同尺寸的字母“A”。
  • 如果一个Text组件的样式是粗体而另一个不是,那么字体图集中会含有一个粗体的“A”和一个普通的“A”。

当使用动态字体的Text对象遇到了没有被栅格化到字体纹理集中的字形时,必须重建字体纹理集。如果新的字形能够加入当前图集,那么将其加入图集并重新上传到图形设备。但是,如果当前的图集太小,那么系统会尝试重建图集。这通过两步完成。

第一步,以相同的大小重建图集,只使用当前在活动的Text组件上显示的字形。这包含了父画布活动(active)但是禁用了CanvasRenderer的Text组件。如果系统成功地将当前使用的所有字形填充进新的图集中,将会栅格化此图集,不再继续进行第二步。

第二步,如果当前使用的字形不能填充进同样大小的图集中,那么会以当前图集大小的短维乘2来创建一个更大的图集。例如,一个512x512的图集或被扩充到512x1024的图集。

因为上述的算法,动态字体集只会在创建时增长一次大小。考虑到重建纹理集的开销,必须时期在重建时最小。这可以通过两种方式实现:

如果可以,使用非动态字体并预先配置对想要使用的字形集的支持。在使用具有良好的字符集约束的UI上,这样做通常效果很好,例如,只是用Latin/ASCII字符并且尺寸范围小的UI。

如果必须支持极其大量的字符,例如整个Unicode集合,那么字体必须设为动态。为了避免可预见的性能问题,使用Font.RequestCharactersInTexture在启动时填充字体字形集。

注意,每个发生变化的Text组件会单独触发字体集重建。当布置极大量Text组件时,将组件内容中全部的不重复字符收集起来并填充进字体集可能有利于提高性能。这样做能够确保字形集只需要重建一次,而不是每次出现新字形时都重建。

另一点需要注意的是,当触发字体集重建时,所有不在当前活动的Text组件中的字符都不会包含进新的图集中

TextMeshPro Text

TextMeshPro(TMP)可以作为Unity中已有的文本组件(例如TextMesh和UI Text)的替代方案。TMP使用Signed Distance Field(有向距离场SDF)作为其首选文本渲染管线,使其可以在任意尺寸和分辨率下清晰的渲染文本。使用一系列自定义的着色器来提升SDF文本渲染的能力后,TMP可以简单的通过修改材质属性来动态地改变视觉效果,例如,放大、外边框、软阴影等,并且可以通过创建材质预设来保存这些效果,在以后重新调用。

下面简述一下啊SDF的原理以及一些简单的应用。TMP基于SDF可以显著提升抗锯齿效果,并且实现一些特效也十分简单。

百度百科上符号距离函数(sign distance function),简称SDF,又可以称为定向距离函数(oriented distance function),在空间中的一个有限区域上确定一个点到区域边界的距离并同时对距离的符号进行定义:点在区域边界内部为正,外部为负,位于边界上时为0。也就是说SDF记录着当前像素点距离某一个区域的最小距离(这个区域我们可以理解为文字,一就是说可以假设像素值为0的点在区域内,像素值为255的点在区域外)。

那么如何生成SDF呢?根据定义SDF记录着当前像素点距离某一个区域的最小距离,最简单的一种办法就是遍历每一个像素点,找到每一个像素点到某一个区域的最小距离。但是这种方法复杂度太高了,对每一个像素点都需要遍历整张图像,设像素点的数量为n,那么总体时间复杂度为n2

可以利用动态规划去减少计算量。状态转移方法可以很容易列出来。

如果当前像素点 img(i,j)的值小于128的时候那么SDF(i,j)=0;

如果当前像素点 img(i,j)的值大于128的时候那么

SDF(i,j)=min{  SDF(i,j-1)+1   SDF(i,j+1)+1   SDF(i-1,j)+1  SDF(i+1,j)+1.414   SDF(i-1,j-1)+1  SDF(i+1,j-1)+1.414  SDF(i+1,j+1)+1.414    SDF(i-1,j+1)+1.414}

边界值需要特殊处理,很容易知道图像的四周边界是不会包含文字的,所以可以假设SDF(边界)=C。C是一个常数。

但是这个状态转移方程需要知道每一个点四周的8个点,一个动态规划是解决不了这个问题的,因为SDF(i,j)需要依赖SDF(i-1,j),而SDF(i-1,j)又需要依赖SDF(i,j),循环依赖了无解。

因此可以考虑使用两次动态规划求解。

第一个动态规划

SDF1(i,j)=min{  SDF1(i-1,j-1)+1.414   SDF1(i-1,j+1)+1.415   SDF1(i-1,j)+1  SDF1(i,j-1)+1}

第二个动态规划

SDF2(i,j)=min{ SDF2(i+1,j-1)+1.414   SDF2(i+1,j+1)+1.414   SDF2(i+1,j)+1  SDF2(i,j+1)+1}

最后SDF(i,j)=min{SDF1(i,j),SDF2(i,j)}这样就解决问题了。

但是上面求出来的SDF的值全为正数,只能统计区域外距离区域边界的距离,那么这么统计区域内部距离区域边界的距离呢?

这个很简单,我们只需要对 255-img再计算一次正向SDF即可。

最后双向SDF=SDF(img)-SDF(255-img)

我们在这里给出了Python代码计算SDF:

from skimage import io
import numpy as np

def dpLU(img):
    SDF=[[0 for i in range(len(img[0]))]for j in range(len(img))]

    for i in range(len(img)):
        SDF[i][0]=128 if img[i][0]>128 else 0
        SDF[i][-1] = 128 if img[i][-1] > 128 else 0
    for i in range(len(img[0])):
        SDF[0][i]=128 if img[0][i]>128 else 0
        SDF[-1][i] = 128 if SDF[-1][i] > 128 else 0

    for i in range(1, len(img) - 1):
        for j in range(1,len(img[0])-1):
            if img[i][j]<128:
                SDF[i][j]=0
            else:
                SDF[i][j]=min(SDF[i-1][j]+1,SDF[i-1][j+1]+1.414,SDF[i-1][j-1]+1.414,SDF[i][j-1]+1)
    return SDF
def dpRB(img):
    SDF=[[0 for i in range(len(img[0]))]for j in range(len(img))]

    for i in range(len(img)):
        SDF[i][0]=128 if img[i][0]>128 else 0
        SDF[i][-1] = 128 if img[i][-1] > 128 else 0
    for i in range(len(img[0])):
        SDF[0][i]=128 if img[0][i]>128 else 0
        SDF[-1][i] = 128 if SDF[-1][i] > 128 else 0

    for i in range(len(img)-2, 0,-1):
        for j in range(len(img[0])-2,0,-1):
            if img[i][j]<128:
                SDF[i][j]=0
            else:
                SDF[i][j]=min(SDF[i+1][j]+1,SDF[i+1][j+1]+1.414,SDF[i+1][j-1]+1.414,SDF[i][j+1]+1)
    return SDF
def GeneratorSDF():
    img=io.imread("xxx.bmp")[:,:,1]
    SDF1,SDF2=dpLU(img),dpRB(img)
    SDF_img1 = [[0 for i in range(len(img[0]) )] for j in range(len(img))]
    for i in range(len(SDF1)):
        for j in range(len(SDF1[0])):
            SDF_img1[i][j] = min(SDF1[i][j], SDF2[i][j])

    img=255-img
    SDF1, SDF2 = dpLU(img), dpRB(img)
    SDF_img2 = [[0 for i in range(len(img[0]) )] for j in range(len(img))]
    for i in range(len(SDF1)):
         for j in range(len(SDF1[0])):
             SDF_img2[i][j]=min(SDF1[i][j],SDF2[i][j])



    SDF=np.array(SDF_img1)-np.array(SDF_img2)
    SDF=SDF+128

    SDF=SDF.astype(np.uint8)

    io.imsave("SDF.png",SDF)
if __name__ == '__main__':

    GeneratorSDF()

最后我们再给出一个简单的示例:

我们自己手写了一个“你”字,大小为128*128

                ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        unity UGUI源码分析(4)Text与TextMeshPro

然后生成SDF

unity UGUI源码分析(4)Text与TextMeshPro

 

把原图进行四倍上采样后

unity UGUI源码分析(4)Text与TextMeshPro

 

而对SDF进行上采样然后在进行阈值分割得到,可以看出效果明显更好了。

unity UGUI源码分析(4)Text与TextMeshPro

 

还可以用SDF对字体进行描边

unity UGUI源码分析(4)Text与TextMeshPro

 

到了这里,关于unity UGUI源码分析(4)Text与TextMeshPro的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Unity UGUI使用Text组件位图字体进行自适应大小

    1.首先我们需要把位图字体导入到unity中,然后利用插件转化一下 我使用的是BMFont 2.然后我们就可以看到生成了四个文件,其中我们主要注意的是.fontsettings文件, 我们主要修改的就是 Character Rect里面的各个参数,至于具体是什么 ,大家有兴趣的可以去搜索, 好了 我们直接上代码 把

    2024年02月05日
    浏览(40)
  • 【Unity-UGUI控件全面解析】| Text文本组件详解

    🎬 博客主页:https://xiaoy.blog.csdn.net 🎥 本文由 呆呆敲代码的小Y 原创,首发于 CSDN 🙉 🎄 学习专栏推荐:Unity系统学习专栏 🌲 游戏制作专栏推荐:游戏制作 🌲Unity实战100例专栏推荐:Unity 实战100例 教程 🏅 欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正! 📆 未来很长

    2024年02月04日
    浏览(34)
  • Unity中UGUI的Text实现超链接点击的解决方案

    1、同一个Text内可以实现多个不同字符区域的点击; 2、适配了中文、英文、韩文、日文、阿拉伯语等,更多语种待测试; 1、文本框内使用富文本,见截图 2、要使该文本的超链接功能生效,调用即可: 可以设置回调的灵活性就比较高了,各位自己扩展 https://github.com/Panda00

    2024年02月07日
    浏览(35)
  • Unity实用功能之UGUI的Text实现颜色渐变详解 Unity3D

    在Unity3D中,UGUI是一种用于创建用户界面的强大工具。其中的Text组件用于显示文本内容,而通过实现颜色渐变效果,可以使文本更加生动和吸引人。本文将详细介绍如何在Unity中使用UGUI的Text组件实现颜色渐变效果,并提供相应的源代码。 首先,我们需要创建一个空的GameObj

    2024年02月02日
    浏览(42)
  • unity ugui text 超链接和下划线,支持部分富文本格式

    unity版本:2021.3.6f1 局限性: 1.测试发现不能使用 size 富文本标签, 2.同一文本不能设置不同颜色的超链接文本 其它:代码中注释掉使用innerTextColor的地方,可以使用富文本设置超链接颜色, 但是下划线是文本本身颜色 项目需要用到该功能, 搜索和参考了很多文章,要么不支

    2024年02月03日
    浏览(33)
  • 【100个 Unity实用技能】☀️ | UGUI Text中加入超链接文本,可直接点击跳转

    老规矩,先介绍一下 Unity 的科普小知识: Unity 是 实时3D互动内容创作和运营平台 。 包括 游戏开发 、 美术 、 建筑 、 汽车设计 、 影视 在内的所有创作者,借助 Unity 将创意变成现实。 Unity 平台提供一整套完善的软件解决方案,可用于创作、运营和变现任何实时互动的2D和

    2024年02月08日
    浏览(37)
  • 【UGUI】TextMeshPro如何配置和使用中文字体

            在Unity中,TextMeshPro与常规 Text 组件相比提供了更高级的文本呈现功能,TextMesh Pro 可以处理各种语言,包括中文。我们可以轻松地在 Unity 项目中使用中文,而不必担心字体和布局问题。 目录 一、配置和使用中文字体步骤 二、警告及解决方案 三、Font Asset Creator属性介

    2024年02月07日
    浏览(44)
  • 【Unity 3D】UI系统中UGUI各个组件的详细讲解(附源码 超详细)

    UI设计又称界面设计,是指对软件的人机交互、操作逻辑、界面美观的整体设计,UI就相当于人可以看到的界面,并且可以对UI进行交互。 Unity  3D的UI,分为UGUI和GUI,UGUI主要是图形渲染界面,搭建方便,学习比较容易,GUI主要是代码渲染界面,需要在编写代码时就思考如何完

    2024年02月03日
    浏览(36)
  • UGUI获取Text宽高

    问题: 版本:2020.3.5 1、Unity获取Text的sizeDetal高度总是不正确,每次都是获取上一次赋值text的sizeDetal结果。 获取当前的sizeDetal 1、下面这种方式是在网上看到的,是预获取Text宽高的值,我测试了下不能正确获取,可能是缺少别的设置。 2、可以直接使用ContentSizeFitter。在Text上添

    2024年02月14日
    浏览(24)
  • 【UGUI】二、Text(TMP) 文本

    点击访问 文本 (Text) 点击观看 Text 轮廓 (Outline) 和 阴影 (Shadow) 组件是额外添加的常用组件,在 UI - Effects 下 Unity中要显示文本,我们一般是用UGUI的 Text 组件,但是 Text 渲染的字体不是矢量的,所以靠近的时候会看到锯齿和模糊;于是乎 TextMeshPro 应运而生,使用它渲染的字体

    2024年02月13日
    浏览(37)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包