I'm writing a shader (HLSL), and I need to pack a color value into the R32 format. I've found various pieces of code for packing a float into the R8G8B8A8 format, but none of them seem to work in reverse. I'm targeting SM3.0, so (afaik) bit operations are not an option.
To sum it up, I need to be able to do this:
float4 color = ...; // Where color ranges from 0 -> 1
float packedValue = pack(color);
Anyone know how to do this?
UPDATE
I've gotten some headway... perhaps this will help to clarify the question.
My temporary solution is as such:
const int PRECISION = 64;
float4 unpack(float value)
{
float4 color;
color.a = value % PRECISION;
value = floor(value / PRECISION);
color.b = value % PRECISION;
value = floor(value / PRECISION);
color.g = value % PRECISION;
value = floor(value / PRECISION);
color.r = value;
return color / (PRECISION - 1);
}
float pack(float4 color)
{
int4 iVal = floor(color * (PRECISION - 1));
float output = 0;
output += iVal.r * PRECISION * PRECISION * PRECISION;
output += iVal.g * PRECISION * PRECISION;
output += iVal.b * PRECISION;
output += iVal.a;
return output;
}
I'm basically... pretending I'm using integer types :s
Through guess and check, 64 was the highest number I could use while still maintaining a [0...1] range. Unfortunately, that also means I'm losing some precision - 6 bits instead of 8.
Have a look at: http://diaryofagraphicsprogrammer.blogspot.com/2009/10/bitmasks-packing-data-into-fp-render.html
The short answer is that it's not possible to do a lossless packing of 4 floats into 1 float.
Even if you do find a way to pack 4 floats, storing their exponent and significand, the packing and unpacking process may be prohibitively expensive.
Do like your sample code for 3 components to pack them into into the s=significand, and do exp2(color.g)(1+s) to pack the other component into the exponent. You'll still lose precision, but it won't be as bad as if you try to pack everything into the significand like you seem to be doing.
Unfortunately there's no way to avoid losing precision, as there are a bunch of NaN and Inf float values that are both indistinguishable from each other and hard to work with (and possibly not even supported by your GPU, depending on how old it is).