Optimizing Normal Maps for Unity and Unreal Engine

Why Engine-Specific Optimization Matters

Unity and Unreal Engine handle normal maps differently due to distinct rendering pipelines, compression formats, and coordinate system conventions. Optimizing for each engine ensures:

  • Better visual quality at equivalent memory budgets
  • Faster loading times through appropriate compression
  • Correct lighting by matching coordinate systems
  • Efficient memory usage on target platforms

Unity Normal Map Settings

Import Configuration

When importing a normal map texture in Unity:

Inspector Settings:
├─ Texture Type: Normal map
├─ Create from Grayscale: Unchecked (if already a normal map)
├─ Bumpiness: 1.0 (default)
└─ Filtering: Bilinear or Trilinear

Format and Compression

Desktop (PC/Mac/Linux):

  • DXT5nm (BC5): Best quality, industry standard
  • Stores X in alpha, Y in green
  • Z calculated in shader (saves memory)
  • 4:1 compression ratio

Mobile (iOS):

  • ASTC 6x6: Best quality/size balance
  • PVRTC 4-bit: Older devices, lower quality
  • Test on actual device hardware

Mobile (Android):

  • ETC2_RGB: Widely supported
  • ASTC 6x6: Better quality if min SDK ≥7.0

Platform Override Settings

// Example: Configure normal map import settings
// Place in Editor folder

using UnityEditor;
using UnityEngine;

public class NormalMapProcessor : AssetPostprocessor
{
    void OnPreprocessTexture()
    {
        TextureImporter importer = (TextureImporter)assetImporter;
        
        if (assetPath.Contains("_normal") || assetPath.Contains("_n."))
        {
            importer.textureType = TextureImporterType.NormalMap;
            importer.mipmapEnabled = true;
            importer.textureCompression = TextureImporterCompression.CompressedHQ;
        }
    }
}

Material Setup (URP/HDRP)

URP:

  • Normal Map strength controlled in material inspector
  • Range: 0.0 (flat) to 2.0 (exaggerated)
  • Default: 1.0

HDRP:

  • More granular normal map controls
  • Separate tangent and object space options
  • Can blend multiple normal maps with masks

Unreal Engine Normal Map Settings

Texture Import

Unreal auto-detects normal maps by naming convention:

  • Files ending in _N, _Normal, _Norm
  • Automatically set to TC_Normalmap compression

Manual override:

Texture Properties:
├─ Compression: TC_Normalmap
├─ sRGB: False (auto-unchecked)
├─ Flip Green Channel: Depends on authoring software
└─ LOD Group: Match other material textures

Compression Settings

Desktop:

  • TC_Normalmap: Uses BC5/BC7 depending on RHI
  • Optimal quality for normal maps
  • Automatically configured

Mobile:

  • TC_Normalmap still used
  • Converts to ASTC/ETC internally
  • No manual override needed

Coordinate System

Unreal uses:

  • +X = Right (red channel)
  • +Y = Down (green channel)
  • +Z = Toward camera

Some software (Substance, Photoshop with certain plugins) uses +Y Up.

Solution: Enable "Flip Green Channel" if normals appear inverted.

Material Graph Setup

// Basic normal map setup
NormalMap -> NormalMap Node (Texture Sample)
           -> Strength Parameter (optional)
           -> Final Material Normal Input

// Advanced: Detail normal overlay
BaseNormal + DetailNormal (blended) -> Final Normal

Cross-Engine Format Considerations

Tangent Space vs. Object Space

Tangent Space (Standard):

  • Purple/blue dominant color
  • Used by both Unity and Unreal
  • Required for deforming meshes (characters)
  • Works with all UV layouts

Object Space:

  • Full RGB color variation
  • Baked to specific mesh
  • No deformation support
  • Rarely used in modern workflows

Normal Map Channels

ChannelDataCompression Benefit
R (Red)XModerate
G (Green)YHigh
B (Blue)ZReconstructed
A (Alpha)Unused or XHigh if used

Both engines reconstruct Z channel in shader:

float3 normal;
normal.xy = texture.rg * 2.0 - 1.0;
normal.z = sqrt(1.0 - dot(normal.xy, normal.xy));

This allows 2-channel compression (BC5) instead of 3-channel (BC7).

Performance Optimization

Resolution Guidelines

Asset TypeResolutionUse Case
Hero character4096Player character, cutscenes
Standard prop2048Weapons, furniture, vehicles
Environment tile2048Walls, floors (will tile)
Background object1024Distant scenery, fillers
Small detail512Screws, rivets, decals

Mipmap Optimization

Enable mipmaps for:

  • ✅ Tiling textures (walls, floors)
  • ✅ Objects viewed at varying distances
  • ✅ Anything using texture streaming

