1. 概述

个人认为,纹理数组是一个非常有用的图形特性。纹理本质上是一个二维的图形数据;通过纹理数组,给图形数据再加上了一个维度。这无疑会带来一个巨大的性能提升:一次性传输大量的数据总是比分批次传输数据要快。

2. 详论

2.1. 实现

创建一个GameObject对象,并且加入Mesh Filter组件和Mesh Renderer组件。Mesh Filter我们可以设置Mesh为Quad,同时在Mesh Filter上挂一个我们新建的材质:

在这个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):

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?

代码地址

Unity3D学习笔记10——纹理数组的更多相关文章

  1. Unity3D学习笔记2——绘制一个带纹理的面

    目录 1. 概述 2. 详论 2.1. 网格(Mesh) 2.1.1. 顶点 2.1.2. 顶点索引 2.2. 材质(Material) 2.2.1. 创建材质 2.2.2. 使用材质 2.3. 光照 ...

  2. Unity3D学习笔记12——渲染纹理

    目录 1. 概述 2. 详论 3. 问题 1. 概述 在文章<Unity3D学习笔记11--后处理>中论述了后处理是帧缓存(Framebuffer)技术实现之一:而另外一个帧缓存技术实现就 ...

  3. Unity3D学习笔记8——GPU实例化(3)

    目录 1. 概述 2. 详论 2.1. 自动实例化 2.2. MaterialPropertyBlock 3. 参考 1. 概述 在前两篇文章<Unity3D学习笔记6--GPU实例化(1)&g ...

  4. thinkphp学习笔记10—看不懂的路由规则

    原文:thinkphp学习笔记10-看不懂的路由规则 路由这部分貌似在实际工作中没有怎么设计过,只是在用默认的设置,在手册里面看到部分,艰涩难懂. 1.路由定义 要使用路由功能需要支持PATH_INF ...

  5. 《C++ Primer Plus》学习笔记10

    <C++ Primer Plus>学习笔记10 <<<<<<<<<<<<<<<<<&l ...

  6. Spring MVC 学习笔记10 —— 实现简单的用户管理(4.3)用户登录显示全局异常信息

    </pre>Spring MVC 学习笔记10 -- 实现简单的用户管理(4.3)用户登录--显示全局异常信息<p></p><p></p>& ...

  7. Go语言学习笔记八: 数组

    Go语言学习笔记八: 数组 数组地球人都知道.所以只说说Go语言的特殊(奇葩)写法. 我一直在想一个人参与了两种语言的设计,但是最后两种语言的语法差异这么大.这是自己否定自己么,为什么不与之前统一一下 ...

  8. matlab学习笔记10 一般运算符

    一起来学matlab-matlab学习笔记10 10_1一般运算符 觉得有用的话,欢迎一起讨论相互学习~Follow Me 参考书籍 <matlab 程序设计与综合应用>张德丰等著 感谢张 ...

  9. unity3d学习笔记(一) 第一人称视角实现和倒计时实现

    unity3d学习笔记(一) 第一人称视角实现和倒计时实现 1. 第一人称视角 (1)让mainCamera和player(视角对象)同步在一起 因为我们的player是生成的,所以不能把mainCa ...

随机推荐

  1. kNN-识别手写数字

    最后,我们要进行手写数字分类任务,但是现在我们是用kNN算法,可能会比较慢 首先,完整地看完2.3.1和2.3.2的内容,然后找到trainingDigits和testDigits文件夹,大致浏览下 ...

  2. Java线程池ThreadPoolExecutor极简教程

    ThreadPoolExecutor 简介 ThreadPoolExecutor 是 java.util.concurrent 包下的一个类,在jdk1.5版本引入,帮助开发人员管理线程并方便地执行并 ...

  3. 【Java面试】如何中断一个正在运行的线程?

    一个去京东面试的工作了5年的粉丝来找我说: Mic老师,你说并发编程很重要,果然我今天又挂在一道并发编程的面试题上了. 我问他问题是什么,他说:"如何中断一个正在运行中的线程?". ...

  4. Vben Admin 源码学习:项目初始化

    0x00 前言 Vue-Vben-Admin 是一个免费开源的中后台模版.使用了最新的vue3,vite2,TypeScript等主流技术开发,开箱即用的中后台前端解决方案考. 本系列本着学习参考的目 ...

  5. 使用 Nocalhost 开发 Rainbond 上的微服务应用

    本文将介绍如何使用 Nocalhost 快速开发 Rainbond 上的微服务应用的开发流程以及实践操作步骤. Nocalhost 可以直接在 Kubernetes 中开发应用,Rainbond 可以 ...

  6. Servlet的本质

    简介:Java Servlet 是运行在 Web 服务器或应用服务器上的程序,它是作为来自 Web 浏览器或其他 HTTP 客户端的请求和 HTTP 服务器上的数据库或应用程序之间的中间层. 功能:使 ...

  7. Docker打包镜像并上传

    Docker打包镜像并上传 登录 账号 docker login --username=yourusername 密码 yourPassword 推送到仓库 docker镜像打标签 docker ta ...

  8. Navicat 连接 MySQL

    目录 简述 新建连接 常见错误 简述 Navicat 是一套快速.可靠和全面的数据库管理工具,专门用于简化数据库管理和降低管理成本.Navicat 图形界面直观,提供简便的管理方法,设计和操作 MyS ...

  9. 总结 到 GDOI 2021 这个阶段

    截止本蒟蒻第一次体验省选(虽然是普及组) 本蒟蒻已经有了许多收获,却也有很多不足 优点 对一些学过的知识掌握还行 没了 缺点 会却做不出来 有一些题不难,却想不到正解 如 Day2 T1 ,就是移一下 ...

  10. python创建分类器小结

    简介:分类是指利用数据的特性将其分成若干类型的过程. 监督学习分类器就是用带标记的训练数据建立一个模型,然后对未知数据进行分类. 一.简单分类器 首先,用numpy创建一些基本的数据,我们创建了8个点 ...