• 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);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48

    这段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
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53

    这里实现的效果是,将纹理数组中的两个纹理根据权重进行混合。权重值也是在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)
    
    • 1
    • 2
    • 3
    • 4
    • 5

    2.2. 注意

    1. 关于纹理数组的创建,也可以使用Graphics.CopyTexture()这个接口。这个接口是纯走GPU端的,效率应该回更高。
    2. 纹理数组这个特性在低端显卡上可能不支持,但是不一定就会非常耗费性能。可以考虑通过纹理数组的方式来合并渲染的批次。
    3. 纹理数组个数的限制并不是纹理单元个数。实际上一个纹理数组只会绑定到一个纹理单元上,而在本人GTX 1660 Ti的显卡上,纹理数组个数的限制是4096个。

    3. 参考

    1. Texture2DArray 功能测试
    2. What are the limits of texture array size?

    代码地址

  • 相关阅读:
    每日4道算法题——第031天
    提效小技巧——记录那些不常用的代码片段
    Zabbix监控平台部署流程
    数据库实验八 视图
    一口气说出 Synchronized 同步方法的八种使用场景
    excel中通过ROW函数返回引用的行号
    windows10 :VMware安装Centos7图文
    1.3 特殊的矩阵乘法
    MySQL 面试题——数据库理论基础
    【Oralce】导出所有表名、表注释、创建时间、最后修改时间、主键
  • 原文地址:https://blog.csdn.net/charlee44/article/details/126012289