Packing Depth into Color

New 24 bit Packing

This new approach will pack a float with range [0,1] into an RGB24 pixel.
It is designed to avoid all precision and rounding errors and give the exact same stored depth values as a D24S8 depth buffer.

Notes:
1) Subtracting multiples of 256 and 65536 from Green and Blue before scaling up reduces 32 bit float precision errors
2) Scaling by 256 or 65536 allows frac() to emulate Modulo
3) Normalizing the resulting range back to [0,1] gives full usage of 8 bits in each channel
4) Using correct unpacking factors, so unpacking float3(1, 1, 1) doesn’t overflow

I built a test harness that calculated maximum and average errors for a number of packing functions.
This approach seems to give 0e0 error across the full range.

float3 UnitToColor24(in float depth) {

    // Constants
    const float3 scale  = float3(1.0, 256.0, 65536.0);
    const float2 ogb    = float2(65536.0, 256.0) / 16777215.0;
    const float normal  = 256.0 / 255.0;
    
    // Avoid Precision Errors
    float3 unit         = depth.xxx;
    unit.gb             -= floor(unit.gb / ogb) * ogb;
    
    // Scale Up
    float3 color        = unit * scale;
    
    // Use Fraction to emulate Modulo
    color               = frac(color);
    
    // Normalize Range
    color               *= normal;
    
    // Mask Noise
    color.rg            -= color.gb / 256.0;

    return color;
    
}//function

float ColorToUnit24(in float3 color) {
    const float3 scale = float3(65536.0, 256.0, 1.0) / 65793.0;
    return dot(color, scale);
}//function

32 bit Packing

This will pack a float with range [0,1] into an ARGB32 Pixel.

float4 UnitToColor32(in float unit) {
    const float4 factor = float4(1, 255, 65025, 16581375);
    const float mask = 1.0 / 256.0;
    float4 color = unit * factor;
    color.gba = frac(color.gba);
    color.rgb -= color.gba * mask;
    return color;
}//function

float ColorToUnit32(in float4 color) {
    const float4 factor = 1.0 / float4(1, 255, 65025, 16581375);
    return dot(color, factor);
}//function

24 bit Packing

This will pack a float with range [0,1] into an RGB24 pixel.

float3 UnitToColor24(in float unit) {
    const float3 factor = float3(1, 255, 65025);
    const float mask = 1.0 / 256.0;
    float3 color = unit * factor.rgb;
    color.gb = frac(color.gb);
    color.rg -= color.gb * mask;
    return saturate(color);
}//function

float ColorToUnit24(in float3 color) {
    const float3 factorinv = 1.0 / float3(1, 255, 65025);
    return dot(color, factorinv);
}//function

Notes

Using the 24 bit version matches the precision of the standard D24S8 depth stencil buffer, and releases an extra 8 bits for something else.

Advertisements

One Response to “Packing Depth into Color”

  1. Garold Says:

    Thanks, this is great, just what I need. I can now render my particles and geometry all at the same time 🙂


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: