Unity的UI究竟為什么可以合批


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爪爪學院查找。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM