【Unity3D】UGUI Canvas画布渲染流程

目录

Screen Space - Overlay

Screen Space - Camera

World Space  

UI合批分析(建议不看 直接看FrameDebugger测试)

优化UI合批

1、Image图片纹理不同导致合批失败

2、文本和图片相交以及排序对合批的影响

3、Mask对合批的影响(情况很复杂)

4、Canvas对合批的影响


参考文档:画布 - Unity 手册 

Canvas组件:画布组件是进行 UI 布局和渲染的抽象空间。所有 UI 元素都必须是附加了画布组件的游戏对象的子对象。

Screen Space - Overlay


        参数:
        Render Mode 渲染模式:Screen Space - Overlay、Screen Space - Camera、World Space。
        Pixel Perfect:是否应该无锯齿精确渲染 UI?
        Sort Order:渲染层级
        Target Display:输出屏幕目标Display 1
        Additional Shader Channels:额外的着色器通道

        画布渲染于所有物体上方,即最后渲染。并且不归于摄像机渲染,因此即使没有摄像机也能渲染出UI。
        
        画布宽高跟随屏幕宽高,画布大小固定(1,1,1),位置屏幕居中,覆盖整个屏幕。
        画布下的子UI需使用锚点适配来适应不同大小的屏幕,因屏幕变化后画布也会变化。

Screen Space - Camera

        画布内容归于摄像机进行渲染。与正常渲染物体一样。       
        画布宽度和高度跟随屏幕。大小会根据UI摄像机参数而适配变化。

Render Camera: UI摄像机
Plane Distance:画布距离UI摄像机的长度
Sorting Layer:可自定义大层级
Order In Layer:子层级

观察Frame Debugger分析
        
        由于有2个摄像机,因此有2个Render.OpaqueGeometry以及2个Camera.RenderSkybox。若将UI摄像机的Clear Flags从Skybox改为DepthOnly,则会减少1个Camera.RenderSkybox。
        UI渲染主要位于Render.TransparentGeometry中
        

Image默认材质着色器UI/Default会将渲染目标交到TempBuffer 355 1920*1080目标,着色器是支持SrcAlpha OneMinusSrcAlpha的常见透明因子混合,并存在深度测试小于等于(<=),不写入深度。(一般情况透明物体是不开启深度测试的,而这里开启的目的是为了能被3D物体遮挡)

规范做法:
主摄像机屏蔽UI层渲染

UI摄像机仅渲染UI层

将3D物体设置到UI层

此时若想把Cube渲染在UI之上,那么就是直接放到Canvas物体前面即可。
Canvas距离UI摄像机的距离由下图参数Plane Distance决定(默认100)



若放在Canvas后面则是被遮挡。


UI摄像机不一定是正交的,即使换成透视视角,依然是保持正常的UI显示(画布会缩放大小)并3D物体以透视视角渲染出来。

但透视视角会有更大的开销用于裁剪,一般情况下都是正交视角节省开销。
注意事项:不要试图用主摄像机去渲染在参与UI排序的3D物体,若使用主摄像机渲染,这个3D物体是绝对位于UI之下的,因为主摄像机的深度缓冲区被UI摄像机清空了,UI摄像机开始渲染时所有UI像素都会正常通过深度测试,所以就肯定会渲染在3D物体之上。正常就应该是交给UI摄像机渲染,UI摄像机渲染时,正常3D物体会先被渲染,深度写入后,UI物体再参与渲染时就会正常通过深度测试将被3D物体遮挡的像素过滤掉,呈现出3D物体在UI之上的。

World Space  

        它同样可以指定一个摄像机专门负责渲染画布。但区别于Screen Space - Camera,画布的位置、旋转、缩放均不会随着屏幕、摄像机变化而变化,它就变成和普通的3D平面物体一样看待。

UI合批分析(建议不看 直接看FrameDebugger测试)

实际上我们不太需要这个合批分析工具,因为太难理顺各种UGUI的合批规则了,而且很多参数都很难获取到,即使获取到了也不一定准确,总之最好的分析工具还是打开Frame Debugger 然后手动调下就好了。 (已放弃维护)

原理及工具参考:【Unity】静态优化工具支持UGUI合批分析、AB包冗余分析、预制体使用资源情况分析_ab包资源冗余-CSDN博客

合批分析使用必须运行游戏之后再使用才正常,因为有很多数据都是运行后Unity才正常设置的,比如图集纹理,不运行是不会使用图集的。 

合批工具问题:
1、相交算法有问题,原本用的是rect1.position 实际要用rect1.localPosition,兼容锚点和中心点不是中心的情况。 
2、解决问题1后,其实发现要真正计算相交的是网格,而不是RectTransform区域,不同组件的网格获取方式不同,如图片、文本,其他组件还未支持,比如Spine等等,所以这个工具很难维护...
RectTransformExtensions.cs 代码修改和新增如下部分。

   public static void CalcualteGraphicRect(Graphic graphic, ref Rect rect)
        {
            Vector2 size = Vector2.zero;

            Text textComponent = graphic as Text;
            if (textComponent != null)
            {
                List<UIVertex> textUIVertexList = new List<UIVertex>();
                textUIVertexList = textComponent.cachedTextGenerator.verts as List<UIVertex>;
                //textComponent.cachedTextGenerator.GetVertices(textUIVertexList);                
                float minX = float.MaxValue;
                float maxX = float.MinValue;
                float minY = float.MaxValue;
                float maxY = float.MinValue;

                //使用center计算出中心点后,你还需要转空间,这个转空间一直出问题,所以不采用这种办法
                //Vector3 center = Vector3.zero;

                Vector3 minPos = Vector3.zero;
                Vector3 maxPos = Vector3.zero;

                string str = "";
                for (int i = 0; i < textUIVertexList.Count; i++)
                {
                    Vector3 pos = textUIVertexList[i].position;
                    minX = Mathf.Min(minX, pos.x);
                    maxX = Mathf.Max(maxX, pos.x);
                    minY = Mathf.Min(minY, pos.y);
                    maxY = Mathf.Max(maxY, pos.y);

                    minPos = Vector3.Min(minPos, pos);
                    maxPos = Vector3.Max(maxPos, pos);

                    //center += pos;
                    str += pos + " ";
                }
                size.x = maxX - minX;
                size.y = maxY - minY;
                //center /= textUIVertexList.Count; 

                //转空间会有问题 不采用计算中心点 而是直接算出以文本空间(中心点是文本坐标点rect.position)的网格中心点。
                //转空间就是直接用 网格中心点 + 文本坐标点,将网格中心点转到 文本所在空间(Canvas空间系下) 将其作为Rect区域的中心点去计算网格相交

                Vector2 rawPos = rect.position;
                //解决偏移问题,根据MinX MaxX  MinY MaxY 求出局部中心点, 用文本中心点rect.position加上局部中心点就能得到真正的文本网格中心点
                Vector2 offset = new Vector2(((minX + maxX) / 2.0f), ((minY + maxY) / 2.0f));
                rect.position += offset;
                Debug.Log("rawPos:" + rawPos + " afterPos:" + rect.position + "offset:" + offset);
                Debug.Log(graphic.gameObject.name + " " + size + "\n" + str);
            }
            else
            {
                Image imageComponent = graphic as Image;
                RectTransform r = imageComponent.GetComponent<RectTransform>();

                //只能编辑器读资源来获取MeshType,其他方式都试过不行...
                string path = AssetDatabase.GetAssetPath(imageComponent.sprite);
                TextureImporter textureImporter = AssetImporter.GetAtPath(path) as TextureImporter;
                TextureImporterSettings settings = new TextureImporterSettings();
                textureImporter.ReadTextureSettings(settings);

                //if (imageComponent.overrideSprite.packingMode == SpritePackingMode.Tight) //不能用这个packingMode判断, 会严格按照Sprite的MeshType和图集的Tight Packing决定。
                if (settings.spriteMeshType == SpriteMeshType.Tight)
                {                    
                    Vector3[] worldPosArr = new Vector3[4];
                    r.GetWorldCorners(worldPosArr);

                    float minX = float.MaxValue;
                    float maxX = float.MinValue;
                    float minY = float.MaxValue;
                    float maxY = float.MinValue;
                    string str = "";
                    for (int i = 0; i < worldPosArr.Length; i++)
                    {
                        Vector3 pos = worldPosArr[i];
                        minX = Mathf.Min(minX, pos.x);
                        maxX = Mathf.Max(maxX, pos.x);
                        minY = Mathf.Min(minY, pos.y);
                        maxY = Mathf.Max(maxY, pos.y);
                        str += pos + " ";
                    }
                    size.x = (maxX - minX) * 100;
                    size.y = (maxY - minY) * 100;
                    Debug.Log("[Tight] " + graphic.gameObject.name + " " + size + "\n" + str);
                }
                else
                {
                    size = r.rect.size;
                    Debug.Log("[FullRect]" + graphic.gameObject.name + " " + size);
                }
            }

            rect.size = size;
        }

        /// <summary>
        /// 适用
        /// </summary>
        /// <param name="rect1"></param>
        /// <param name="rect2"></param>
        /// <returns></returns>
        public static bool IsRectTransformOverlap(RectTransform rect1, RectTransform rect2)
        {
            //获取真实网格大小宽度高度
            Vector2 size1 = rect1.rect.size;
            Vector2 size2 = rect2.rect.size;
            Vector2 pos1 = rect1.localPosition;
            Vector2 pos2 = rect2.localPosition;

            Rect r1 = new Rect(pos1, size1);
            Rect r2 = new Rect(pos2, size2);

            Graphic g1 = rect1.GetComponent<Graphic>();
            Graphic g2 = rect2.GetComponent<Graphic>();
            if (g1 as Text || g1 as Image)
            {
                CalcualteGraphicRect(g1, ref r1);
            }
            if (g2 as Text || g2 as Image)
            {
                CalcualteGraphicRect(g2, ref r2);
            }

            Debug.Log(rect1.gameObject.name + " rect1: " + r1);
            Debug.Log(rect2.gameObject.name + " rect2: " + r2);

  
            float rect1MinX = r1.x - r1.width / 2;
            float rect1MaxX = r1.x + r1.width / 2;
            float rect1MinY = r1.y - r1.height / 2;
            float rect1MaxY = r1.y + r1.height / 2;

            //Debug.Log($"r1 pos:{pos1}, rect:{rect1.rect}");


            float rect2MinX = r2.x - r2.width / 2;
            float rect2MaxX = r2.x + r2.width / 2;
            float rect2MinY = r2.y - r2.height / 2;
            float rect2MaxY = r2.y + r2.height / 2;

            //Debug.Log($"r2 pos:{pos2}, rect:{rect2.rect}");

            bool xNotOverlap = rect1MaxX <= rect2MinX || rect2MaxX <= rect1MinX;
            //Debug.Log($"r1 maxX:{rect1MaxX}, r2 minx:{rect2MinX}, rect1MaxX <= rect2MinX:{rect1MaxX <= rect2MinX}");
            //Debug.Log($"r2 maxX:{rect2MaxX}, r1 minx:{rect1MinX}, rect2MaxX <= rect1MinX:{rect2MaxX <= rect1MinX}");
            //Debug.Log($"xNotOverlap:" + xNotOverlap);

            bool yNotOverlap = rect1MaxY <= rect2MinY || rect2MaxY <= rect1MinY;
            //Debug.Log($"r1 maxY:{rect1MaxY}, r2 minY:{rect2MinY}, rect1MaxY <= rect2MinY:{rect1MaxY <= rect2MinY}");
            //Debug.Log($"r2 maxY:{rect2MaxY}, r1 minY:{rect1MinY}, rect2MaxY <= rect1MinY:{rect2MaxY <= rect1MinY}");
            //Debug.Log($"yNotOverlap:" + yNotOverlap);

            bool notOverlap = xNotOverlap || yNotOverlap;

            return !notOverlap;
        }

图片网格就是注意Tight紧凑(Mesh Type)情况,网格区域是比RectTransform小的(大部分情况),而文本网格则是肯定会比RectTransform小,而且要特别注意不要只盯着一个文本符号网格的高度看,要看整体符号网格,比如下图:你只有发现全部都没相交图片网格才是不相交,否则只要有1个相交了,那就打断合批(节点交叉摆放情况才会,下面优化部分说明)

优化UI合批

1、Image图片纹理不同导致合批失败

0/0/0/0 代表 合批ID/深度/材质ID/贴图ID(仅需关注合批ID)


解决办法:将2张图片使用图集合并成一张图集图片(SpriteAtlas)然后Unity就会是使用图集图片去渲染,而不是2张图片,至于具体渲染图集图片的那张图片由Unity分析出图片所在图集图片的UV范围而采集纹理。
需要开启Sprite Packer Mode是Always Enabled
位于Project Settings - Editor 

2、文本和图片相交以及排序对合批的影响

仅有文本放到图片上且网格相交会打断文本合批,如果文本位于图片下面(被图片遮挡)则不会有影响。
原本文本和图片网格不相交(注意是网格,不是RectTransform的区域Rect)

一旦相交网格,就会打断合批,检查发现打断的是文本合批,图片合批正常执行。

