An editor pipeline for authoring and managing Texture2DArray assets in URP projects. Combine dozens of individual textures into a single GPU array, reduce draw calls through batching, and keep your build clean — source textures and the definition asset never enter the final player build.
When rendering many objects that share the same texture set but differ in which texture they display — think building facades, tile variants, character skins — Unity issues a separate draw call per unique material. The standard fix (texture atlases) requires UV remapping and complicates authoring. Texture2DArray keeps UVs intact and lets the GPU pick the right slice on the fly, enabling GPU instancing across the entire set.
Without Texture Array Pipeline, each variant needs its own material → its own texture → its own draw call:
| Before | After |
|---|---|
![]() |
![]() |
| Before | After |
|---|---|
![]() |
![]() |
- One-click
Texture2DArraybuilder — drag textures into the definition asset, press Generate, done. - Automatic format normalization — Quick Mode auto-detects the best
GraphicsFormat; Advanced Mode lets you pin an exact format. - Automatic resolution normalization — mismatched source textures are reimported to the target resolution before the array is assembled.
- GPU-accelerated scaling — when sizes differ, a
RenderTextureblit path scales textures on the GPU and optionally re-compresses them. - Editor-only definition asset —
TextureArrayDefinitioncarriesHideFlags.DontSaveInBuild. The ScriptableObject and the original source textures are never included in player builds; only the embeddedTexture2DArraysub-asset is referenced by materials and therefore ends up in the build. - Mesh Baker — an editor window (
Tools → Texture Array → Mesh Baker) that encodes the array slice index into mesh vertex color (channel R, 0–255), re-exports the FBX via Unity's FBX Exporter, and remaps materials on the prefab automatically. - Custom URP shaders —
LitArray(full PBR) andSimpleLitArray(Blinn-Phong) read the slice index fromvertexColor.rin the fragment shader, enabling GPU instancing with zero per-instance material overhead. - Pre-build validation — format mismatches and missing mip chains are reported as errors before the array is written.
TextureArrayDefinition is a ScriptableObject that stores:
- A list of source texture GUIDs (not direct
Texture2Dreferences, so moves/renames don't break the list). - Build settings: target resolution,
GraphicsFormat, mip/filter/wrap options, normal-map flags. - The timestamp of the last successful build (used for stale detection).
Because it is flagged DontSaveInBuild, Unity's build pipeline strips it entirely. Only the embedded Texture2DArray sub-asset reaches the player.
Source GUIDs
│
▼
ResolveTextures — GUID → Texture2D, report missing
│
▼
NormalizeImporters — set maxTextureSize, fix NormalMap type, reimport
│
▼
NormalizeCompression — Quick: pick highest-quality format present
Advanced: enforce a specific GraphicsFormat
│
▼
Validate — formats must match; mip chains must exist
│
▼
BuildArray
├─ Fast path — Graphics.CopyTexture (GPU, zero CPU memory)
└─ Scaling path — Graphics.Blit → ReadPixels → CompressTexture
│
▼
EmbedSubAsset — AddObjectToAsset, replace existing sub-asset
│
▼
RecordBuildTime — persist ticks for stale check
Why this is better than a texture atlas:
| Atlas | Texture2DArray | |
|---|---|---|
| UV remapping | ❌ Required | ✅ Not needed |
| GPU instancing | ❌ Limited (per material) | ✅ One material, many slices |
| Mip bleeding | ❌ Needs padding/fixes | ✅ No issues |
| Source textures in build | ❌ Always included | ✅ Only array included |
| Authoring complexity | ❌ High | ✅ Low |
Each mesh submesh gets a vertex color written into channel R (integer 0–255, stored as R / 255.0 in the color attribute). The vertex shader passes it to the fragment shader via Varyings.color. The fragment shader decodes it:
// LitInput.hlsl
int DecodeArrayIndexFromColor(float4 color)
{
return (int)(color.r * 255.0 + 0.5);
}
// Usage in fragment
int arrayIndex = DecodeArrayIndexFromColor(input.color);
half4 albedo = SAMPLE_TEXTURE2D_ARRAY(_BaseMapArray, sampler_BaseMapArray, uv, arrayIndex);This means a single material covers the entire set. GPU instancing is unrestricted because there is no per-instance texture difference — the slice is baked into the geometry.
The Mesh Baker window automates the per-vertex encoding:
- You assign a
Rendererfrom a prefab asset or prefab instance. - It scans all submeshes for materials using
LitArray/SimpleLitArray. - You pick a slice index (0–N) per slot via an int slider — the result is previewed live in the Scene View against a hidden working clone.
- Refresh Slots re-scans the Renderer; Reset All Slots zeros all indices.
- Export to FBX writes the indices into the vertex color channel R, overwrites the source FBX via Unity's FBX Exporter, and remaps materials on the prefab asset.
Right-click in the Project window → Create → TextureArrayDefinition
- Open the asset inspector.
- Drag your source textures into the list (or use the + button).
- Choose Quick Mode (pick a resolution preset) or Advanced Mode (full control over resolution,
GraphicsFormat, mipmaps, filter, wrap, normal-map settings). - Click Generate Texture Array.
The inspector shows a green status when the array is up to date and an orange Stale warning when any source texture has been modified since the last build.
- Create a material using Universal Render Pipeline/LitArray or Universal Render Pipeline/SimpleLitArray.
- Drag the definition asset's embedded
Texture2DArraysub-asset into the Albedo Array slot.
Before opening the Mesh Baker, configure your prefab so that:
- Each submesh's material slot already uses LitArray or SimpleLitArray.
- Materials are assigned in the order that matches the desired slice mapping — the Baker reads the material list as-is and only surfaces slots whose material uses one of the supported shaders.
- Open Tools → Texture Array → Mesh Baker.
- Drag the prefab (or a prefab instance from the scene) into the Renderer field — the Baker will resolve the source FBX and the prefab asset path automatically.
- The window lists every submesh slot whose material uses
LitArray/SimpleLitArray. Use the int slider next to each slot to set the desired slice index (0–N). The result is previewed live in the Scene View immediately — no button needed. - Click Save Changes — the Baker writes the slice indices into the vertex color channel R of the working mesh, overwrites the source FBX via Unity's FBX Exporter, and remaps materials on the prefab asset.
The _Project/Scenes/PrefabStressTest sample scene contains a PrefabStressTest component. Right-click it in the Inspector and choose Spawn Default (individual materials) or Spawn Array (single shared material) to compare GPU stats directly in the Editor.
- Open Window → Package Manager.
- Click the + button → Add package from git URL…
- Enter:
https://github.com/dexsper/TextureArrayPipeline.git?path=src/Assets/Plugins/TextureArrayPipeline
- Click Add. UPM will download the package and resolve its dependencies automatically.
Add the following entry to Packages/manifest.json:
{
"dependencies": {
"com.dexsper.texarraypipeline": "https://github.com/dexsper/TextureArrayPipeline.git?path=src/Assets/Plugins/TextureArrayPipeline"
}
}| Requirement | Minimum version |
|---|---|
| Unity Editor | 2022.3 LTS |
| Universal Render Pipeline | 14.0.12 (com.unity.render-pipelines.universal) |
| FBX Exporter | 4.2.1 (com.unity.formats.fbx) |
Both URP and FBX Exporter are declared as hard dependencies in package.json and will be installed automatically by UPM.
- Shaders support
Texture2DArrayon Base Map only. Metallic, Normal, Occlusion, Emission, and Detail maps are standardTexture2Dproperties shared across all slices. This is sufficient for use cases where only the albedo varies per object (building facades, color variants, etc.), but not for full per-slice PBR. - Maximum 256 slices per array — the slice index is encoded as a single
uint8(vertex color R channel, 0–255). - Desktop / console only for the fast build path —
Graphics.CopyTexturerequiresCopyTextureSupporton the platform. The scaling path is used as a fallback but requiresRenderTexturesupport. - FBX Exporter rewrites the source FBX — the Mesh Baker overwrites the original
.fbxfile on disk. Keep source files in version control before baking.
MIT © Dexsper



