Most game artists bake normal maps and call it done. But the difference between a flat-looking asset and one that reads convincingly in any lighting comes down to your full PBR texture suite — AO, roughness, metallic, and emission baked correctly and packed efficiently. This guide walks through the complete multi-channel bake workflow in Blender, from scene setup through export-ready textures for Unity, Unreal Engine 5, and Godot 4.
Setting Up Your Bake Scene in Blender
A clean bake starts with a clean scene. Before you open the Render Properties panel, get these five things right:
Render engine: Switch to Cycles. Eevee cannot bake. Set Device to GPU Compute if available — bakes run 5–20× faster on GPU.
High-poly and low-poly workflow: If you're baking detail from a high-poly mesh, make sure both meshes share the same origin point. Name them clearly (`MyProp_high`, `MyProp_low`). In the Bake panel, enable *Selected to Active* and set an Extrusion value of 0.01–0.05m depending on mesh complexity.
UV maps: Every mesh you bake must have a non-overlapping UV layout in the channel Blender will write to. Add a second UV map (`UVMap_Bake`) if your material UVs are tiled or overlapping. Select the bake UV map as active before baking.
Image node setup: In every material on the low-poly mesh, add an Image Texture node (Shift+A → Texture → Image Texture), create a new 2048×2048 or 4096×4096 image with 32-bit float enabled for AO and roughness bakes, and select that node WITHOUT connecting it to any other node. Blender bakes into whichever Image Texture node is selected, not connected.
Bake margin: Set Margin to at least 16px at 2K resolution to prevent texture bleeding across UV islands at runtime MIP levels.
![]()
Baking Ambient Occlusion and Roughness Maps
AO and roughness require different node setups before baking.
Ambient Occlusion
AO is the simplest bake — select the target Image Texture node in each material, set Bake Type to *Ambient Occlusion*, and hit Bake. No special node wiring needed. However, tune these settings first:
- Samples: 64–128 for final bakes. AO is noisy at low sample counts and that noise becomes permanent in the texture.
- Distance: Controls how far Blender casts AO rays. For a character at roughly 2m scale, 0.5m is a good starting point. Too high and everything goes dark; too low and crevices won't darken.
- Local only: Enable this to prevent other objects in the scene from influencing the bake — essential for assets you'll use in varied environments.
Roughness
Roughness is a *Emit* bake — you wire your roughness value into an Emission node and bake as Emit. This lets Blender write any procedural or texture-driven roughness value directly to the image:
```python
# Blender Python — set up roughness bake node chain
import bpy
for mat in bpy.data.materials:
mat.use_nodes = True
nodes = mat.node_tree.nodes
links = mat.node_tree.links
# Find the BSDF
bsdf = next((n for n in nodes if n.type == 'BSDF_PRINCIPLED'), None)
if not bsdf:
continue
# Add emission node
emit = nodes.new('ShaderNodeEmission')
emit.location = (bsdf.location.x - 200, bsdf.location.y - 200)
# Connect roughness socket output to emission color
rough_input = bsdf.inputs['Roughness']
if rough_input.links:
rough_node = rough_input.links[0].from_node
rough_socket = rough_input.links[0].from_socket
links.new(rough_socket, emit.inputs['Color'])
else:
# Constant roughness — drive through value node
val = nodes.new('ShaderNodeValue')
val.outputs[0].default_value = rough_input.default_value
links.new(val.outputs[0], emit.inputs['Color'])
# Wire emission to output
out = next(n for n in nodes if n.type == 'OUTPUT_MATERIAL')
links.new(emit.outputs['Emission'], out.inputs['Surface'])
```
After the Emit bake completes, rewire the output back to the BSDF — the script above connects temporarily for baking only.
Save the AO image as a 16-bit PNG (lossless). Save roughness as 8-bit PNG — it doesn't need float precision.
Baking Metallic and Emission Channels
Both metallic and emission use the same Emit bake approach as roughness.
Metallic
Metallic is a binary value in most PBR workflows (0.0 = dielectric, 1.0 = metal), but parts of an asset may blend — painted metal, rusted areas, or worn edges. Bake it the same way: wire the Metallic socket output into an Emission node, set Bake Type to Emit, bake, then restore the original node graph.
If your metallic value is a constant (e.g. the whole mesh is metal or all dielectric), don't bake it — just set the metallic value to 0 or 1 in Unity/Unreal directly and skip the texture slot entirely. You'll save memory and a sampler.
Emission
For glowing parts — screens, lava cracks, neon trim — bake emission directly:
- Set Bake Type to *Emit*
- Wire only the emission color into the material output (disconnect the BSDF)
- Bake to a dedicated emission image
- In Unity/Unreal, assign this as your emissive texture and enable bloom as needed
Important: If non-emissive parts of the mesh have a black emission (the default), those areas bake to pure black correctly — no masking needed.
![]()
Channel Packing Your Bakes for Unity, Unreal, and Godot
Each engine expects textures in specific channel layouts. Packing multiple maps into one RGBA image cuts texture memory and sampler count — essential for mobile and Quest targets.
| Engine | Packed Texture | R | G | B | A |
|---|---|---|---|---|---|
| Unity (URP/HDRP) | Mask Map | Metallic | AO | Detail | Smoothness |
| Unreal Engine 5 | ORM | AO | Roughness | Metallic | — |
| Godot 4 | ORM | AO | Roughness | Metallic | — |
Note: Unity's Mask Map uses *Smoothness* (1 - Roughness), not Roughness directly. Invert your roughness bake before packing for Unity.
Pack channels in Blender's compositor or use this Python snippet post-bake:
```python
import bpy
from mathutils import Vector
# Pack AO (R), Roughness (G), Metallic (B) into ORM image
ao_img = bpy.data.images['bake_ao']
rough_img = bpy.data.images['bake_roughness']
metal_img = bpy.data.images['bake_metallic']
width, height = ao_img.size
orm = bpy.data.images.new('ORM_Packed', width, height, alpha=False)
ao_px = list(ao_img.pixels)
rough_px = list(rough_img.pixels)
metal_px = list(metal_img.pixels)
orm_px = [0.0] * len(ao_px)
for i in range(0, len(ao_px), 4):
orm_px[i] = ao_px[i] # R = AO
orm_px[i+1] = rough_px[i] # G = Roughness
orm_px[i+2] = metal_px[i] # B = Metallic
orm_px[i+3] = 1.0 # A = 1.0
orm.pixels = orm_px
orm.filepath_raw = '//textures/ORM_Packed.png'
orm.file_format = 'PNG'
orm.save()
```
This gives you a single ORM texture compatible with Unreal Engine 5 and Godot 4 out of the box.
Exporting and Verifying Your Baked Assets
Once textures are baked and packed, run through this checklist before committing to your project:
- [ ] Color space is correct — AO, Roughness, Metallic, and ORM maps must be set to Non-Color in Blender's Image Texture node before export. Only Base Color and Emission use sRGB.
- [ ] No pure black AO — If your AO bake is entirely black, check that *Local Only* wasn't too aggressive or that your Extrusion value isn't clipping through geometry.
- [ ] Roughness range check — Open the baked roughness image in Blender's UV Editor and use the histogram. Values should spread across 0.1–0.9; a spike at 0 or 1 suggests a node wiring error.
- [ ] Seams are invisible — Rotate a preview mesh under a direct light in the engine. Visible seams mean your UV margin was too small or bake margin didn't match.
- [ ] File format — Export Base Color as PNG (sRGB). Export all data maps (Normal, AO, Roughness, Metallic, ORM) as PNG with no color correction. Use 8-bit for binary maps, 16-bit for AO.
- [ ] Texture resolution — Hero props: 4K. Background/env props: 2K or 1K. Characters: 4K body, 2K accessories.
You can find a growing library of game-ready assets with correctly baked PBR textures on BitSoul's marketplace — each asset ships with separated AO, roughness, metallic, and packed ORM textures where applicable, saving you hours of bake time on common prop types.
Putting It Together: The Full Bake Pipeline
Here's the complete bake order for a hero prop, from clean mesh to engine-ready textures:
- UV unwrap the low-poly with adequate margin (at least 4px/texel at target resolution)
- Bake Normal map first (Cycles, Selected to Active if using high-poly)
- Bake AO — 128 samples, Local Only, Distance tuned to asset scale
- Set up Emit bakes for Roughness, Metallic, and Emission via node chains
- Channel pack into ORM (Unreal/Godot) or Mask Map (Unity) using the compositor or script
- Verify color spaces — all data maps to Non-Color before export
- Import to engine, assign textures to the correct slots, and light-test under multiple HDRIs
This pipeline keeps your texture memory efficient and your draw calls low — critical for any real-time target. For assets you plan to sell or distribute, the baked texture quality is the first thing buyers evaluate. Properly baked PBR maps that hold up under studio lighting and dynamic environments are what separate professional marketplace assets from hobbyist uploads.
Browse professionally prepared, engine-ready 3D assets with complete PBR texture sets at BitSoul's marketplace — and if you're uploading your own work, use this bake checklist to make sure every texture ships clean.