其实也就是常见的按钮就是文本放到了图片上面的,如果有多个会如下。。。

解决办法:将文本全部挪到图片节点的下面,不要出现交叉图片和文本节点的形式。(图文节点交叉形式)

3、Mask对合批的影响(情况很复杂)

Show Mask Graphic:是否显示裁剪图片,勾选显示,否则不显示。这个不会影响裁剪、以及裁剪合批情况。

情况一:正常裁剪(Mask与其他Mask或图片不相交;Mask内图片与其他不相交)(渲染4次)

一共2个图集图片Image、其中一个图片(树)是白色图片Mask的子物体被裁剪。
Mask裁剪会将Mask内与Mask外图片合批打断,所以2张图片需要2次渲染;
1个Mask组件自身会进行一次Mask遮罩模板写入渲染,一次Mask遮罩模板清除渲染,共2次。
每个Mask组件之间的这个模板写入渲染和模板清除渲染也是可以合批的,稍后说明。
FrameDebugger情况如下:

自上而下分别是:
Mask图片自身渲染+Mask模板写入渲染
花图片渲染
树图片渲染
Mask模板清除渲染

情况二:将花图片拖拽到树和Mask图片的位置,不过花图片层级是低于树和Mask图片的,与情况一相比没有任何合批影响。(即不会增加DC次数)(渲染4次)

但是,渲染顺序自上而下变化:

Mask写入

Mask清除

情况三:将花图片也使用一个新的Mask图片裁剪(渲染3次)


2个Mask模板写入(合批进行)、2个Mask下同图集图片合批、2个Mask模板清除(合批进行)共3次渲染。也就是增加Mask反而能让DC下降。

情况四:花图片网格与其他Mask图片网格相交时,合批将发生巨大变化,由3变6(渲染6次)

严格来说是低层Mask下的图片网格与高层Mask网格相交

自上而下:
花Mask裁剪模板写入
花图片
树Mask裁剪模板写入
花Mask裁剪模板清除
树图片
树Mask裁剪模板清除

也就是说,全部都没有进行合批,目前已知情况三是最好的,情况四是最差的。

情况五:将花的Mask网格与另一个Mask下的树网格相交。(渲染5次)
严格来说是低层Mask网格与高层Mask下的图片网格相交


自上而下:
2个Mask模板写入

花Mask模板清除

树Mask模板清除

情况六:花的Mask网格与树的Mask网格相交(渲染6次)

自上而下:
花Mask裁剪模板写入
花图片
花Mask裁剪模板清除
树Mask裁剪模板写入
树图片
树Mask裁剪模板清除

情况七:将花转移到树的Mask下(作为子物体),花的Mask图片物体删除。(渲染3次)

只要Mask下的图片网格与Mask网格相交,则都会合批图片。
自上而下:Mask模板写入、图集图片合批、Mask模板清除

情况八:在情况七基础上,将花的网格不与Mask网格相交(将花移动到Mask裁剪区域之外)(渲染4次)

自上而下:花、mask写入、树、mask清除

情况九:将Mask下的所有图片移动到Mask网格之外。(渲染3次)

自上而下:mask写入、图集图片合批、mask清除

总结:情况三、情况七、情况九是最好的,仅3次,除了情况四、情况五、情况六是比较差的。
需要着重处理比较差的情况,往最好的情况去处理。

4、Canvas对合批的影响

与Mask的影响类似,但不会有Canvas自身的渲染(例如:模板写入、模板清除操作)但它会打断非Canvas内的上一个物体与下一个物体的合批。而每个Canvas内的元素合批情况与原本一样。

