← Back to Blog 3d-modeling

Channel Packing PBR Textures in Blender: ORM Maps for Unreal Engine 5 and Unity

By BitSoul Team7/8/2026Updated 8/1/20265 min read47 views
Channel Packing PBR Textures in Blender: ORM Maps for Unreal Engine 5 and Unity

Every extra texture you send to the GPU costs memory bandwidth and a sampler slot. If you're still shipping separate Ambient Occlusion, Roughness, and Metallic maps as three individual files, you're leaving performance on the table. Channel packing solves this by merging all three into a single image — one ORM texture that Unreal Engine 5 and Unity both consume natively. Here's the complete workflow from Blender to engine.

What Is Channel Packing and Why It Matters

Channel packing is the practice of storing independent grayscale data in the Red, Green, and Blue channels of a single RGB image. For PBR workflows the standard layout is:

This is the ORM convention used by Unreal Engine 5's default material functions and Unity's Lit shader. The alternative — separate R, G, and M textures — wastes two texture sample instructions per material, increases VRAM pressure, and can push you over platform sampler limits on mobile. A single 2K ORM map consumes exactly the same memory as one 2K grayscale map while encoding three channels of data.

For a project with 50 unique materials, the difference is 100 fewer texture fetches per fragment shader invocation. On mobile or VR targets this is not optional optimization — it's a requirement.

Building a Channel-Pack Node Group in Blender

Building a Channel-Pack Node Group in Blender — illustrated

Blender's Compositor is the right tool for channel packing — it operates on full-resolution image buffers without material overhead. Here's the exact setup:

  1. Open the Compositor workspace and enable Use Nodes.
  2. Add three Image input nodes: one for AO, one for Roughness, one for Metallic. Load your baked grayscale maps into each.
  3. Add a Combine RGBA node (`Shift+A → Converter → Combine Color`, then switch to RGBA mode).
  4. Connect each Image node's output to `Combine RGBA` as follows: AO → R, Roughness → G, Metallic → B. Leave A at 1.0 or connect a white constant.
  5. Add a Composite output node and a File Output node. Set the File Output to PNG (lossless) or 16-bit TIFF if you need precision beyond 8-bit.

```python
# Blender Python: automate ORM packing via bpy
import bpy

def pack_orm(ao_path, rough_path, metal_path, output_path):
scene = bpy.context.scene
scene.use_nodes = True
tree = scene.node_tree
nodes = tree.nodes
links = tree.links
nodes.clear()

def img_node(path, label):
n = nodes.new('CompositorNodeImage')
n.image = bpy.data.images.load(path)
n.label = label
return n

ao = img_node(ao_path, 'AO')
rough = img_node(rough_path, 'Roughness')
metal = img_node(metal_path, 'Metallic')

combine = nodes.new('CompositorNodeCombRGBA')
links.new(ao.outputs['Image'], combine.inputs['R'])
links.new(rough.outputs['Image'], combine.inputs['G'])
links.new(metal.outputs['Image'], combine.inputs['B'])

out = nodes.new('CompositorNodeOutputFile')
out.base_path = output_path
out.file_slots[0].path = 'ORM_'
out.format.file_format = 'PNG'
links.new(combine.outputs['Image'], out.inputs[0])

bpy.ops.render.render(use_viewport=False)

pack_orm('/tmp/ao.png', '/tmp/roughness.png', '/tmp/metallic.png', '/tmp/')
```

Run this from Blender's scripting workspace. The result is a single PNG with AO in Red, Roughness in Green, Metallic in Blue — ready to import into any engine.

Critical: always bake individual maps at the highest quality your pipeline allows before packing. Packing never compensates for bake errors — fix seams and artifacts in the individual maps first.

Exporting ORM Maps from Blender

Before packing, ensure your baked maps are consistent:

| Map | Color Space | Expected Range | Common Errors |
|---|---|---|---|
| AO | Non-Color | 0.0–1.0 | Using sRGB gamma — darkens incorrectly |
| Roughness | Non-Color | 0.0–1.0 | Inverted (smooth=1, rough=0) for some engines |
| Metallic | Non-Color | 0.0 or 1.0 | Mid-gray values cause engine validation warnings |

Set all bake image color spaces to Non-Color in Blender's Image Editor before baking. This prevents sRGB gamma from corrupting your linear data.

For the packed ORM output, export as PNG with 8-bit depth for most assets. Use 16-bit only for assets where AO or roughness precision is visually critical (character skin, hero props). 16-bit doubles file size with minimal visual gain on most geometry.

Importing ORM Textures into Unreal Engine 5

Importing ORM Textures into Unreal Engine 5 — illustrated

UE5's M_ORM material function handles packed textures natively. Here's the import checklist:

Texture import settings (Content Browser):
- Set sRGB to false — the ORM texture is linear data, not color
- Set Compression Settings to Masks (this selects BC4/BC5 compression appropriate for non-color data)
- MipGen Settings: DefaultMips works correctly; do not use NormalMap settings

Material setup:
1. In the Material Editor, add a `Texture Sample` node and plug your ORM texture in
2. Route outputs: `R → AmbientOcclusion`, `G → Roughness`, `B → Metallic`
3. Or use UE5's built-in `M_ORM` material function from the Engine Content folder — it expects exactly this layout

For reuse across many assets, build a Master Material with a single ORM TextureParameter so you can create Material Instances without touching shader logic. See BitSoul's marketplace for GLB assets that already ship ORM-packed textures ready for this workflow.

Importing ORM Textures into Unity URP

Unity's Lit shader stores its packed map differently — it uses the Mask Map with this layout:

This is not the same as UE5's ORM order. You have two options:

Option A — Repack for Unity layout in Blender Compositor:
Swap your combine node connections: Metallic → R, AO → G, (white) → B, and for the Alpha channel connect Roughness through a Invert node (Math → Subtract: 1 − Roughness) to get Smoothness.

Option B — Use Blender's multi-output File node:
Bake a separate ORM (UE5 layout) and a separate Mask Map (Unity layout) from the same source data in one Compositor pass using two File Output nodes.

In Unity, set the Mask Map texture's sRGB (Color Texture) checkbox to off. Import as Default compression — BC7 on desktop, ASTC on mobile.

Smoothness from Roughness inversion in Unity (HLSL reference):
```hlsl
float smoothness = 1.0 - roughnessSample;
```

This single line is the most common source of the "everything looks shiny" or "everything looks matte" bug when porting between engines.

Channel Packing Checklist

Before publishing an asset with ORM textures, verify each point:

Channel packing is a one-time workflow cost that pays off every time an asset hits the GPU. Set it up once in a reusable Blender Compositor node group or Python script, and every asset you ship will meet console and mobile texture budget requirements without rework.

Ready to grab professionally prepared GLB assets with pre-packed ORM maps? Browse the BitSoul marketplace for game-ready 3D models optimized for Unreal Engine 5, Unity URP, and Godot 4.

Tags: blender pbr texturing unreal-engine-5 unity game-assets workflow

Skip the modelling — download it instead

A free BitSoul account gets you 2 game-ready models every month plus 25 AI Engine credits to generate one of your own, no card required. Clean topology, PBR textures, and GLB downloads that drop straight into Unreal, Unity, Godot or Blender — plus OBJ and 3D-printable STL export.

Create a free account → Browse 846 models