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

来源:https://www.cnblogs.com/jieke666/archive/2019/04/24/10763009.html
-Advertisement-
Play Games

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爪爪學院查找。
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 首先引入dll文件ICSharpCode.SharpZipLib.dll 管理NuGet包裡面下載 壓縮文件 文件下載 具體使用 ...
  • 最近做項目用到MVC,發現用linq查詢得到的數據是匿名類型對象,通過模型綁定、或者ViewBag、ViewData進行數據傳遞後,View解析報錯:“object 未包含xx的定義”; 沒找到好的解決辦法,就只能通過內部對象一個類型去解決。 所以想到了對內部對象一個類型轉換,轉換成Jobject, ...
  • 一、什麼是依賴註入 二、Asp.net core中依賴註入的生命周期 依賴註入的生命周期有三種Transient,Scoped和Singleton; 1、Transient每次調用都是不同的實例,比如常用的Microsoft.Extensions.Options.IConfigureOptions< ...
  • 本文介紹如何處理多個用戶併發更新同一實體(同時)時出現的衝突 。 主要是兩種:一種,檢查屬性併發衝突,使用 [ConcurrencyCheck] ;另一種,檢測行的併發衝突,使用 rowversion 跟蹤屬性,如果在保存之前有修改,就報錯 發生併發衝突的情況: 1.用戶導航到實體編輯頁面; 2.第 ...
  • ASP.NET Core 有內置的log組件,遺憾的是看了微軟官方文檔,貌似無法直接將日誌存於文件或資料庫,只能由自己實現或引用第三方日誌組件。 以下為Nlog和log4net的使用記錄 Nlog使用 搜索添加Nuget包 搜索添加Nuget包 新建一個xml文件,並改名為nlog.config 新 ...
  • C 開發輔助類庫,和士官長一樣身經百戰且越戰越勇的戰爭機器,能力無人能出其右。 GitHub: "MasterChief" 歡迎Star,歡迎Issues; 項目架構思維導圖: 目錄 ================= "1\. 資料庫訪問" "2\. 日誌" "3\. 緩存" "4\. 配置" " ...
  • 今天我們先來聊聊有關線程的話題...... 一. 線程概述 1. 簡單區分程式、進程和線程 程式是指一段靜態的代碼 進程是指正在執行的程式,將靜態的代碼運行起來 線程是指正在執行程式的小單元 舉個慄子,班級準備大掃除,在大掃除之前,老師在紙上列了一個清單,每個同學都有不同的工作任務,分配好任務之後, ...
  • getLoginQRCode (獲取登錄二維碼)CheckLoginQRCode(檢測掃碼狀態)ManualAuth(掃碼登錄)ManualAuth(62數據登錄)ManualAuth(賬號密碼登錄)newverifypasswd(驗證密碼)GetQRCode(獲取個人或群二維碼)F2FQrcode ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...