自上而下:(最差的情况)
花Canvas_Flwoer(1)
Canvas_Flower(1) 的 文本1 Text(1)
Canvas_Flower(1) 的 Image
Canvas_Flower(1) 的 文本2 Text(2)
树 Tree
花Canvas_Flower(2)
花Canvas_Flower(2) 的 文本 Text
树 Tree(1)

可发现Canvas和Canvas之间的Tree虽然是同样的图片,但没有合批,因为被Canvas打断,而Canvas_Flower(1)内的文本2个也没有合批,因为被Image打断。

优化后如下:

不要有交叉Canvas和Canvas之外物体的节点情况。
Canvas内的直接使用原本优化UI合批方法进行优化即可,即将文本和图片节点不要出现交叉。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/949600.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

平安产险安徽分公司携手安徽中医药临床研究中心附属医院 共筑儿童安全防护网

为响应金融知识普及教育号召&#xff0c;平安产险安徽分公司联动安徽中医药临床研究中心附属医院&#xff0c;于近日在朝霞小学举办了一场儿童安全防范与健康守护活动。此次活动旨在提升学生的安全防范意识&#xff0c;守护儿童健康成长&#xff0c;同时有力推动金融知识与传统…

zephyr移植到STM32

Zephy如何移植到单片机 1. Window下搭建开发环境1.1 安装Choncolatey1.2 安装相关依赖1.3创建虚拟python环境1.4 安装west1.4.1 使用 pip 安装 west1.4.2 检查 west 安装路径1.4.3 将 Scripts路径添加到环境变量1.4.4 验证安装 1.5 获取zephyr源码和[安装python](https://so.cs…

fail api scope is not declared in the privacy agreement微信小程序uniapp 解决录音无法播放、授权

已解决 fail api scope is not declared in the privacy agreement微信小程序uniapp 解决录音无法播放、授权 没有声明内容协议导致的 微信公众平台&#xff1a;https://mp.weixin.qq.com/【1.左下角的-移动过去后会出现 “帐号设置”】 【2.基本设置->服务内容声明->修…

虚拟机 网络防御(预防信息泄露)

了解VMware网络基本配置 Bridged(桥接模式):虚拟机和主机好比在同一个网络环境下的两台电脑。 NAT(网络地址转换模式):NAT模式虚拟机通过主机进行联网。&#xff08;推荐&#xff09; Host-0nly(主机模式):主机模式将虚拟机与外网隔开&#xff0c;只能让虚拟机和虚拟机之间联…

打包部署若依(RuoYi)SpringBoot后端和Vue前端图文教程

打包后端‘ 1&#xff0c;打开若依&#xff0c;点击右侧的Maven展开Maven管理&#xff0c;选择ruoyi>Lifecycle 先双击clean清除原本启动项目时生成的文件。然后点击package等待项目打包&#xff0c;切记要取消运行再打包 打包完成后会在ruoyi-admin>src>target里面…

矩阵碰一碰发视频源码搭建全解析,支持OEM

在数字化营销与互动体验需求日益增长的当下&#xff0c;矩阵碰一碰发视频功能以其独特的交互性和高效的信息传播能力&#xff0c;正逐渐成为吸引用户、提升品牌影响力的有力工具。本文将深入探讨如何搭建矩阵碰一碰发视频的源码&#xff0c;帮助开发者实现这一创新功能。 一、技…

专题十四——BFS

目录 一BFS解决FloodFill算法 1图像渲染 2岛屿数量 3岛屿的最大面积 4被环绕的区域 二BFS解决蛋源最短路径问题 1迷宫中离入口最近的出口 2最小基因变化 3单词接龙 4为高尔夫比赛砍树 三BFS解决多源最短路径问题 1 01矩阵 2飞地的数量 3地图中的最高点 4地图分…

openwrt 清缓存命令行

一、查看缓存 &#xff1a; free -m 二、清缓存&#xff1a;echo 3 > /proc/sys/vm/drop_caches  三、详解。 释放物理页缓存 echo 1 > /proc/sys/vm/drop_caches 释放可回收的slab对象&#xff0c;包含inode and dentry echo 2 > /proc/sys/vm/drop_caches 同时…

huggingface 下载方法 测试ok

目录 python下载方法&#xff1a; 设置环境变量 ~/.bashrc 缓存目录&#xff0c;默认模型下载目录 安装方法&#xff1a; python 下载无token&#xff1a; python 下载带token 常见报错 登录后创建Read token 2.3 创建token 使用token登录 python下载方法&#xff1…

滑动窗口_⻓度最⼩的⼦数组⽆重复字符的最⻓⼦串将x减到0的最⼩操作数

⻓度最⼩的⼦数组&#xff08;medium https://leetcode.cn/problems/minimum-size-subarray-sum/ 思路一&#xff1a;两个指针&#xff0c;p1 p2同时指向第一个元素。sump2,如果sum<target&#xff0c;p2直到大于&#xff0c;然后记录lenright-left。p1 p2再同时指向第二个…

【无标题】linux

Linux工具快速教程 Linux 基础 Linux工具快速教程1、使用命令帮助1.1 查看命令的简要说明1.2 查看路径 2. 文件及目录2.1 创建和删除2.2 切换目录2.3 列出目录项2.4 查找文件或目录 find2.5 查看文件内容2.6 查找文件内容 好用小工具linux工具电源统计1. 查询公网IPhttp://www.…

HarmonyOS-面试资料

1. HarmonyOS-面试资料 1.1. HarmonyOS 优点、特点 1.1.1. 优点 &#xff08;1&#xff09;在国家方面&#xff0c;是国产的系统&#xff0c;受国家支持不会有限制的情况。   &#xff08;2&#xff09;设备互连18N(1:手机 8&#xff1a;平板、PC、vr设备、可穿戴设备、智慧…

关于物联网的基础知识(一)

成长路上不孤单&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a; 【14后&#x1f60a;///计算机爱好者&#x1f60a;///持续分享所学&#x1f60a;///如有需要欢迎收藏转发///&#x1f60a;】 今日分享关于物联网的基础知识&#xff08;一&a…

基于Thinkphp6+uniapp的陪玩陪聊软件开发方案分析

使用uni-app框架进行前端开发。uni-app是一个使用Vue.js开发所有前端应用的框架&#xff0c;支持一次编写&#xff0c;多端发布&#xff0c;包括APP、小程序、H5等。 使用Thinkphp6框架进行后端开发。Thinkphp6是一个轻量级、高性能、面向对象的PHP开发框架&#xff0c;具有易…

springcloud 介绍

Spring Cloud是一个基于Spring Boot的微服务架构解决方案集合&#xff0c;它提供了一套完整的工具集&#xff0c;用于快速构建分布式系统。在Spring Cloud的架构中&#xff0c;服务被拆分为一系列小型、自治的微服务&#xff0c;每个服务运行在其独立的进程中&#xff0c;并通过…

jenkins入门6 --拉取代码

Jenkins代码拉取 需要的插件&#xff0c;缺少的安装下 新建一个item,选择freestyle project 源码管理配置如下&#xff1a;需要添加git库地址&#xff0c;和登录git的用户密码 配置好后执行编译&#xff0c;成功后拉取的代码在工作空间里

idea全局替换显示不全(ctrl+shift+R)

修改一下idea的配置就行 idea的默认显示条数为100&#xff0c;可以修改成10000

Ubuntu 下测试 NVME SSD 的读写速度

在 Ubuntu 系统下&#xff0c;测试 NVME SSD 的读写速度&#xff0c;有好多种方法&#xff0c;常用的有如下几种&#xff1a; 1. Gnome-disks Gnome-disks&#xff08;也称为“Disks”&#xff09;是 GNOME 桌面环境中的磁盘管理工具&#xff0c;有图形界面&#xff0c;是测试…

AI投资分析:用于股票评级的大型语言模型(LLMs)

“AI in Investment Analysis: LLMs for Equity Stock Ratings” 论文地址&#xff1a;https://arxiv.org/pdf/2411.00856 摘要 投资分析作为金融服务领域的重要组成部分&#xff0c;LLMs&#xff08;大型语言模型&#xff09;为股票评级带来了改进的潜力。传统的股票评级方式…

基于CLIP和DINOv2实现图像相似性方面的比较

概述 在人工智能领域&#xff0c;CLIP和DINOv2是计算机视觉领域的两大巨头。CLIP彻底改变了图像理解&#xff0c;而DINOv2为自监督学习带来了新的方法。 在本文中&#xff0c;我们将踏上一段旅程&#xff0c;揭示定义CLIP和DINOv2的优势和微妙之处。我们的目标是发现这些模型…