Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I convert between float and vec4,vec3,vec2?

This question is very related to the question here(How do I convert a vec4 rgba value to a float?).

There is some of articles or questions related to this question already, but I wonder most of articles are not identifying which type of floating value. As long as I can come up with, there is some of floating value packing/unpacking formula below.

  • unsigned normalized float
  • signed normalized float
  • signed ranged float (the floating value I can find range limitation)
  • unsigned ranged float
  • unsigned float
  • signed float

However, these are just 2 case actually. The other packing/unpacking can be processed by these 2 method.

  • unsigned ranged float (I can pack/unpack by easy bitshifting)
  • signed float

I want to pack and unpack signed floating values into vec3 or vec2 also.

For my case, the floating value is not ensured to be normalized, so I can not use the simple bitshifting way.

like image 313
kyasbal Avatar asked Dec 03 '22 15:12

kyasbal


2 Answers

If you know the max range of values you want to store, say +5 to -5, than the easiest way is just to pick some convert that range to a value from 0 to 1. Expand that to the number of bits you have and then break it into parts.

vec2 packFloatInto8BitVec2(float v, float min, float max) {
   float zeroToOne = (v - min) / (max - min);
   float zeroTo16Bit = zeroToOne * 256.0 * 255.0;
   return vec2(mod(zeroTo16Bit, 256.0), zeroTo16Bit / 256.0);
}

To put it back you do the opposite. Assemble the parts, divide to get back to a zeroToOne value, then expand by the range.

float unpack8BitVec2IntoFloat(vec2 v, float min, float max) {
   float zeroTo16Bit = v.x + v.y * 256.0;
   float zeroToOne = zeroTo16Bit / 256.0 / 255.0;
   return zeroToOne * (max - min) + min;
}

For vec3 just expand it

vec3 packFloatInto8BitVec3(float v, float min, float max) {
   float zeroToOne = (v - min) / (max - min);
   float zeroTo24Bit = zeroToOne * 256.0 * 256.0 * 255.0;
   return vec3(mod(zeroTo24Bit, 256.0), mod(zeroTo24Bit / 256.0, 256.0), zeroTo24Bit / 256.0 / 256.0);
}

float unpack8BitVec3IntoFloat(vec3 v, float min, float max) {
   float zeroTo24Bit = v.x + v.y * 256.0 + v.z * 256.0 * 256.0;
   float zeroToOne = zeroTo24Bit / 256.0 / 256.0 / 256.0;
   return zeroToOne * (max - min) + min;
}
like image 115
gman Avatar answered Jan 06 '23 23:01

gman


I have written small example few days ago with shadertoy: https://www.shadertoy.com/view/XdK3Dh

It stores float as RGB or load float from pixel. There is also test that function are exact inverses (lot of other functions i have seen has bug in some ranges because of bad precision).

Entire example assumes you want to save values in buffer and read it back in next draw. Having only 256 colors, it limits you to get 16777216 different values. Most of the time I dont need larger scale. I also shifted it to have signed float insted in interval <-8388608;8388608>.

float color2float(in vec3 c) {
    c *= 255.;
    c = floor(c); // without this value could be shifted for some intervals
    return c.r*256.*256. + c.g*256. + c.b - 8388608.;
}

// values out of <-8388608;8388608> are stored as min/max values
vec3 float2color(in float val) {
    val += 8388608.; // this makes values signed
    if(val < 0.) {
        return vec3(0.);
    }
    if(val > 16777216.) {
        return vec3(1.);
    }
    vec3 c = vec3(0.);
    c.b = mod(val, 256.);
    val = floor(val/256.);
    c.g = mod(val, 256.);
    val = floor(val/256.);
    c.r = mod(val, 256.);
    return c/255.;
}

One more thing, values that overflow will be rounded to min/max value.

like image 20
Entity Black Avatar answered Jan 06 '23 22:01

Entity Black