Disable for:

  • ❌ UI elements
  • ❌ Decals viewed at fixed distance
  • ❌ Skydomes and skyboxes

Virtual Texturing

Unreal Engine:

Material → Use Virtual Texturing: True
Texture → Virtual Texture Streaming: True

Benefits:

  • Reduced memory overhead
  • Better streaming performance
  • Support for massive texture atlases

Unity (2022.2+):

  • Virtual Texturing still experimental
  • Use standard texture streaming for now

Memory Budget Examples

Console/High-End PC

Character (Full Body):
├─ Albedo: 4K (DXT1) = 11 MB
├─ Normal: 4K (BC5) = 11 MB
├─ Roughness: 2K (BC4) = 3 MB
└─ Total: ~25 MB

Environment Tile:
├─ Albedo: 2K (DXT1) = 3 MB
├─ Normal: 2K (BC5) = 3 MB  
├─ Roughness: 1K (BC4) = 0.75 MB
└─ Total: ~7 MB

Mobile

Character:
├─ Albedo: 2K (ASTC 6x6) = 2 MB
├─ Normal: 2K (ASTC 6x6) = 2 MB
├─ Packed RMA: 1K (ASTC 6x6) = 0.5 MB
└─ Total: ~4.5 MB

Prop:
├─ Albedo: 1K (ASTC 6x6) = 0.5 MB
├─ Normal: 1K (ASTC 6x6) = 0.5 MB
└─ Total: ~1 MB

Common Pitfalls

Wrong Color Space

Problem: Normal map tinted green/purple overall
Cause: sRGB enabled on normal map texture
Fix: Ensure sRGB is disabled (both engines auto-disable for normal map type)

Inverted Normals

Problem: Bumps appear as holes, vice versa
Cause: Coordinate system mismatch
Fix:

  • Unity: Check normal map generation tool's output space
  • Unreal: Toggle "Flip Green Channel" in texture properties

Compression Artifacts

Problem: Banding or blockiness on smooth surfaces
Cause: Wrong compression format
Fix:

  • Use BC5/TC_Normalmap compression
  • Increase texture resolution if needed
  • Check source normal map quality before compression

Seams on UV Boundaries

Problem: Visible lines where UVs meet
Cause: Normal map generated without UV padding
Fix:

  • Generate with padding (2-4 pixels)
  • Use texture tool to dilate edges
  • Adjust UV islands to minimize seams

Export Settings Checklist

When exporting from a normal map generator:

For Unity:

  • ✅ 16-bit PNG or 8-bit PNG (16 if quality critical)
  • ✅ Tangent space
  • ✅ OpenGL format (+Y up) - Unity converts internally
  • ✅ Power of 2 dimensions for mipmaps

For Unreal:

  • ✅ 8-bit PNG sufficient
  • ✅ Tangent space
  • ✅ DirectX format (+Y down) or use "Flip Green Channel"
  • ✅ Power of 2 dimensions

Testing and Validation

Visual Checks

  1. Lighting Consistency: Rotate light source, normals should respond correctly
  2. Seam Inspection: Check UV boundaries in wireframe view
  3. Distance LODs: View at multiple distances to verify mipmap quality
  4. Performance: Profile texture memory usage in engine's profiler

Unity Profiler

Window → Analysis → Profiler
├─ Memory → Textures
└─ Filter by "normal" to see all normal maps

Unreal Statistics

Console command: stat texture
or
Console command: stat streaming

Platform-Specific Recommendations

Mobile Optimization

  • Prefer 1K for most assets
  • Use ASTC 6x6 minimum
  • Combine roughness/metallic/AO into single texture with normal
  • Test on low-end devices (not just flagship)

Console (PS5/Xbox Series X)

  • 2K-4K normal maps acceptable
  • BC7 for highest quality (where supported)
  • Rely on hardware texture compression
  • Leverage SSD streaming for rapid loads

WebGL

  • Keep total texture memory under 256MB
  • Use DXT compression where supported
  • Aggressive mipmapping to reduce bandwidth
  • Consider optional quality settings for low-end GPUs

Conclusion

Optimizing normal maps for Unity and Unreal Engine requires understanding each engine's texture pipeline, compression systems, and coordinate conventions. Key principles:

  1. Use engine-appropriate compression: BC5 for Unity desktop, ASTC for mobile, TC_Normalmap for Unreal
  2. Match coordinate systems: Check green channel flip requirements
  3. Budget wisely: Allocate resolution based on visual importance and viewing distance
  4. Test on target hardware: Profiling reveals real-world performance

By following these guidelines, your normal maps will deliver maximum visual impact while respecting memory and performance constraints across all target platforms.