如何将 27 个 vector3 编码为 0-256 值?

how to encode 27 vector3's into a 0-256 value?

我有 3 个值的 27 种组合,从 -1 到 1 类型:

 Vector3(0,0,0);
 Vector3(-1,0,0);
 Vector3(0,-1,0);
 Vector3(0,0,-1);
 Vector3(-1,-1,0);
 ... up to
 Vector3(0,1,1);
 Vector3(1,1,1);

我需要将它们与 8 位 sbyte/byte 数组相互转换。

一个解决方案是说第一个数字,256 = X 第二个数字是 Y,第三个是 Z...

所以

  Vector3(-1,1,1) becomes 022,
  Vector3(1,-1,-1) becomes 200,
  Vector3(1,0,1) becomes 212...

我更喜欢用更紧凑的方式编码,也许使用字节(我对此一无所知),因为上面的解决方案使用了很多乘法和舍入函数来解码,你有什么建议吗?另一种选择是写27 if条件将Vector3组合写入数组,这似乎效率低下。

感谢 Evil Tak 的指导,我稍微更改了代码以将 0-1 值添加到第一位,并使其适用于 unity3d:

function Pack4(x:int,y:int,z:int,w:int):sbyte {
var b: sbyte = 0;

b |= (x + 1) << 6; 
b |= (y + 1) << 4;
b |= (z + 1) << 2;
b |= (w + 1);   
return b;
}

function unPack4(b:sbyte):Vector4 {
var v : Vector4; 
v.x = ((b & 0xC0) >> 6) - 1;      //0xC0 == 1100 0000   
v.y = ((b & 0x30) >> 4) - 1;    // 0x30 == 0011 0000
v.z = ((b & 0xC) >> 2) - 1;     // 0xC  == 0000 1100
v.w = (b  & 0x3) - 1;            // 0x3  == 0000 0011
return v;
}

一种方法是将每个向量的分量存储在一个字节的每 2 位中。

将矢量分量值与 2 位存储形式相互转换就像分别加一和减一一样简单。

-1 (1111 1111 as a signed byte) <-> 00 (in binary)
 0 (0000 0000 in binary)        <-> 01 (in binary)
 1 (0000 0001 in binary)        <-> 10 (in binary)

打包的 2 位值可以按您喜欢的任何顺序存储在一个字节中。我将使用以下格式:00XXYYZZ 其中 XX 是 X 组件的转换(打包)值,依此类推。开头的 0 不会被使用。

一个向量将被打包成一个字节,如下所示:

byte Pack(Vector3<int> vector) {
    byte b = 0;
    b |= (vector.x + 1) << 4; 
    b |= (vector.y + 1) << 2;
    b |= (vector.z + 1);
    return b;
}

将向量从其字节形式解包如下:

Vector3<int> Unpack(byte b) {
    Vector3<int> v = new Vector<int>();
    v.x = ((b & 0x30) >> 4) - 1;    // 0x30 == 0011 0000
    v.y = ((b & 0xC) >> 2) - 1;     // 0xC == 0000 1100
    v.z = (b & 0x3) - 1;     // 0x3 == 0000 0011
    return v;
}

以上两种方法都假设输入有效,即Packvector的所有分量都是-101并且 Unpackb 的所有两位部分都具有 000110.

的(二进制)值

由于此方法使用位运算符,因此速度快且效率高。如果你想进一步压缩数据,你也可以尝试使用2个未使用的位,并将每3个处理的两位元素转换为一个向量。

最紧凑的方法是在基数 3 中写入 27 位数字(使用移位 -1 -> 00 -> 11 -> 2)。

这个数字的值将从 03^27-1 = 7625597484987,这需要 43 位进行编码,即 6 字节(和 5 备用位)。

与将 4 两位数字打包在一个字节中的打包表示相比,这节省了一点(因此总共 7 字节/56 位)。

一个有趣的变体是以字节为单位将基数 3 数字五乘五分组(因此数字 0242)。您仍然需要 6 个字节(并且没有备用位),但是字节的解码可以很容易地硬编码为 table 个 243 个条目。

  1. 我假设你的值是 float 而不是整数

    因此与转换为整数类型相比,位操作不会提高太多速度。所以我打赌使用全范围会更好。我会为 3D 案例这样做:

    8 bit -> 256 values
    3D -> pow(256,1/3) = ~ 6.349 values per dimension
    6^3 = 216 < 256
    

    所以 (x,y,z) 的包装看起来像这样:

    BYTE p;
    p =floor((x+1.0)*3.0);
    p+=floor((y+1.0)*3.0*6.0);
    p+=floor((y+1.0)*3.0*6.0*6.0);
    

    想法是将 <-1,+1> 转换为范围 <0,1> 因此 +1.0*3.0 而不是 *6.0 然后乘以到正确的位置最终 BYTE.

    p 的解包如下所示:

    x=p%6; x=(x/3.0)-1.0; p/=6;
    y=p%6; y=(y/3.0)-1.0; p/=6;
    z=p%6; z=(z/3.0)-1.0;
    

    通过这种方式,您可以使用 256 个值中的 216 个值,这比仅使用 2 位(4 个值)要好得多。你的 4D 案例看起来很相似只是使用 3.0,6.0 不同的常量 floor(pow(256,1/4))=4 所以使用 2.0,4.0 但当心 p=256 的情况或者每个维度使用 2 位和位方法就像接受的答案

    如果你需要真正的速度,你可以优化它以强制浮点表示保存数据包 BYTE 的结果到特定的指数并提取尾数位作为你的打包 BYTE直接。由于结果将是 <0,216>,您可以向其添加任何更大的数字。有关详细信息,请参阅 IEEE 754-1985,但您希望尾数与您的 BYTE 对齐,因此如果您添加到 p 数字,例如 2^23,那么 float 的最低 8 位应该是您的包装直接值(因为 MSB 1 不存在于尾数中)所以不需要昂贵的转换。

  2. 如果你得到的只是 {-1,0,+1} 而不是 <-1,+1>

    然后你应该使用整数方法,比如每个维度 2 位的位打包,或者使用所有 3^3 = 27 可能性的 LUT table 并打包整个5 位向量。

    编码看起来像这样:

    int enc[3][3][3] = { 0,1,2, ... 24,25,26 };
    p=enc[x+1][y+1][z+1];
    

    并解码:

    int dec[27][3] = { {-1,-1,-1},.....,{+1,+1,+1} };
    x=dec[p][0];
    y=dec[p][1];
    z=dec[p][2];
    

    这应该足够快,如果你有很多向量,你可以将 p 打包到每 5 位中......以节省更多内存 space