如何在 float 和 vec4、vec3、vec2 之间进行转换?

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

这个问题和这里的问题很相关(How do I convert a vec4 rgba value to a float?)。

已经有一些文章或问题与这个问题相关,但我想知道大多数文章都没有识别哪种类型的浮动值。 只要我能想出,下面有一些浮动值packing/unpacking的公式。

然而,这实际上只是2个案例。其他packing/unpacking可以用这2个方法处理。

我也想将带符号的浮点值打包和解包到 vec3 或 vec2 中。

对于我的情况,浮点值不能保证归一化,所以我不能使用简单的位移方式。

如果您知道要存储的值的最大范围,比如说 +5 到 -5,那么最简单的方法就是选择一些将该范围转换为 0 到 1 之间的值。将其扩展为数字你拥有的位,然后将其分成几部分。

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

要将其放回原位,则相反。 Assemble 部分,除以得到零至一值,然后按范围展开。

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

对于 vec3 只需展开它

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

几天前我用shadertoy写了一个小例子: https://www.shadertoy.com/view/XdK3Dh

它将浮点数存储为 RGB 或从像素加载浮点数。还有一个函数是精确逆函数的测试(我见过的许多其他函数由于精度差而在某些范围内存在错误)。

整个示例假设您要将值保存在缓冲区中并在下一次绘制时读回。只有 256 种颜色,它限制了您获得 16777216 个不同的值。大多数时候我不需要更大的规模。我也将其移动为在区间 <-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.;
}

还有一件事,溢出的值将四舍五入为 min/max 值。

为了在 vec2vec3vec4 中打包浮点值,必须限制并明确指定源值的范围,或者指数也必须以某种方式存储。
一般情况下,如果一个浮点数的有效位要以字节为单位进行打包,则必须从有效位中提取连续的8位包,并存储在一个字节中。


在受限和预定义的范围内对浮点数进行编码

必须定义值范围 [minValmaxVal],其中包括要编码的所有值,并且值范围必须映射到范围 [0.0, 1.0]。

[minValmaxVal] 到 vec2vec3vec4 范围内的浮点数编码:

vec2 EncodeRangeV2( in float value, in float minVal, in float maxVal )
{
    value        = clamp( (value-minVal) / (maxVal-minVal), 0.0, 1.0 );
    value       *= (256.0*256.0 - 1.0) / (256.0*256.0);
    vec3 encode  = fract( value * vec3(1.0, 256.0, 256.0*256.0) );
    return encode.xy - encode.yz / 256.0 + 1.0/512.0;
}

vec3 EncodeRangeV3( in float value, in float minVal, in float 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 encode.xyz - encode.yzw / 256.0 + 1.0/512.0;
}

vec4 EncodeRangeV4( in float value, in float minVal, in float 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;
}

vec2vec3vec4 解码为 [minValmaxVal] 范围内的浮点数:

float DecodeRangeV2( in vec2 pack, in float minVal, in float maxVal )
{
    float value  = dot( pack, 1.0 / vec2(1.0, 256.0) );
    value       *= (256.0*256.0) / (256.0*256.0 - 1.0);
    return mix( minVal, maxVal, value );
}

float DecodeRangeV3( in vec3 pack, in float minVal, in float maxVal )
{
    float value  = dot( pack, 1.0 / vec3(1.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 );
}

float DecodeRangeV4( in vec4 pack, in float minVal, in float maxVal )
{
    float 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 );
}

注意,由于标准的 32 位 [IEEE 754][2] 数字只有 24 位有效数字,因此将数字编码为 3 个字节是完全足够的。


对浮点数的有效数字和指数进行编码

将浮点数及其指数的有效数字编码为vec2vec3vec4:

vec2 EncodeExpV2( in float value )
{
    int exponent  = int( log2( abs( value ) ) + 1.0 );
    value        /= exp2( float( exponent ) );
    value         = (value + 1.0) * 255.0 / (2.0*256.0);
    vec2 encode   = fract( value * vec2(1.0, 256.0) );
    return vec2( encode.x - encode.y / 256.0 + 1.0/512.0, (float(exponent) + 127.5) / 256.0 );
}

vec3 EncodeExpV3( in float value )
{
    int exponent  = int( log2( abs( value ) ) + 1.0 );
    value        /= exp2( float( exponent ) );
    value         = (value + 1.0) * (256.0*256.0 - 1.0) / (2.0*256.0*256.0);
    vec3 encode   = fract( value * vec3(1.0, 256.0, 256.0*256.0) );
    return vec3( encode.xy - encode.yz / 256.0 + 1.0/512.0, (float(exponent) + 127.5) / 256.0 );
}

vec4 EncodeExpV4( 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 );
}

vec2vec3vec4 解码为浮点数及其指数的有效数字:

float DecodeExpV2( in vec2 pack )
{
    int exponent = int( pack.z * 256.0 - 127.0 );
    float value  = pack.x * (2.0*256.0) / 255.0 - 1.0;
    return value * exp2( float(exponent) );
}

float DecodeExpV3( in vec3 pack )
{
    int exponent = int( pack.z * 256.0 - 127.0 );
    float value  = dot( pack.xy, 1.0 / vec2(1.0, 256.0) );
    value        = value * (2.0*256.0*256.0) / (256.0*256.0 - 1.0) - 1.0;
    return value * exp2( float(exponent) );
}

float DecodeExpV4( 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) );
}


另请参阅以下问题的答案:

  • How do you pack one 32bit int Into 4, 8bit ints in glsl / webgl?

我测试了gman的方案,发现比例因子不正确,产生了舍入误差,如果要将结果存储到RGB纹理中,需要额外除以255.0。所以这是我修改后的解决方案:

#define SCALE_FACTOR (256.0 * 256.0 * 256.0 - 1.0)

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

float unpack8BitVec3IntoFloat(vec3 v, float min, float max) {
   vec3 scaleVector = vec3(1.0, 256.0, 256.0 * 256.0) / SCALE_FACTOR * 255.0;
   float zeroToOne = dot(v, scaleVector);
   return zeroToOne * (max - min) + min;
}

示例:

  • 如果使用 min=0 和 max=1 打包 0.25,您将得到 (1.0, 1.0, 0.247059)
  • 如果你解压那个向量,你会得到 0.249999970197678