1.UI/Default代碼研究首先,我想到的是,既然是對圖集紋理進行採樣,而且又不能統一更改材質的紋理UV值,我們通常寫的shader都是直接根據模型UV值對主紋理進行採樣,那會不會是shader中對MainTexture進行了什麼神奇的處理,讓圖片採樣只根據指定的UV值進行採樣呢?我去官網下載了 ...
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爪爪學院查找。