1.UI/Default代码研究
首先,我想到的是,既然是对图集纹理进行采样,而且又不能统一更改材质的纹理UV值,我们通常写的shader都是直接根据模型UV值对主纹理进行采样,那会不会是shader中对MainTexture进行了什么神奇的处理,让图片采样只根据指定的UV值进行采样呢?
我去官网下载了shader代码,找到了UI/Default的具体实现:

fixed4 _Color;
fixed4 _TextureSampleAdd;
float4 _ClipRect;

v2f vert(appdata_t v)
{
v2f OUT;
UNITY_SETUP_INSTANCE_ID(v);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(OUT);
OUT.worldPosition = v.vertex;
OUT.vertex = UnityObjectToClipPos(OUT.worldPosition);

OUT.texcoord = v.texcoord;

OUT.color = v.color * _Color;
return OUT;
}

sampler2D _MainTex;

fixed4 frag(v2f IN) : SV_Target
{
half4 color = (tex2D(_MainTex, IN.texcoord) + _TextureSampleAdd) * IN.color;

#ifdef UNITY_UI_CLIP_RECT
color.a *= UnityGet2DClipping(IN.worldPosition.xy, _ClipRect);
#endif

#ifdef UNITY_UI_ALPHACLIP
clip (color.a - 0.001);
#endif

return color;
}
看了上面的代码,我们可以基本确定,没有在shader中做什么特别神奇的MainTexture处理。但是我们还是可以发现一些不同的地方,这里上面的变量_Color,_TextureSampleAdd,_ClipRect并没有暴露在面板上,可以看出来这三个变量是通过某些脚本传递给shader的。
我们知道,伴随着Defalut材质的一般使用的是Image组件、Text组件。这两个组件会绘制顶点与三角形,然后使用指定的材质进行渲染。所以会不会是Image组件或Text组件中使用了什么算法,计算过图片UV值,并把上面三个变量填充好传给shader的呢?

2.Image组件代码研究
因为unity的ui代码已经开源了,所以我们很幸运的可以看到Image的源码是怎么实现的,因为Image组件代码很多,所以这里就只贴出比较主要的绘制顶点的函数:

/// <summary>
/// Update the UI renderer mesh.
/// </summary>
protected override void OnPopulateMesh(VertexHelper toFill)
{
if (activeSprite == null)
{
base.OnPopulateMesh(toFill);
return;
}

switch (type)
{
case Type.Simple:
if (!useSpriteMesh)
GenerateSimpleSprite(toFill, m_PreserveAspect);
else
GenerateSprite(toFill, m_PreserveAspect);
break;
case Type.Sliced:
GenerateSlicedSprite(toFill);
break;
case Type.Tiled:
GenerateTiledSprite(toFill);
break;
case Type.Filled:
GenerateFilledSprite(toFill, m_PreserveAspect);
break;
}
}

我们可以看到,这个函数是用来刷新UI渲染的,unity对图片的四种类型分别进行了处理,这里我们就只看一下最简单的Simple模式的代码:

/// <summary>
/// Generate vertices for a simple Image.
/// </summary>
void GenerateSimpleSprite(VertexHelper vh, bool lPreserveAspect)
{
Vector4 v = GetDrawingDimensions(lPreserveAspect);
var uv = (activeSprite != null) ? Sprites.DataUtility.GetOuterUV(activeSprite) : Vector4.zero;

var color32 = color;
vh.Clear();
vh.AddVert(new Vector3(v.x, v.y), color32, new Vector2(uv.x, uv.y));
vh.AddVert(new Vector3(v.x, v.w), color32, new Vector2(uv.x, uv.w));
vh.AddVert(new Vector3(v.z, v.w), color32, new Vector2(uv.z, uv.w));
vh.AddVert(new Vector3(v.z, v.y), color32, new Vector2(uv.z, uv.y));

vh.AddTriangle(0, 1, 2);
vh.AddTriangle(2, 3, 0);
}

/// Image's dimensions used for drawing. X = left, Y = bottom, Z = right, W = top.
private Vector4 GetDrawingDimensions(bool shouldPreserveAspect)
{
var padding = activeSprite == null ? Vector4.zero : Sprites.DataUtility.GetPadding(activeSprite);
var size = activeSprite == null ? Vector2.zero : new Vector2(activeSprite.rect.width, activeSprite.rect.height);

Rect r = GetPixelAdjustedRect();
// Debug.Log(string.Format("r:{2}, size:{0}, padding:{1}", size, padding, r));

int spriteW = Mathf.RoundToInt(size.x);
int spriteH = Mathf.RoundToInt(size.y);

var v = new Vector4(
padding.x / spriteW,
padding.y / spriteH,
(spriteW - padding.z) / spriteW,
(spriteH - padding.w) / spriteH);

if (shouldPreserveAspect && size.sqrMagnitude > 0.0f)
{
PreserveSpriteAspectRatio(ref r, size);
}

v = new Vector4(
r.x + r.width * v.x,
r.y + r.height * v.y,
r.x + r.width * v.z,
r.y + r.height * v.w
);

return v;
}

public void AddVert(Vector3 position, Color32 color, Vector2 uv0);
就是在这里了,首先拿到绘制的尺寸v,也就是四个顶点的位置,然后根据activeSprite拿到纹理的UV值。我们可以看到AddVert函数中,第三个值是绘制的顶点中填充的uv0也就是这个得到的UV值,而shader中也会根据这个uv值对MainTexture进行采样。

3.小实验
我们已经知道计算顶点与UV值的操作是在image中进行的,其实unity有一个组件可以自己控制采样的uv值,就是RawImage组件,相比Image组件,RawImage组件更为精简,因为没有处理Image中的四种图片样式。
其实Image组件中帮我们做的操作其实就相当于(是相当于,其实计算比这复杂的多)在RawImage中设置了不同的UV偏移值。这样就可以做到,每个组件使用的UV值不同,而不是改变统一使用材质上的UV值。

