Search code examples
compressionglslbit-shiftglslesbit-packing

GLSL - compressing/packing multiple 0-1 colours (var4) into a single var4 variable


I am trying to do the following in GLSL 2 es:

Given a number (say 4 for example) of normalized var4 variables(RGBA) reduce their bit depth and pack the results in a single 0-1 clamped var4. This will be stored as an 8 bit (per channel) texture and later unpacked. I realize that this will result in quality loss but that is acceptable.

So in this example:

RGBA 8 bit
-> reduced to RGBA 2 bit
-> packed with 3 other RGBA 2 bit var4s
-> saved as single 8 bit RGBA texture
-> unpacked back into 4 x RGBA 2 bit variables
-> scaled back to a reduced quality version of the original.

I realize that I can't do bit-shifting in GLSL so I would have to do some collection of multiplications, the magic combination of which has so far escaped me! Others have talked about packing floats in vec4s but my problem is a little different.

Thanks!


Solution

  • Well, bit shifting can be represented by mulitplication (for left shifts) or division (for right shifts) with powers of two. You just have to take into account that the floats will stroe the fractional parts which would normally be shifted "out" in normal integer bitshifts.

    So to pack 4 normalized floats a,b,c,d into a normalized float x, you can simply use

    x = 1.0/255.0 * (floor(a*255.0/64.0)*64.0 + floor(b*255.0/64.0)*16.0 + floor(c*255.0/64.0)*4.0 + floor(d*255.0/64.0));
    

    Unpacking is a bit more complicated because you can't simply mask out single bits. Instead, you can subtract the most significant bits as you reconstruct them, for example like this:

    a = floor(x*255.0/64.0)*64.0/255.0;
    x -= a;
    b = floor(x*255.0/16.0)*16.0/255.0;
    x -= b;
    b *= 4.0;
    c = floor(x*255.0/4.0)*4.0/255.0;
    x -= c;
    c *= 16.0;
    d = x*255.0 * 64.0 / 255.0; // scan be simplified to just x*64.0