开启 (Read/Write = True):CPU 可以读写贴图像素数据,贴图会在内存一份、GPU显存一份。
关闭 (Read/Write = False,默认):CPU 无法访问贴图像素数据,贴图只存GPU 显存一份。
Read/Write什么时候开启?
读取像素:用
GetPixels获取颜色数据,用于截图保存、地图编辑等修改像素:用
SetPixels运行时动态绘制贴图(如画板功能),修改后需调用Apply()上传至 GPU生成资产:脚本创建 Texture2D 后调用
EncodeToPNG保存到本地
Texture2D.Apply()
当你 new Texture2D()后,不会在GPU分配显存,会首先在系统内存(CPU侧)创建纹理对象的数据容器。
内存分配:构造函数会立即在系统内存中分配像素数据数组(可通过
GetPixels等直接访问)。显存分配:只有当你调用
Apply()方法后,数据才会被上传到GPU显存。如果不调用Apply(),纹理只存在于系统内存中,无法被正确渲染显示。调用
Apply()的核心作用,就是将当前系统内存中的像素数据同步(上传/更新)到GPU显存中。问题:为什么
new Texture2D()后,即使没分配显存,为什么也能在Game窗口中看到?(是纯白的图像)
理论上的运行时行为:如果这是一个打包后的游戏(Build出来的exe),没有调用
Apply(),贴图绝对不会显示,或者显示为紫色/黑色错误材质。因为GPU显存里根本没有数据。编辑器中的“魔法”:Unity编辑器为了提升开发体验,在某些情况下会“好心”地帮你自动调用
Apply()。当它检测到你将一个未Apply的Texture2D赋值给材质时,编辑器可能会强制同步数据到GPU,让你能在Scene/Game视图里看到效果,方便调试。
new Texture2D()之后,要显示就一定要Apply()。编辑器里的“显示”只是一个为了方便调试的假象。Read/Write 可以在构建时设定一次,构建出来Texture2D后,Read/Write就不可设定了。
在Apply后释放内存:
直接在
Apply时将makeNoLongerReadable参数设为true,Unity会在数据上传至GPU后,立即自动释放系统内存中的那份副本。// 修改像素后,上传数据并立即释放CPU内存 texture2D.Apply(true, true);参数含义:第一个
true是更新Mipmap;第二个true就是“不再让CPU读取”,上传后丢掉内存副本。
Read / Write 默认设置:
| 场景 | Read / Write | 原因 |
|---|---|---|
| Unity 导入 Assets | 不勾选 (False) | 省内存:上传到 GPU 后自动丢弃系统内存副本。如果不需要用 CPU 读写像素,保持默认即可。 |
动态创建(new Texture2D) | 默认开启 (True), 可在构造函数中设定 | 保功能:创建时会在系统内存保留数据,方便你随时调用GetPixels或SetPixels。 |
Read / Write 如果为false,如果硬要读取时怎么办?
如果贴图未开启 Read/Write,官方方案是通过RenderTexture间接读取(无需修改原贴图设置):
创建临时 RenderTexture
用
Graphics.Blit将贴图画到 RenderTexture 上用
ReadPixels从 RenderTexture 读取像素到新 Texture2D⚠️ 此方法有额外性能开销,不适合频繁操作,适合偶发的截图或工具开发。
Texture2D 的 LoadImage 方法:
目的是从 byte[]获取像素数据创建出 Texture2D。
有两点需要注意:
① 它会自己调用 Apply,无需手动再调用一次
② LoadImage 方法有一个重载版本:
public bool LoadImage(byte[] data, bool markNonReadable);
设为 true → 告诉 Unity 你不需要在 CPU 端读写像素数据 → Unity 可以尝试释放 CPU 端的拷贝
· 设为 false(默认) → Unity 保留 CPU 端可读写拷贝