GLES Encode/Decode 32 бита с плавающей запятой до 2x16 бит

Я пытаюсь оптимизировать память текстур, и все, что мешает мне преобразовать GL_RGBA32F LUT в GL_RGBA16F, — это один индекс, который (может) превышать предел. Могу ли я в C взять число с плавающей запятой и разделить его на 2 значения, а затем в GLSL восстановить это число с плавающей запятой из двух значений, хранящихся в LUT?

Я имею в виду что-то вроде этого:

[ 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

[ ГЛСЛ ]

vec4 lookup = texture2D( sampler0, texcoord );

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

ps: я использую GLES 2.0 (чтобы также быть совместимым с WebGL)


person McBob    schedule 21.03.2017    source источник


Ответы (1)


Если вам просто нужен больший диапазон, чем обеспечивает float16, и только в одном направлении (больше или меньше), вы можете умножить на фиксированный коэффициент масштабирования.

Например, если вам нужно какое-то число N больше 65503, вы можете «закодировать», разделив N на 2, и «декодировать», умножив на 2. Это сдвигает эффективный диапазон вверх, жертвуя диапазоном 1/N, но расширение диапазона максимум на +/-N. Вы можете поменять местами умножение и деление, если вам нужен больший диапазон в 1/N, чем в +/-N. Вы можете использовать второе значение для хранения коэффициента масштабирования, если вам нужно, чтобы он менялся на основе данных.

Вы также можете поэкспериментировать с exp2 и log2, например:

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;
}

Обратите внимание, что ничего из этого не будет работать, если в вашем шейдере GLSL нет highp float.

person Jeff Gilbert    schedule 26.04.2017