What is the difference, and when should they be used?
In this short post, I intend to highlight some of the subtle differences.
First, and overview of RGBM:
RGBM is an 8bit RGBA format, where Alpha is sacrificed to store a shared multiplier.
Decoding RGBM:
float3 DecodeRGBM(float4 rgbm)
{
return rgbm.rgb * (rgbm.a * MaxRange);
}
This produces a range of 0 to MaxRange.
Assuming MaxRange is 65025,
When M=1, the range is 0, 1, 2, 3 ... 255,
When M=2, the range is 0, 2, 4, 6 ... 510,
When M=3, the range is 0, 3, 6, 9 ... 765,
When M=255, the range is 0,255,510,765 ... 65025.
Encoding RGBM:
float4 EncodeRGBM(float3 rgb)
{
float maxRGB = max(rgb.x,max(rgb.g,rgb.b));
float M = maxRGB / MaxRange;
M = ceil(M * 255.0) / 255.0;
return float4(rgb / (M * MaxRange), M);
}
RGBD follows the same rules as RGBM, however it stores a divider in Alpha, instead of a Multiplier.
Decoding RGBD:
float3 DecodeRGBD(float4 rgbd)
{
return rgbd.rgb * ((MaxRange / 255.0) / rgbd.a);
}
Encoding RGBD:
float4 EncodeRGBD(float3 rgb)
{
float maxRGB = max(rgb.x,max(rgb.g,rgb.b));
float D = max(MaxRange / maxRGB, 1);
D = saturate(floor(D) / 255.0);
return float4(rgb.rgb * (D * (255.0 / MaxRange)), D);
}
As can be seen, RGBD is a bit more complex in both the encode and the decode. The encode is trickier, as RGBD is a bit more sensitive to values outside of {0,MaxRange}.
But when should either be used?
On the face of it, RGBM looks like the logical choice. It's easier understand, and easier to use.
For the most part, it is the best choice. But not always. Here is why:
Distribution of values:
Neither format can store 65k distinct luminance values (16bits). However both have radically different distribution of the possible values they can store. The following images are gamma-space sample distributions for both formats:
RGBM | RGBD |
If you are after good precision over the entire range, then RGBM will do. If you only want to store occasional highlights, RGBD may be a better option. The 0-1 range is clearly visible in the RGBD distribution (Assuming MaxRange was 256).
However, to take full advantage of both formats would require a much smarter encoder.
Interpolation:
Both formats interpolate differently, and this may be the main reason you would not choose RGBM.
The following graphs demonstrate how the formats interpolate in two very different situations:
The first is a very simple transition from 255 to 256. In both formats, this requires the alpha value to change. For RGBM, the encoding is {255,1} and {128,2}. For RGBD, there are numerous combinations, however the graph shows {255,1} and {128,0.5}.
Here the primary limitation of RGBM is revealed. The interpolation between {255,1} and {128,2} is {192,1.5} - which multiplies to 287.25. Clearly not 255.5!
It's worth noting the RGBD interpolation isn't exactly right either.
For a larger interpolation, 100->1000, RGBD produces a less accurate result. In the following graph, the encoded values are {100,1} and {250,4} for RGBM, and {100,1} and {250,0.25} for RGBD.
Neither scheme produces a linear interpolation.
In summary, neither format is a clear winner. If you need subtle interpolation or good retention of detail in lower range, then consider taking the hit and choose RGBD over RGBM.