RRév O'Conner
The Himalayas, IN · UTC+5:30
← All writing
CGI2024 · November · 249 min read

Texture compression for Unreal Engine — BCn and texture packing.

Bits per pixel, block compression, and what to actually reach for when. BCn formats deconstructed — plus the texture-packing decisions Unreal hides behind its named presets.

Understanding Bits Per Pixel (bpp)

"bpp" refers to bits per pixel, measuring the data used per pixel in textures. In uncompressed formats, bits distribute evenly among channels. In compressed formats, allocation uses complex algorithms across pixel blocks, which makes per-channel bit depths less direct.

FormatTypical useAlphaChannelsUncompressed bppCompressed bpp
DXT1default texturesno (or 1-bit)R, G, B24 (8 x 3)4
DXT5default texturesyesR, G, B, A32 (8 x 4)8
BC5normal mapsnoR, G16 (8 x 2)8
BC4alpha masks / single-channelnoR or G84
G8grayscale / displacementnoG8uncompressed
G16grayscale / displacementnoG16uncompressed
B8G8R8A8vector displacement, UIyesB, G, R, A32 (8 x 4)uncompressed
FloatRGBAHDR texturesyesR, G, B, A128 (32-bit float x 4)uncompressed
BC6HHDR compressednoR, G, Bvaries (HDR precision)8
BC7high-quality compressionoptionalR, G, B, (A)24 (no alpha) or 32 (with alpha)8

Understanding compressed formats

  • Block compression. Formats like DXT and BC4–BC7 compress textures in blocks (typically 4×4 pixels).
  • Variable bit allocation. Bits aren't evenly divided among channels or pixels — instead they represent color endpoints and indices that reconstruct the image during rendering.
  • Compression efficiency. These formats reduce texture size in memory and improve performance without significantly compromising visual quality.

Differences between similar settings

Vector displacement map vs UI 2D (both use B8G8R8A8)

Vector displacement map

  • Purpose: store precise displacement vectors (X, Y, Z) for modifying surface geometry.
  • Requirement: uncompressed, to preserve exact values — compression artifacts can distort geometry.

UI 2D

  • Purpose: display crisp UI elements like icons and text.
  • Requirement: uncompressed, to maintain sharpness — even minor compression artifacts are noticeable.

Default textures vs masks (both can use DXT1/DXT5)

Default textures

  • Usage: general textures where standard sRGB gamma correction is applied.
  • Compression: sRGB color space.

Masks (no sRGB)

  • Usage: grayscale masks for blending or masking operations.
  • Compression: sRGB disabled to prevent gamma correction, ensuring linear color values.

Grayscale textures vs distance-field fonts (both use G8)

Grayscale textures

  • Purpose: black and white masks, height maps.
  • Data: single-channel intensity values.

Distance-field fonts

  • Purpose: scalable fonts with smooth edges.
  • Data: precise control over glyph edges, relies on accurate single-channel data.

Normal maps (BC5) vs alpha masks (BC4)

Normal maps (BC5)

  • Channels: two (R and G) representing surface normals.
  • Compression: 8 bpp, optimized for gradient data.

Alpha masks (BC4)

  • Channels: single (often R).
  • Compression: 4 bpp, suitable for opacity information.

HDR uncompressed (FloatRGBA) vs HDR compressed (BC6H)

HDR uncompressed

  • Precision: highest possible (32-bit floats per channel).
  • Usage: critical when maximum dynamic range and precision are required.

HDR compressed (BC6H)

  • Precision: high dynamic range with compression.
  • Usage: balances memory usage with HDR support — suitable when slight precision loss is acceptable.

Why DeriveNormalZ doesn't work with all formats

Normal maps store surface normals in texture channels to simulate detailed surface geometry without increasing mesh complexity. A normal vector has three components — X, Y, and Z — and is typically normalized (length = 1). In tangent-space normal maps:

  • X is stored in the Red channel.
  • Y is stored in the Green channel.
  • Z is often reconstructed in the shader to save memory.

With BC5 and BC3, the standard derive-Z works. With BC7 it needs this:

float Input1 = R;
float Input2 = G;
float2 Appended = float2(Input1, Input2);
float2 Multiplied = Appended * 2;
float2 Subtracted = Multiplied - 1;
float2 MultipliedBySelf = Subtracted * Subtracted;
float MaskR = MultipliedBySelf.r;
float MaskG = MultipliedBySelf.g;
float OneMinusMaskR = 1 - MaskR;
float FinalSubtract = OneMinusMaskR - MaskG;
float SquaredRoot = sqrt(FinalSubtract);
float3 AppendX = float3(Subtracted, SquaredRoot);
return AppendX;

Both are linear-color texture type.

BC5 compression format

  • Designed for two-channel data. BC5 is optimized for two-channel textures, making it ideal for normal maps storing X and Y components.
  • High precision. Each channel (R and G) is compressed separately using techniques similar to BC4, preserving high fidelity.
  • Data range. When sampled, the texture returns values in [0, 1], linearly mapped to [-1, 1] in the shader.

Standard method to derive Z (works with BC5):

float2 normalXY = tex2D(normalMap, texCoords).rg * 2.0 - 1.0;
float normalZ = sqrt(1.0 - dot(normalXY, normalXY));
float3 normal = float3(normalXY, normalZ);

BC7 compression format

  • Designed for high-quality color data. BC7 targets high-quality compression of color images with optional alpha.
  • Complex compression. Uses multiple modes, partitions, and bit allocations — which can introduce slight inaccuracies.
  • Data range and precision. Possible artifacts: due to its compression scheme, BC7 can introduce minor errors in the decompressed values. Values may not map perfectly to the expected range after decompression.

Why the difference occurs

  1. Compression artifacts and precision loss. BC5 maintains high precision for the R and G channels, so the standard method works reliably. BC7 may introduce slight deviations in R and G, leading to potential inaccuracies when reconstructing Z.

  2. Negative values inside sqrt(). Minor inaccuracies can cause 1.0 - dot(normalXY, normalXY) to become negative — sqrt() of a negative number returns NaN.

  3. Need for additional calculations. The longer code above carefully computes each step to prevent negative values and ensure valid results. Breaking the calculation down explicitly helps manage the precision issues introduced by BC7.

What are BCn?

Definition. "BC" stands for Block Compression. BCn formats compress texture data for efficient real-time rendering on GPUs.

Block structure

  • Operate on 4×4 pixel blocks.
  • Each block is self-contained, with all data needed for decoding.
  • Fixed block sizes: 8 or 16 bytes, depending on the format.

Compression ratios

Achieve 4:1 or 8:1 compression ratios for 8-bit RGBA images.

GPU optimization

  • Designed for fast, random access required by GPUs.
  • Fixed-size blocks allow easy calculation of any pixel's location.
  • Self-contained blocks enable decompression of only required portions.
  • Locality of texture sampling is leveraged through the 4×4 block structure.

General concepts

Limited color variation. Small areas (4×4 blocks) often have limited color variation.

Color palettes. Each block has a small palette (typically a few colors). Pixels are indexed into this palette.

Endpoints and indices. Colors are assumed to lie along a line in RGB space. Only the endpoints of this line are stored. Intermediate colors are interpolated between endpoints.

Block data. Each block contains:

  • Endpoints — define the color line segment.
  • Indices — per-pixel references into the palette.

Limitations of BCn formats

  • Color representation. Poor quality when more than two distinct, non-linear colors are in a block. Struggles with blocks containing red, green, and blue together.
  • Gradient representation. Smooth gradients can exhibit banding due to limited precision.
  • Normal maps. Standard BCn formats may not adequately represent normal maps.

Specific BCn formats

BC1 (DXT1)

  • Data type: RGB with optional 1-bit alpha.
  • Block size: 8 bytes (0.5 bytes per pixel).
  • Color precision: endpoints in RGB 5:6:5 format.
  • Palette size: 4 colors.

Use cases: standard color maps without smooth gradients; cutout textures requiring 1-bit alpha (fences, vegetation).

Limitations: poor for smooth gradients due to low color precision; potential for dark fringes with bilinear filtering on cutout textures.

