Texture2DArray 功能测试

在使用GPU Instancing时,如果要给每个物体设置不同的贴图,直觉的想法是使用 MaterialPropertyBlock对象设置一个Texture对象数组,类似 GPU Instancing测试 这篇里写给每个物体设置不同颜色的方式(MaterialPropertyBlock.SetVectorArray),如果有一个MaterialPropertyBlock.SetTextureArray之类的方法就完美了,但是实际上并没有,所以需要用其他方式来实现,这时候应该让 Texture2DArray 来发挥作用。

效果:每次点击按钮传递一个不同的Index到Shader,用来从Texture2DArray中读取不同的图片。
使用了四张图

每次点击传递不同的Index到Shader

Texture2DArray 类

看名字可以大概猜到这个类的对象可以包含多个Texture2D对象,类似Texture2D对象的数组。用Texture2DArray对象来实现给每个物体一个不同的贴图的思路是:

  1. 在C#中定义一个Texture2D数组
  2. 再定义一个Texture2DArray对象,用Texture2D数组内容来初始化Texture2DArray对象
  3. 把Texture2DArray对象作为Texture传递给Shader
  4. 通过一个索引值(_Index)来控制Shader中读取Texture2DArray对象里的哪一张图

思路清晰以后就直接上代码:

using UnityEngine;
using UnityEngine.Rendering;

public class Tex2DArrayTest : MonoBehaviour
{
    public MeshRenderer render;
    public Texture2D[] textures;
    public ECopyTexMethpd copyTexMethod;                // 把Texrure2D信息拷贝到Texture2DArray对象中使用的方式 //

    public enum ECopyTexMethpd
    {
        CopyTexture = 0,                                 // 使用 Graphics.CopyTexture 方法 //
        SetPexels = 1,                                      // 使用 Texture2DArray.SetPixels 方法 //
    }

    private Material m_mat;

    void Start()
    {
        if (textures == null || textures.Length == 0)
        {
            enabled = false;
            return;
        }

        if (SystemInfo.copyTextureSupport == CopyTextureSupport.None ||
            !SystemInfo.supports2DArrayTextures)
        {
            enabled = false;
            return;
        }

        Texture2DArray texArr = new Texture2DArray(textures[0].width, textures[0].width, textures.Length, textures[0].format, false, false);

        // 结论 //
        // Graphics.CopyTexture耗时(单位:Tick): 5914, 8092, 6807, 5706, 5993, 5865, 6104, 5780 //
        // Texture2DArray.SetPixels耗时(单位:Tick): 253608, 255041, 225135, 256947, 260036, 295523, 250641, 266044 //
        // Graphics.CopyTexture 明显快于 Texture2DArray.SetPixels 方法 //
        // Texture2DArray.SetPixels 方法的耗时大约是 Graphics.CopyTexture 的50倍左右 //
        // Texture2DArray.SetPixels 耗时的原因是需要把像素数据从cpu传到gpu, 原文: Call Apply to actually upload the changed pixels to the graphics card //
        // 而Graphics.CopyTexture只在gpu端进行操作, 原文: operates on GPU-side data exclusively //

        //using (Timer timer = new Timer(Timer.ETimerLogType.Tick))
        //{
            if (copyTexMethod == ECopyTexMethpd.CopyTexture)
            {
                for (int i = 0; i < textures.Length; i++)
                {
                    // 以下两行都可以 //
                    //Graphics.CopyTexture(textures[i], 0, texArr, i);
                    Graphics.CopyTexture(textures[i], 0, 0, texArr, i, 0);
                }
            }
            else if (copyTexMethod == ECopyTexMethpd.SetPexels)
            {
                for (int i = 0; i < textures.Length; i++)
                {
                    // 以下两行都可以 //
                    //texArr.SetPixels(textures[i].GetPixels(), i);
                    texArr.SetPixels(textures[i].GetPixels(), i, 0);
                }

                texArr.Apply();
            }
        //}

        texArr.wrapMode = TextureWrapMode.Clamp;
        texArr.filterMode = FilterMode.Bilinear;

        m_mat = render.material;

        m_mat.SetTexture("_TexArr", texArr);
        m_mat.SetFloat("_Index", Random.Range(0, textures.Length));

        //AssetDatabase.CreateAsset(texArr, "Assets/RogueX/Prefab/texArray.asset");
    }