修改RawImage中的UV值

总结
我们最开始的想法是修改材质中的UV值,但是这样是不行的,因为改变了材质UV值后所有物体都会跟着改变。Unity使用了一个巧妙的办法,也就是在建模(绘制顶点/三角形)的时候,就把得到的图集中纹理的UV采样值填充到mesh的UV中。所以材质使用的都是同一个材质,也都是对MainTexture进行采样,只不过每个图片的mesh中存储的UV值都是不同的。

更多unity2018的功能介绍请到paws3d爪爪学院查找。

Unity的UI究竟为什么可以合批的更多相关文章

  1. 关于如何在 Unity 的 UI 菜单中默认创建出的控件 Raycast Target 属性默认为 false

    关于如何在 Unity 的 UI 菜单中默认创建出的控件 Raycast Target 属性默认为 false 我们在 Unity 中通过 UI 菜单创建的各种控件,比如 Text, Image 等, ...

  2. C#程序员整理的Unity 3D笔记(十五):Unity 3D UI控件至尊–NGUI

    目前,UGUI问世不过半年(其随着Unity 4.6发布问世),而市面上商用的产品,UI控件的至尊为NGUI:影响力和广度(可搜索公司招聘Unity 3D,常常能看到对NGUI关键词). NGUI虽然 ...

  3. Unity在UI界面上显示3D模型/物体,控制模型旋转

    Unity3D物体在UI界面的显示 本文提供全流程,中文翻译. Chinar 坚持将简单的生活方式,带给世人!(拥有更好的阅读体验 -- 高分辨率用户请根据需求调整网页缩放比例) Chinar -- ...

  4. 关于Unity中UI中的Mask组件、Text组件和布局

    一.Mask组件 遮罩,Rect Mask矩形Mask(Rect Mask2D组件),图片Mask(Mask组件)(图片Mask的透明度不为0的部分显示子图片,为0的部分不显示子图片) Rect Ma ...

  5. 关于Unity中UI中的Image节点以及它的Image组件

    一.图片的Inspector面板属性 Texture Type:一般是选择sprite(2D and UI) Sprite Mode:一般是选择Single Packing Tag:打包的标志值,最后 ...

  6. unity简易ui框架

    在unity项目开发中,ui模块的开发往往占据了很大一部分工作,部分游戏甚至绝大部分的工作都是在ui上,如何高效管理各种界面,这里分享一套高效易用的UI框架. 首先,我们定义一个PanelBase类, ...

  7. Unity shader UI的3D效果

    原创,转载请标明出处 1.效果 scene视图中的效果: game视图中效果: 2.核心思想:改变UI的顶点坐标 3.好处:可以用正交相机来实现3D效果. 4.Shader 实现 // Unity b ...

  8. unity UGUI UI跟随

    实现2dUI跟随游戏中角色的移动(应用于玩家名称,血条,称号) using UnityEngine; public class UI_Follow : MonoBehaviour { public C ...

  9. [Unity优化]UI优化(三):GraphicRebuild

    参考链接: https://blog.csdn.net/jingangxin666/article/details/80143176 调试过程: 1.修改Image的颜色 2.Graphic.SetV ...

随机推荐

  1. 【转】Sentry--错误日志收集

    简介 Sentry是一个实时事件日志记录和汇集的日志平台,其专注于错误监控,以及提取一切事后处理所需的信息.他基于Django开发,目的在于帮助开发人员从散落在多个不同服务器上的日志文件里提取发掘异常 ...

  2. PAT1047: Student List for Course

    1047. Student List for Course (25) 时间限制 400 ms 内存限制 64000 kB 代码长度限制 16000 B 判题程序 Standard 作者 CHEN, Y ...

  3. Oracle聚合函数

    max(),min(),avg(),sum() 其中 avg(),sum()只能操作数字  , max(),min()能操作数字.日期.字母 等.

  4. Hibernate中的多表查询及抓取策略

    1.Hibernate中的多表查询 1.1SQL中的多表查询 [交叉连接] select * from A,B; [内连接] 显示内连接:inner join(inner 可以省略) Select * ...

  5. mybatis一对一映射配置详解

    听说mybatis一对一有三种写法,今天我试了一下. 数据库表准备 为了偷懒,我直接就拿用户权限菜单里的菜单表和菜单与权限的中间表做实现,他们原来是多对多的关系,这边我假设这两张表是一对一. 表  g ...

  6. linux几种时间函数总结

    一.linux时间函数总结 最近的工作中用到的时间函数比较频繁,今天抽时间总结一下,在linux下,常用的获取时间的函数有如下几个:  asctime,  ctime, gmtime, localti ...

  7. mybatis设置callSettersOnNulls解决返回字段不全的问题

    Spring+MyBatis开发过程中,在xxMapper.xml配置文件进行select查询时resultType="map",如果要查询的字段是空值,在返回的map中会出现找不 ...

  8. An annotation based command line parser

    Java命令行选项解析之Commons-CLI & Args4J & JCommander http://rensanning.iteye.com/blog/2161201 JComm ...

  9. Scrapy 1.4 文档 03 Scrapy 教程

    在本教程中,我们假设您已经安装了Scrapy.如果没有,请参阅安装指南. 我们将要抓取 quotes.toscrape.com,一个列出著名作家的名言(quote)的网站. 本教程将引导您完成以下任务 ...

  10. 领域驱动设计和Spring

    原文 http://static.olivergierke.de/lectures/ddd-and-spring/ 1.介绍这篇文章是的介绍一下领域驱动设计的基础构件.概念和Java的web应用(主要 ...