McBob
McBob

Reputation: 1011

GLES Encode/Decode 32bits float to 2x16bits

Im trying to optimize texture memory and all that stop me from converting a GL_RGBA32F LUT to GL_RGBA16F is one index that (might) exceed the limit. Is there anyway that I could in C take a float and split it into 2 values and then in GLSL reconstruct that float from the 2 values stored in the LUT?

What I mean is something like this:

[ C ]

float v0,v1, *pixel_array;

magic_function_in_c( my_big_value, &v0, &v1 );

pixel_array[ index++ ] = pos.x; // R
pixel_array[ index++ ] = pos.y; // G
pixel_array[ index++ ] = v0;    // B
pixel_array[ index++ ] = v1;    // A

[ GLSL ]

vec4 lookup = texture2D( sampler0, texcoord );

float v = magic_function_in_glsl( lookup.b, lookup.a );

ps: Im using GLES 2.0 (to be also compatible with WebGL)

Upvotes: 0

Views: 366

Answers (1)

Jeff Gilbert
Jeff Gilbert

Reputation: 396

If you just need more range than float16 provides, and only in one direction (larger or smaller), you can multiply by a fixed scaling factor.

For instance, if you need to some number N, greater than 65503, you can 'encode' by dividing N by 2, and 'decode' by multiplying by 2. This shifts the effective range up, sacrificing the range of 1/N, but expanding the range maximum for +/-N. You can swap the multiply and divide if you need more range in 1/N than in +/-N. You can use the second value to store what the scaling factor is, if you need it to change based on data.

You can also experiment with exp2 and log2, something like:

void
magic_function_in_c(float fVal, uint16_t* hExponent, uint16_t* hMult)
{
    float fExponent = log2f(f);
    *hExponent = f32_to_f16(fExponent);

    // Compensate for f32->f16 precision loss
    float fActualExponent = f16_to_f32(*hExponent);
    float fValFromExponent = exp2f(fActualExponent);

    float fMult;
    if (fValFromExponent != 0.0f) {
        fMult = fVal / fValFromExponent;
    } else if (fVal < 0.0f) {
        fMult = -1.0f;
    } else {
        fMult = 1.0f
    }
    *hMult = f32_to_f16(fMult);
}

highp float
magic_function_in_glsl(highp float hExponent, highp float hMult)
{
    return exp2(hExponent) * hMult;
}

Note that none of this will work if you don't have highp floats in your GLSL shader.

Upvotes: 1

Related Questions