Unity3D學習筆記10——紋理數組

1. 概述

個人認爲,紋理數組是一個非常有用的圖形特性。紋理本質上是一個二維的圖形數據;通過紋理數組,給圖形數據再加上了一個維度。這無疑會帶來一個巨大的性能提升:一次性傳輸大量的數據總是比分批次傳輸數據要快。

2. 詳論

2.1. 實現

創建一個GameObject對象,並且加入Mesh Filter組件和Mesh Renderer組件。Mesh Filter我們可以設置Mesh爲Quad,同時在Mesh Filter上掛一個我們新建的材質:

imglink1

在這個GameObject對象上掛接一個我們創建的C#腳本:

using Unity.Collections;
using UnityEngine;

[ExecuteInEditMode]
public class Note10Main : MonoBehaviour
{
    public Texture2D texture1;
    public Texture2D texture2;

    [Range(0.0f, 1.0f)]
    public float weight;

    Material material;

    // Start is called before the first frame update
    void Start()
    { 
        MeshRenderer mr = GetComponent<MeshRenderer>();
        material = mr.sharedMaterial;

        Texture2DArray texture2DArray = CreateTexture2DArray();

        material.mainTexture = texture2DArray;
        material.SetFloat("_Weight", weight);
    }

    Texture2DArray CreateTexture2DArray()
    {
        Texture2DArray texture2DArray = new Texture2DArray(texture1.width, texture1.height, 2,
            texture1.format, false);

        NativeArray<byte> pixelData1 = texture1.GetPixelData<byte>(0);
        NativeArray<byte> pixelData2 = texture2.GetPixelData<byte>(0);
                
        texture2DArray.SetPixelData(pixelData1, 0, 0, 0);
        texture2DArray.SetPixelData(pixelData2, 0, 1, 0);

        texture2DArray.Apply(false, false);

        return texture2DArray;
    }

    // Update is called once per frame
    void Update()
    {
        material.SetFloat("_Weight", weight);
    }
}

這段C#腳本的意思是,通過傳入兩個Texture2d,生成一個texture2DArray;並且,將這個texture2DArray傳入到材質中。需要注意的是紋理數組中的每個紋理的參數如寬、高等參數都需要一致,否則不能組成紋理數組。

材質使用我們自定義的Shader:

Shader "Custom/TextureArrayShader"
{
    Properties
    {
		_MainTex ("Texture", 2DArray) = "" {}
		_Weight ("Weight", float) = 0.0 
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
  
            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;       
                float4 vertex : SV_POSITION;
            };

            UNITY_DECLARE_TEX2DARRAY(_MainTex);
			float _Weight;

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = v.uv;          
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {           
				fixed4 col0 = UNITY_SAMPLE_TEX2DARRAY(_MainTex, float3(i.uv, 0));
				fixed4 col1 = UNITY_SAMPLE_TEX2DARRAY(_MainTex, float3(i.uv, 1));		
                return lerp(col0, col1, _Weight);
            }
            ENDCG
        }
    }
}

這裏實現的效果是,將紋理數組中的兩個紋理根據權重進行混合。權重值也是在C#腳本中傳入到Shader中的。在編輯器中將權重調整到中間一點的位置(例如0.5):
imglink2

Shader代碼也很好理解,關鍵在於紋理數組相關的宏,其實是對hlsl或者glsl的封裝:

#define UNITY_DECLARE_TEX2DARRAY(tex) Texture2DArray tex; SamplerState sampler##tex
#define UNITY_SAMPLE_TEX2DARRAY(tex,coord) tex.Sample (sampler##tex,coord)

#define UNITY_DECLARE_TEX2DARRAY(tex) sampler2DArray tex
#define UNITY_SAMPLE_TEX2DARRAY(tex,coord) tex2DArray (tex,coord)

2.2. 注意

  1. 關於紋理數組的創建,也可以使用Graphics.CopyTexture()這個接口。這個接口是純走GPU端的,效率應該回更高。
  2. 紋理數組這個特性在低端顯卡上可能不支持,但是不一定就會非常耗費性能。可以考慮通過紋理數組的方式來合併渲染的批次。
  3. 紋理數組個數的限制並不是紋理單元個數。實際上一個紋理數組只會綁定到一個紋理單元上,而在本人GTX 1660 Ti的顯卡上,紋理數組個數的限制是4096個。

3. 參考

  1. Texture2DArray 功能測試
  2. What are the limits of texture array size?

代碼地址

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章