四元数的压缩存储
今天在读 https://github.com/guillaumeblanc/ozz-animation 这个动画库的代码时,发现它使用了一个有趣的四元数压缩技术。
我们用四元数来表示 3D 空间中的旋转,通常需要 4 个浮点数。不过用四元数表示旋转时,四元数通常会先做一次归一化,即 x*x + y *y + z*z + w*w = 1
。所以我们只需要保留 x,y,z 和 w 的符号位就够了。
但三个 float 来表示四元数既然有压缩余地。这是因为用来表示旋转量时, float 提供的 23 bit 精度是多余的。 1/2^23 的转角在视觉上完全是忽略不计的。而且 x,y,z 都一定小于 1 ,所以 float 的指数位也浪费了 1bit 。
ozz-animation 这个库采用了一个有趣的方法来做进一步压缩。
如果直接用 half 来代替 float 是达不到,或是浪费了精度的。half 只有 10bit 有效精度,也就是千分之一左右,这是肉眼可察的误差。
因为 x y z 都归一化过了,绝对值都小于 1 ,所以我们可以考虑直接用定点数,保留小数点后 16 位,最小可表示角度就提高到了六万分之一圆周。
由于需要 1 个 bit 记录第四分量的符号位,如果直接在前三个分量的数据位中减去,会导致不平均。所以,可以不去保存固定的四元数的前三个量,而是保存四个分量中最小的三个。第二大的分量最大不会超过1/√2,所以可以把每个分量再乘上√2,这样大约可以增加半个 bit 的精度。由于是使用的定点数,这种方法对于接近零的小数字意义比浮点计数法要大。
这样,还需要花上 2 bit 记录最大的分量是第几个。连同最大分量的符号,一共需要额外的 3 bit ,正好每个分量扣除一个 bit 。
ozz-animation 因为同时还需要记录四元数归属的骨骼编号,所以把这额外的 3 bit 压缩在骨骼编号字中。我想打包在 3 个 16bit 定点数中也可以。每个分量在符号位外还能保留 14bit 的精度,可以比 half 的精度高16 倍。
Comments
Posted by: nealwu | (4) August 30, 2021 11:50 AM
Posted by: 小锡 | (3) March 29, 2019 11:46 AM
Posted by: 小锡 | (2) March 29, 2019 11:20 AM
Posted by: 周枫 | (1) November 10, 2017 09:42 AM