1. 程式人生 > >Unity 通過 Secondary Maps + UV Set 動態修改UV

Unity 通過 Secondary Maps + UV Set 動態修改UV

本問題是在開發ar 塗塗樂專案過程中碰到的

(2017.03.15. 更新 (此解決方案有顯示上的問題,現已用shader完美解決,請看後面17日更新的內容))

本文的主題是:unity如何動態的修改單個模型已存在的多個uv

首先說明一個事實,untiy沒有提供動態修改UV的指令碼介面。我的實現方式有些取巧但並不是強行的取巧方式。

大家剛開始查詢這個問題的答案的時候會發現unity API中有這樣幾個東西:

  1. Swap UVs(在模型的匯入設定裡面,也就是模型的Import Setting)
  2. Generate Lightmap UVs(在模型的匯入設定裡面,也就是模型的Import Setting)
  3. UV Set(Standard Shader,也就是unity自帶的最基本的shader,的一個引數)

當然,用腳趾頭想想,都應該會想到,動態切換模型的uv肯定是要先從shader下手。所以我查看了unity自帶的shader,然後就在Standard中發現了uv set這個引數,如下圖:
這裡寫圖片描述

於是,我就決定從uv set開始入手。

  1. 先在3D軟體中,給模型附上多套UV,然後才能在unity中使用。
  2. unity貌似可以支援最多4套UV切換(注意這個貌似,因為我最後也沒有找到這個切換的介面,只找到了什麼資料都查不到的UVChannelFlags.UV)

然後這個文章裡面有些東西是有問題的:

  1. 裡面的shader原始碼並沒有存在的意義,首先這個shader原始碼沒有提供切換uv的地方,沒有實現切換uv的效果,然後,真正在生產環境中是不可能使用這個shader的,因為你不能去給每一個需要切換uv的模型的shader進行重寫

但是查到後面,發現uv set即使切換了,也不能達到切換uv的效果。帶著兩層uv的模型,隨便貼一張貼圖,然後使用程式碼切換了這個shader的uv set之後,發現場景中的模型身上的貼圖並沒有變化,更別說是否正確的切換了。

隨後我又開始轉變思路,先後查了下面的幾點:

  1. UVChannelFlags.UV:這個結構雖然在render的原始碼中看到了,而且這個的結構是這樣的:
    [Flags]
    public enum UVChannelFlags
    {
    UV0 = 1,
    UV1 = 2,
    UV2 = 4,
    UV3 = 8
    }

    ,所以我以為我找到了unity可以切換uv的關鍵,忽然相信了unity可以方便的切換4個uv,但是隨後我並沒有找到任何和UVChannelFlags相關的資料和引用它的地方。並宣佈這是個dead end。

  2. Mesh.SetUVsMesh.uv 這個Mesh.SetUVs一看到他的API和實現,就知道是用於手動實現uv的,而並不是去切換模型上已存在的多個uv。而Mesh.uv,可以通過給其賦值達到切換uv的效果,但是我並沒有找到可以模型上的uv介面以給其賦值。就是說,我沒有可以給其賦值的東西。具體的Mesh.uv是一個Vector2[]型的變數,如果說你們找到了可以通過Mesh.uv達到動態切換單個模型中已儲存的多個uv的方法,可以在底下留言。

  3. Swap UVs(在模型的匯入設定裡面,也就是模型的Import Setting)。這裡寫圖片描述通過勾選Swap UVs這個選項確實可以達到切換uv的效果,這個選項官方的解釋就是可以切換匯入模型的主uv和次uv。所以我列一下這個選項的幾個問題:1。只能在兩個uv之間切換,而如果照官方API中uv相關的api(比如UVChannelFlags.UV,Mesh.uv等)可以看出,是有四個uv可以切換的;2。因為這個選項是在model的import setting裡面,所以並不能在runtime的時候動態的去修改。所以最後,這個也是條dead end。
  4. Secondary Maps,這個才是能實現動態修改的關鍵,它是在standard shader裡面的。這裡寫圖片描述只有在使用了Secondary Maps後切換UV Set才能生效。所以我先是把Secondary Maps的Albedo和上面的Main Maps的Albedo分別使用相同的貼圖(測試同一張貼圖不同uv的顯示效果),然後切換UV Set發現切換uv的效果出來了,但是不太對,在切換到UV1的時候,UV0的貼圖還在(原因大家可以去查查uv set中UV1的作用)。所以我後面直接將Main Maps的Tilling設定成了(0,0),然後就達到了切換uv的效果。使用的時候,使用指令碼來控制這個UV Set就可以了。指令碼如下:
    private void SetToUV0()
    {
        MeshRenderer render = GetComponent<MeshRenderer>();
        render.material.SetFloat("_UVSec", 0);
    }

    private void SetToUV1()
    {
        MeshRenderer render = GetComponent<MeshRenderer>();
        render.material.SetFloat("_UVSec", 1);
    }

最後,我的實現方式就是Secondary Maps+UV Set(也就是上面的第4點)。

(2017.03.17. 更新)

