Как вы упаковываете один 32-битный int Into 4, 8bit ints в glsl/webgl?
Я хочу распараллелить некоторую сложную математику, и webgl выглядит как идеальный способ сделать это.
Проблема в том, что вы можете читать только 8 битных целых чисел из текстур.
В идеале я хотел бы получить 32-битные числа из текстуры.
У меня была идея использовать 4 цветовых канала для получения 32 бит на пиксель вместо 4-х раз в 8 бит.
Моя проблема в том, что glsl не имеет оператора "%" или любого побитового оператора!
TL;DR:
Как преобразовать 32-битное число в 4 8-битные числа, используя операторы в glsl.
Дополнительная информация по технике (с использованием побитовых операторов):
Как сохранить 64-битное целое число из двух 32-битных целых чисел и преобразовать обратно
Ответы
Ответ 1
Вы можете битрейт путем умножения/деления на степени двух.
Как отмечалось в комментариях, подход, который я изначально опубликовал, работает, но неверен, здесь один из Арас Пранкявичюс, обратите внимание, что исходный код в сам пост содержит опечатку и является HLSL, это порт GLSL с исправленной опечаткой:
const vec4 bitEnc = vec4(1.,255.,65025.,16581375.);
const vec4 bitDec = 1./bitEnc;
vec4 EncodeFloatRGBA (float v) {
vec4 enc = bitEnc * v;
enc = fract(enc);
enc -= enc.yzww * vec2(1./255., 0.).xxxy;
return enc;
}
float DecodeFloatRGBA (vec4 v) {
return dot(v, bitDec);
}
Ответ 2
В общем случае, если вы хотите упаковать значимые цифры числа с плавающей запятой в байтах, вы последовательно извлекаете 8 битных пакетов значимых цифр и сохраняете их в байте.
Кодировать число с плавающей запятой в предопределенном диапазоне
Чтобы упаковать значение с плавающей запятой в 4 * 8-битных буферах, сначала необходимо указать диапазон исходных значений.
Если вы определили диапазон значений [ minVal
, maxVal
], он должен быть отображен в диапазон [0.0, 1.0]:
float mapVal = clamp((value-minVal)/(maxVal-minVal), 0.0, 1.0);
Функция Encode
упаковывает значение с плавающей запятой в диапазоне [0.0, 1.0] в значение vec4
:
vec4 Encode( in float value )
{
value *= (256.0*256.0*256.0 - 1.0) / (256.0*256.0*256.0);
vec4 encode = fract( value * vec4(1.0, 256.0, 256.0*256.0, 256.0*256.0*256.0) );
return vec4( encode.xyz - encode.yzw / 256.0, encode.w ) + 1.0/512.0;
}
Функция Decode
извлекает значение с плавающей запятой в диапазоне [0.0, 1.0] из vec4
:
float Decode( in vec4 pack )
{
float value = dot( pack, 1.0 / vec4(1.0, 256.0, 256.0*256.0, 256.0*256.0*256.0) );
return value * (256.0*256.0*256.0) / (256.0*256.0*256.0 - 1.0);
}
Следующие функции упаковывают и извлекают значение с плавающей запятой в диапазоне и от диапазона [ minVal
, maxVal
]:
vec4 EncodeRange( in float value, flaot minVal, maxVal )
{
value = clamp( (value-minVal) / (maxVal-minVal), 0.0, 1.0 );
value *= (256.0*256.0*256.0 - 1.0) / (256.0*256.0*256.0);
vec4 encode = fract( value * vec4(1.0, 256.0, 256.0*256.0, 256.0*256.0*256.0) );
return vec4( encode.xyz - encode.yzw / 256.0, encode.w ) + 1.0/512.0;
}
float DecodeRange( in vec4 pack, flaot minVal, maxVal )
{
value = dot( pack, 1.0 / vec4(1.0, 256.0, 256.0*256.0, 256.0*256.0*256.0) );
value *= (256.0*256.0*256.0) / (256.0*256.0*256.0 - 1.0);
return mix( minVal, maxVal, value );
}
Кодировать число с плавающей запятой с показателем
Другая возможность заключается в кодировании значимых цифр в 3 * 8 бит значений RGB и экспоненте в 8 бит битового канала:
vec4 EncodeExp( in float value )
{
int exponent = int( log2( abs( value ) ) + 1.0 );
value /= exp2( float( exponent ) );
value = (value + 1.0) * (256.0*256.0*256.0 - 1.0) / (2.0*256.0*256.0*256.0);
vec4 encode = fract( value * vec4(1.0, 256.0, 256.0*256.0, 256.0*256.0*256.0) );
return vec4( encode.xyz - encode.yzw / 256.0 + 1.0/512.0, (float(exponent) + 127.5) / 256.0 );
}
float DecodeExp( in vec4 pack )
{
int exponent = int( pack.w * 256.0 - 127.0 );
float value = dot( pack.xyz, 1.0 / vec3(1.0, 256.0, 256.0*256.0) );
value = value * (2.0*256.0*256.0*256.0) / (256.0*256.0*256.0 - 1.0) - 1.0;
return value * exp2( float(exponent) );
}
Заметьте, поскольку стандартный 32-разрядный номер IEEE 754 имеет только 24 значащих цифры, вполне достаточно кодировать число в 3 байта.
См. также ответы на следующие вопросы: