如何将 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;
}
以上两种方法都假设输入有效,即Pack
中vector
的所有分量都是-1
、0
或1
并且 Unpack
中 b
的所有两位部分都具有 00
、01
或 10
.
的(二进制)值
由于此方法使用位运算符,因此速度快且效率高。如果你想进一步压缩数据,你也可以尝试使用2个未使用的位,并将每3个处理的两位元素转换为一个向量。
最紧凑的方法是在基数 3
中写入 27
位数字(使用移位 -1 -> 0
、0 -> 1
、1 -> 2
)。
这个数字的值将从 0
到 3^27-1 = 7625597484987
,这需要 43
位进行编码,即 6
字节(和 5
备用位)。
与将 4
两位数字打包在一个字节中的打包表示相比,这节省了一点(因此总共 7
字节/56
位)。
一个有趣的变体是以字节为单位将基数 3
数字五乘五分组(因此数字 0
到 242
)。您仍然需要 6
个字节(并且没有备用位),但是字节的解码可以很容易地硬编码为 table 个 243
个条目。
我假设你的值是 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
不存在于尾数中)所以不需要昂贵的转换。
如果你得到的只是 {-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
我有 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;
}
以上两种方法都假设输入有效,即Pack
中vector
的所有分量都是-1
、0
或1
并且 Unpack
中 b
的所有两位部分都具有 00
、01
或 10
.
由于此方法使用位运算符,因此速度快且效率高。如果你想进一步压缩数据,你也可以尝试使用2个未使用的位,并将每3个处理的两位元素转换为一个向量。
最紧凑的方法是在基数 3
中写入 27
位数字(使用移位 -1 -> 0
、0 -> 1
、1 -> 2
)。
这个数字的值将从 0
到 3^27-1 = 7625597484987
,这需要 43
位进行编码,即 6
字节(和 5
备用位)。
与将 4
两位数字打包在一个字节中的打包表示相比,这节省了一点(因此总共 7
字节/56
位)。
一个有趣的变体是以字节为单位将基数 3
数字五乘五分组(因此数字 0
到 242
)。您仍然需要 6
个字节(并且没有备用位),但是字节的解码可以很容易地硬编码为 table 个 243
个条目。
我假设你的值是
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 位应该是您的包装直接值(因为 MSB1
不存在于尾数中)所以不需要昂贵的转换。如果你得到的只是
{-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