Degeneracy and 1-bit alpha in BC1

  • Degeneracy breaking. The order of endpoints triggers different modes — two modes: four-color palette, or three-color palette with transparency.
  • Transparency handling. Supports 1-bit alpha by designating one palette entry as transparent. No color information stored in transparent areas.

BC4

  • Data type: grayscale (single-channel).
  • Block size: 8 bytes (0.5 bytes per pixel).
  • Palette size: 8 grayscale values.

Higher quality: full 8-bit endpoints and a larger palette improve quality over BC1 for grayscale images.

Use cases: height maps, gloss maps, font atlases, any grayscale texture.

BC2 (rarely used)

  • Data type: RGB with explicit 4-bit alpha per pixel.
  • Block size: 16 bytes (1 byte per pixel).

Limitations: only 16 levels of alpha → severe banding. No endpoint-and-index compression for the alpha channel.

Current relevance: generally obsolete due to better alternatives like BC3.

BC3 (DXT5)

  • Data type: RGBA.
  • Block size: 16 bytes (1 byte per pixel).

Components: RGB stored using BC1 compression; alpha stored separately using BC4 compression.

Use cases: textures requiring full alpha channel; combining color with grayscale maps (gloss, specular).

BC5

  • Data type: two-channel data (e.g. XY components of normal maps).
  • Block size: 16 bytes (1 byte per pixel).

Components: two BC4 blocks (one per channel).

Advantages: better fidelity for normal maps compared to BC1; separate endpoints and indices per channel.

Use cases: tangent-space normal maps; any two-channel texture data.

BC6

  • Data type: RGB half-precision floating-point (HDR images).
  • Block size: 16 bytes (1 byte per pixel).

Features: native HDR content; multiple modes for flexibility; partitioning allows multiple line segments per block. Uses delta compression for endpoints.

Use cases: HDR images; replacing less-efficient HDR encoding methods.

BC7

  • Data type: RGB or RGBA.
  • Block size: 16 bytes (1 byte per pixel).

Features: high-quality compression for complex color images; multiple modes and partitioning for adaptability; supports smooth gradients and fine color variations. Utilizes P-bits for endpoint precision; offers shared or separate indices for color and alpha.

Use cases: high-quality color textures; textures requiring full alpha with minimal quality loss.

Advanced features in BC6 and BC7

Per-block modes. Modes change palette size, endpoint storage, and number of line segments. The first few bits of each block specify the mode.

Partitioning. Blocks can have multiple line segments via predefined partition patterns — improves quality for blocks with colors that don't fit a single line in RGB space.

Compression techniques.

  • Degeneracy breaking — saves bits by enforcing certain bit patterns and swapping endpoints when necessary.
  • Delta compression (BC6) — stores one endpoint precisely, others as lower-precision deltas.
  • P-bits (BC7) — shared least-significant bits to improve endpoint precision without extra storage.

Hardware requirements. Supported by Direct3D 11-level GPUs and above. Compression is more complex and time-consuming due to the advanced features.

Comparison table of BCn formats

FORMATDATA TYPEBYTES/PIXELPALETTELINESUSE CASES
BC1RGB + optional 1-bit alpha0.54 colors1Standard color maps, cutout textures
BC2RGBA with 4-bit alpha1.04 colors + 16 alpha1(Obsolete)
BC3RGBA1.04 colors + 8 alpha1 color + 1 alphaTextures with full alpha
BC4Grayscale0.58 grayscale1Height maps, gloss, font atlases
BC5Two-channel (e.g. normal maps)1.08 per channel1 per channelTangent-space normal maps
BC6RGB half-precision float1.08–161–2HDR images
BC7RGB or RGBA1.04–161–3High-quality color maps with alpha

Key takeaways

  • BC1 — best for simple color textures without smooth gradients or full alpha.
  • BC3 — preferred for textures requiring full alpha channels.
  • BC4 / BC5 — ideal for grayscale images and normal maps, offering higher quality in the same or less space.
  • BC6 — efficient storage of HDR images on compatible hardware.
  • BC7 — high-quality compression for complex textures with minimal artifacts, suitable for next-gen graphics.
Filed under: CGI← Back to writing