由於上面的解決方案,用到正式的專案中的時候,發現貼圖變成了藍色(檢視standard原始碼後發現是因為少了兩個通道的顏色),所以重新研究standard shader原始碼,然後發現可以通過shader非常方便的實現。而且是可以進行4個uv的切換。

下面是部分standard shader原始碼(為了方便理解,我進行了簡化):


Properties
{
    _MainTex("Albedo", 2D) = "white" {}
    [Enum(UV0,0,UV1,1)] _UVSec ("UV Set for secondary textures", Float) = 0 //這就是UVSet
}

struct VertexInput
{
    float2 uv0      : TEXCOORD0;
    float2 uv1      : TEXCOORD1;
};

float4 TexCoords(VertexInput v)
{
    float4 texcoord;
//從下面兩行原始碼可以看出,上面的解決方案(通過切換UVSet(即這裡的_UVSec 變數)只能影響到texcoord的zw,而不能影響到xy,所以會出現貼圖變藍的情況)
    texcoord.xy = TRANSFORM_TEX(v.uv1, _MainTex); 
    texcoord.zw = TRANSFORM_TEX(((_UVSec == 0) ? v.uv0 : v.uv1), _DetailAlbedoMap);
    return texcoord;
}   

所以,在shader中,uv是可以很方便的拿到並且切換的,關鍵的程式碼就是(上方中的一行):

texcoord.xy = TRANSFORM_TEX(v.uv1, _MainTex); 

但是這個只能影響到texcoord的xy,所以應該寫成:

texcoord = TRANSFORM_TEX(v.uv1, _MainTex);

這裡的uv1就是模型身上的第二張uv(uv0:第一張,uv1:第二張),為什麼是呢?是因為在上面的結構體中,把他們定義成了TEXCOORD0、TEXCOORD1、TEXCOORD2等,如下:

struct VertexInput
{
    float2 uv0      : TEXCOORD0;
    float2 uv1      : TEXCOORD1;
};

所以,隨便拿一個shader過來,我們都可以把它加上切換uv的功能。我這裡用了unity內建的”Unlit/Texture” shader來進行修改,因為我們的正式工程這個shader用的比較多。

原來的”Unlit/Texture”:

Shader "Unlit/Texture" {
Properties {
    _MainTex ("Base (RGB)", 2D) = "white" {}
}

SubShader {
    Tags { "RenderType"="Opaque" }
    LOD 100

    Pass {  
        CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile_fog

            #include "UnityCG.cginc"

            struct appdata_t {
                float4 vertex : POSITION;
                float2 texcoord : TEXCOORD0;
            };

            struct v2f {
                float4 vertex : SV_POSITION;
                half2 texcoord : TEXCOORD0;
                UNITY_FOG_COORDS(1)
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;

            v2f vert (appdata_t v)
            {
                v2f o;
                o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
                o.texcoord = TRANSFORM_TEX(v.texcoord, _MainTex); 
                UNITY_TRANSFER_FOG(o,o.vertex);
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                fixed4 col = tex2D(_MainTex, i.texcoord);
                UNITY_APPLY_FOG(i.fogCoord, col);
                UNITY_OPAQUE_ALPHA(col.a);
                return col;
            }
        ENDCG
    }
}

}

下面是修改後的:

Shader "Unlit/Texture-ForUV" {
    Properties{
        _MainTex("Base (RGB)", 2D) = "white" {}

        [Enum(UV0,0,UV1,1)] _UVSet("UV Set for textures", Float) = 0 //增加了_UVSet的切換功能
    }

        SubShader{
        Tags{ "RenderType" = "Opaque" }
        LOD 100

        Pass{
        CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_fog

#include "UnityCG.cginc"

        struct appdata_t {
        float4 vertex : POSITION;
        float2 uv0 : TEXCOORD0;//上面這裡的變數名是texcoord,我改了個名字,改成了uv0
        float2 uv1  : TEXCOORD1;//添加了uv1
    };

    struct v2f {
        float4 vertex : SV_POSITION;
        half2 texcoord : TEXCOORD0;
        UNITY_FOG_COORDS(1)
    };

    sampler2D _MainTex;
    float4 _MainTex_ST;
    half        _UVSet;//註冊_UVSet

    v2f vert(appdata_t v)
    {
        v2f o;
        o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
        o.texcoord = TRANSFORM_TEX(((_UVSet == 0) ? v.uv0 : v.uv1), _MainTex);//關鍵是修改了這行,根據_UVSet的值,在uv0和uv1中進行切換
        UNITY_TRANSFER_FOG(o,o.vertex);
        return o;
    }

    fixed4 frag(v2f i) : SV_Target
    {
        fixed4 col = tex2D(_MainTex, i.texcoord);
        UNITY_APPLY_FOG(i.fogCoord, col);
        UNITY_OPAQUE_ALPHA(col.a);
        return col;
    }

        ENDCG
    }
    }

}

最後來張shader截圖:
這裡寫圖片描述

【個人廣告】
希望大家可以支援我的個人微訊號“小遊戲情報局

這裡寫圖片描述