    void OnGUI()
    {
        if (GUI.Button(new Rect(0, 0, 200, 100), "Change Texture"))
        {
            m_mat.SetFloat("_Index", Random.Range(0, textures.Length));
        }
    }
}

Shader部分:

Shader "MJ/Texture2DArray"
{
	Properties
	{
		_TexArr ("Texture Array", 2DArray) = "" {}
		_Index("Texture Array Index", Range(0,4)) = 0
	}

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

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

			// 会提示警告: Unrecognized #pragma directive: require at line 24
			// #pragma require 2darray

			UNITY_DECLARE_TEX2DARRAY(_TexArr);
			int _Index;

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

			struct v2f
			{
				float4 pos : SV_POSITION;
				float3 uv : TEXCOORD0;
			};

			v2f vert (appdata v)
			{
				v2f o;

				o.pos = UnityObjectToClipPos(v.vertex);
				o.uv = v.uv;
				return o;
			}

			fixed4 frag (v2f i) : SV_Target
			{
				return UNITY_SAMPLE_TEX2DARRAY(_TexArr, float3(i.uv.xy, _Index));
			}
			ENDCG
		}
	}

	Fallback Off
}

需要注意的地方有:

  1. Texture2DArray使用的图片需要保证大小相同,格式一致,并且开启了 read/write enabled 选项
  2. Graphics.CopyTexture和Texture2DArray.SetPixels,这两种方法都可以把图像信息传给Texture2DArray对象的每一个子Texture2D。整体上Graphics.CopyTexture 方法要比 Texture2DArray.SetPixels 快的多,大概是40到50倍的样子,原因大概是 Graphics.CopyTexture 只在GPU端进行操作,而Texture2DArray.SetPixels在CPU端操作,操作结束后需要调用Apply方法把图片数据传给GPU,所以比较耗时,Texture2DArray.Apply文档 中也建议如果不需要在CPU上读取像素信息的话建议使用更快的 Graphics.CopyTexture 方法。
