« skynet 1.1.0 发布 | 返回首页 | Windows 下重定向当前进程的 stdout 到网络连接 »

四元数的压缩存储

今天在读 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

通过最大分量取负的方式省掉符号位的方式理论上可行,但是在动画中是不行的。因为动画中是帧插值,两帧之前如果前边的q变成了-q,会导致中间的插值都发生错误。
如果,每个分量的精度降底到10bit,可以三个较小分量,各存10个bit。最大分量以:1-(x*x+y*y+z*z),求出。再拿两个bit存最大分量位数。一共只花32bit。故用32bit的数据类型就满足了。
Q(x,y,z,w) = Q(-x,-y,-z,-w) 故,发一现最大那个分量为负,就马上四个分量取负,再压缩。解缩时都按正数处理,可以,再省一个bit.
这个读着像Hacker’s Delight里面的内容

Post a comment

非这个主题相关的留言请到:留言本