参考链接:
https://www.reddit.com/r/Unity3D/comments/6uueox/gpu_instancing_texture2darray/
https://forum.unity.com/threads/instance-of-texture.500408/
https://docs.unity3d.com/Manual/SL-TextureArrays.html
https://docs.unity3d.com/ScriptReference/Texture2DArray.Apply.html
https://docs.unity3d.com/ScriptReference/Graphics.CopyTexture.html
https://blog.youkuaiyun.com/aa20274270/article/details/64923942
https://www.cnblogs.com/hont/p/7258615.html
<think>好的,用户现在问的是关于`r.AllowTexture2DArrayCreation`这个控制台变量。我需要先回忆一下之前回答类似问题时的结构,确保这次也保持清晰和详细。 首先,用户可能是游戏开发者,特别是使用虚幻引擎的开发者。他们可能在优化渲染性能或者处理纹理相关的问题时遇到了这个变量,所以需要了解它的作用和使用方法。 接下来,我需要确认这个变量的基本信息,比如所属引擎部分、功能范畴。记得之前提到过`r.`前缀属于渲染系统变量,所以这里应该类似。功能方面,Texture2D数组创建,可能涉及纹理管理和资源分配。 然后,作用解析部分需要详细说明它的核心行为,比如控制是否允许创建2D纹理数组。需要解释纹理数组的概念,比如在渲染中的应用,比如减少draw calls,优化性能。同时,默认状态和启用后的影响也很重要,比如内存使用和兼容性问题。 性能与精度权衡部分,可能需要比较启用和禁用的优缺点。例如,启用后可以提升性能,但增加内存占用,或者在某些设备上不支持。这里需要结合典型应用场景,比如地形系统、材质变体、体积雾等,说明使用场景的实际好处。 配置与调试部分要给出具体的控制台指令,可能包括运行时设置和项目配置。同时,兼容性检查也是关键,比如需要检查图形API的支持情况,比如OpenGL ES可能不支持,或者需要特定版本。 潜在风险部分需要提醒用户可能的问题,比如内存不足、兼容性问题导致的回退,或者渲染错误。这部分帮助用户预见到可能的坑,避免出现问题。 实践建议方面,可以推荐用户先进行性能分析,使用相关命令查看纹理内存,测试目标平台的支持情况,或者按需启用。例如,移动端可能需要更谨慎,而高端PC可以更积极使用。 最后,技术原理示意图可能需要用文字描述纹理数组的结构和创建流程,帮助用户直观理解。源码参考部分,可能需要指出相关的引擎代码文件,方便深入研究的用户查看。 需要注意用户可能的需求不仅仅是了解变量本身,而是如何在实际项目中应用和优化。因此,回答要兼顾理论和实践,提供足够的细节和实用建议。同时,保持结构清晰,分点说明,便于用户快速找到所需信息。 检查是否有遗漏的部分,比如变量是否需要重启,或者是否与其他控制台变量联动。例如,是否依赖其他渲染设置,或者是否在特定渲染路径下有效。此外,是否有版本差异,比如这个变量在UE4和UE5中的表现是否不同,可能需要提及,但如果不确定,可以建议查阅对应版本的文档。 最后,确保语言准确,避免技术错误,比如正确解释纹理数组的作用,不混淆于其他纹理类型如Texture2D或Texture3D。同时,使用中文术语,保持专业性和易懂性。</think>关于 `r.AllowTexture2DArrayCreation` 控制台变量,以下是技术解析: --- ### 1. **基础信息** - **所属引擎**:Unreal Engine 渲染系统(`r.` 前缀表示渲染相关变量) - **功能范畴**:控制 **2D纹理数组(Texture2D Array)** 的创建权限,影响纹理资源管理策略。 --- ### 2. **作用解析** #### 核心行为 - **纹理数组定义**: 允许将多个 **2D纹理(Texture2D)** 打包为单一GPU资源(类似“纹理图集”但更高效),共享相同尺寸与格式。 - **创建机制**: 启用(`=1`)时,引擎可自动或按需生成2D纹理数组;禁用(`=0`)则强制使用独立纹理。 - **默认策略**: 通常根据项目设置和平台能力动态调整(例如移动端默认禁用以节省内存)。 --- ### 3. **性能与精度权衡** | 状态 | 优势 | 劣势 | |-----------|-------------------------------|---------------------------| | **启用** | 减少纹理切换 → 降低DrawCall | 内存占用增加(需预分配固定槽位) | | | 支持纹理数组采样指令优化 | 部分老旧GPU不支持 | | **禁用** | 节省显存(按需加载独立纹理) | 频繁纹理切换 → 渲染性能下降 | --- ### 4. **典型应用场景** - **地形系统**: 混合多层材质(如泥土、草地、岩石)时,通过纹理数组统一管理。 - **材质变体**: 角色皮肤/服装的细节贴图动态切换(如破损、干净状态)。 - **体积雾/粒子**: 多噪声纹理的批次采样优化。 - **虚拟纹理(VT)**: 作为虚拟纹理流送的中间存储结构。 --- ### 5. **配置与调试** #### 控制台指令 ```cpp // 运行时启用(部分情况需重启渲染器) r.AllowTexture2DArrayCreation 1 // 恢复默认 r.AllowTexture2DArrayCreation 0 ``` #### 项目设置(DefaultEngine.ini) ```ini [ConsoleVariables] r.AllowTexture2DArrayCreation=1 ``` #### 兼容性检查 - 需满足 **图形API支持**(如DirectX 10+/Vulkan/Metal,OpenGL ES 3.1+部分支持)。 - 与 `r.VirtualTexturedLightmaps` 联用时需额外验证。 --- ### 6. **潜在风险** - **内存碎片化**: 频繁动态创建/释放纹理数组可能导致显存管理效率下降。 - **兼容性回退**: 在不支持纹理数组的硬件上,引擎会回退到独立纹理,可能引发材质编译错误。 - **Mipmap错位**: 若纹理数组内各子纹理的Mip链不一致,可能导致采样异常。 --- ### 7. **实践建议** 1. **按平台差异化配置**: 在高端PC/主机启用,移动端根据设备等级选择性启用。 2. **内存监控**: 使用 `stat memory` 或 `r.TextureMemoryStats` 跟踪纹理数组内存消耗。 3. **材质适配**: 在材质蓝图中通过 `Texture2DArray` 节点显式指定采样逻辑。 4. **流送优化**: 结合 `r.Streaming.PoolSize` 调整纹理流送池,避免纹理数组阻塞资源加载。 --- ### 附:技术原理示意图 ```plaintext [纹理数组创建流程] 1. 资源检测 → 2. 匹配尺寸/格式 → 3. 分配数组槽位 ↗ 启用 AllowTexture2DArrayCreation → 打包为数组 分支 ↘ 禁用 → 保持独立纹理 ``` 可通过引擎源码 `Engine/Source/Runtime/Engine/Private/TextureResource.cpp` 分析具体实现逻辑。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值