EnglishРусскийTürkçeDeutschFrançais À propos · RSS
WebInterface.org
WebInterface.org

Notes sur le dessin, l’impression, les photographies et les images créées par ordinateur.

Accueil · Graphisme tridimensionnel · Bits fractionnaires dans une rotation compactée

Bits fractionnaires dans une rotation compactée

Un quaternion unitaire est constitué de quatre nombres dont les carrés additionnés donnent un. Ken Shoemake les a introduits dans l’infographie en 1985 afin de stocker et d’interpoler des rotations, et on les retrouve aujourd’hui partout où il faut conserver une rotation. Un squelette animé en stocke un pour chaque articulation dans chaque image clé. Un maillage peut stocker le repère tangent de chaque sommet sous forme de quaternion, pour le normal mapping. Une scène de Gaussian splatting attribue une orientation à chacune de ses millions de gaussiennes, et il s’agit là aussi d’un quaternion. La mémoire et la bande passante, plus que l’arithmétique, sont ce qui ralentit ces programmes. Un octet économisé sur une rotation l’est des millions de fois.

Ces quatre nombres ne sont pas indépendants. Une fois trois d’entre eux connus, le quatrième est déterminé au signe près. Un compresseur stocke donc trois nombres ainsi qu’un petit index appelé carte qui indique comment reconstruire le quatrième. La méthode la plus courante est celle des trois plus petites : on élimine la composante ayant la plus grande valeur absolue, on note laquelle c’était et son signe. ozz-animation fonctionne de cette façon, tout comme l’extension glTF EXT_meshopt_compression et les formats de splats SPZ et SOG.

Dans Compression of Uniformly Distributed Unit Quaternions, publié sur arXiv en octobre 2026, Carsten Faber, Bastian Kuth, Rinaldo Schuster, Daniel Pauls et Quirin Meyer, de l’Université des sciences appliquées et des arts de Coburg ainsi que d’AMD, divisent plutôt par la plus grande composante. Cela place la rotation sur la surface d’un cube à quatre dimensions. Une déformation cubique, de la forme a·x3 + (1 − a)·x avec a égal à 0,373964, répartit ensuite les échantillons de manière plus uniforme que ne le ferait le cube seul. Les auteurs appellent ce résultat ℓ∞cub. Ici, c’est le codec du cube. Quatre choix de plus grande composante multipliés par deux signes donnent huit cartes, et huit cartes tiennent dans trois bits.

Les bits qui ne se divisent pas par trois

Plaçons une telle rotation dans un mot de 32 bits. La carte occupe trois bits, laissant ainsi 29 bits pour trois nombres, soit environ 9,67 bits par nombre. Les champs de bits ne connaissent que des bits entiers. Chaque nombre dispose de 9 bits, ce qui correspond à 512 niveaux, et deux bits du mot restent inutilisés. Une autre solution consiste à attribuer 10 bits à un nombre et moins aux autres, afin qu’un axe de la rotation soit stocké avec plus de précision que les autres.

L’article empaquette le mot sous forme d’un entier unique dans un système de numération à bases mixtes, où chaque chiffre possède sa propre base. La carte est un chiffre en base 8. Chacun des trois nombres correspond à un chiffre en base U. L’empaquetage multiplie par U et additionne, trois fois de suite. Le dépaquetage prend le reste et divise, trois fois, et ce qui reste à la fin est la carte. La plus grande valeur de U pour laquelle 8·U3 reste inférieur à 232 est 812. Chaque axe compte 812 niveaux au lieu de 512, et l’écart entre les échantillons voisins diminue pour atteindre environ 0,63 de l’écart des champs de bits. Bastian Kuth et Quirin Meyer avaient déjà utilisé ce même empaquetage en 2021 pour les poids de mélange des sommets. Les auteurs le comparent aux systèmes de numération asymétriques à distribution uniforme.

Le coût de la division

La division semble lente pour un décodeur. Ici, chaque base est une constante, ce qui amène le compilateur à transformer chaque division et chaque reste en une multiplication et un décalage. Lorsque les bits restants après la carte se divisent équitablement par trois, comme c’est le cas pour 24 et 48 bits avec trois bits de carte, aucune division entière n’est nécessaire. Les auteurs ont mesuré des décodages isolés à l’aide du compteur de cycles matériel d’une AMD Radeon RX 7900 XTX. Leur décodeur à 32 bits a nécessité 111 cycles. Une interpolation linéaire sphérique entre deux quaternions a demandé 171 cycles sur la même carte graphique. La méthode MPEG-4, qui déforme le cube à l’aide d’une tangente, a nécessité 273 cycles, car la tangente est compilée en un sinus divisé par un cosinus. La déformation cubique se combine avec l’opération qui transforme un niveau en nombre, et ces deux opérations ensemble coûtent trois multiplications-additions fusionnées (FMA).

Le signe qui ne porte rien

Un quaternion et son opposé décrivent la même rotation. Un codec peut inverser chaque rotation de façon à ce que la plus grande composante soit positive, ce qui évite d’avoir à enregistrer le signe. Certaines bibliothèques d’animation le font. ozz-animation ne le fait pas. La bibliothèque consacre 48 bits à chaque clé : 15 bits pour chacun des trois nombres, deux bits pour la carte et un bit pour le signe. Sans ce dernier, 46 bits restent disponibles. Trois champs de bits identiques ne peuvent pas partager 46 bits, c’est pourquoi la bibliothèque conserve le signe ainsi que ses 15 bits par nombre. En tant qu’entier unique, ces 46 bits donnent à chaque axe 41 285 niveaux au lieu de 32 768, ce qui correspond à 246/3 arrondi vers le bas. Un bit récupéré, partagé par les trois axes, représente un facteur de 21/3, soit environ 1,26, pour chacun d’eux.

Le jeu de test était le corpus Mixamo, soit l’ensemble de ses 34 884 287 images clés. Le codec original d’ozz-animation présentait une erreur maximale de 0,00909°. Le codec du cube, qui utilise toujours 15 bits par nombre tout en conservant le signe, a réduit cette valeur à 0,00203°. L’abandon du signe a encore abaissé l’erreur à 0,00161°, soit 5,6 fois moins que la valeur de départ. Une clé de 40 bits sans signe donnait une erreur de 0,01020°, 12 % de plus que le format de 48 bits original, tout en économisant un octet par clé, soit 33,3 Mio pour l’ensemble du corpus. Les rendus d’un dragon animé des deux manières présentaient des erreurs d’image comparables. L’échantillonnage de 1024 personnages skinnés était 12 à 16 % plus rapide qu’avec le codec original.

Un repère tangent est une rotation qui peut aussi être réfléchie, car les coordonnées de texture peuvent être retournées le long d’une couture. Le bit que le signe n’occupe plus peut servir à enregistrer cette orientation. Les QTangents de CryEngine, présentés en 2011, conservaient quatre composantes de 16 bits et cachaient l’orientation dans le signe de la quatrième composante, en éloignant cette valeur de zéro afin que le signe survive aux arrondis. Avec le codec du cube à 16 bits par repère, huit cartes laissent 13 bits pour les trois nombres. Les champs de bits donneraient 16 niveaux à chaque nombre. Un entier unique en donne 20, et utilise 64 000 des 65 536 codes possibles. L’article indique 15,97 bits utilisés dans ce cas.

Niveaux par axe : champs de bits contre un entier unique
Ce qui est stockéBitsCartesChamps de bitsUn entier
Rotation avec son signe328512812
Clé d’animation sans signe48432 76841 285
Clé d’animation sans signe4044 0966 501
Repère tangent avec orientation1681620

Les splats gaussiens sont traités de la même manière, car un quaternion et son opposé donnent la même gaussienne. Le moteur de rendu de Rijsdijk et ses collaborateurs range la géométrie de chaque gaussienne dans 128 bits, dont 30 sont consacrés à la rotation, puis il compresse cette rotation à l’aide d’une application exponentielle. Sur trois scènes de Mip-NeRF 360, l’application exponentielle à 30 bits atteignait un rapport signal sur bruit de crête de 45,90 dB. Le codec du cube a atteint 45,83 dB avec 24 bits. Avec 16 bits, il avait 6,67 dB d’avance. Le décodage des rotations prenait environ 0,5 ms par image dans les deux cas, contre 11,8 ms pour le rendu.

Nombres impairs de niveaux et borne inférieure

Un champ de bits contient toujours un nombre pair de niveaux, puisque c’est une puissance de deux. Le quantificateur de l’article place ses échantillons au centre de pas égaux sur l’intervalle allant de −1 à 1, de sorte qu’avec un nombre pair aucun échantillon ne se situe à zéro. Zéro correspond à la rotation identité, au milieu de sa carte, et les animations sont remplies de rotations identité. Un champ de bits ne la restitue jamais exactement. Un nombre impair place un échantillon sur zéro, et l’entier unique laisse le codec choisir un nombre impair. Les auteurs jugent cela trivial à imposer. Leur analyse des erreurs ne le couvre pas.

Il existe une borne inférieure à tout cela. L’article montre qu’aucun code disposant de 2k états ne peut maintenir son erreur maximale en deçà d’environ 96° multiplié par 2−k/3. Pour de grands budgets de bits, le codec du cube nécessite environ 1,5 bit de plus que cette borne. Cette borne suppose que les rotations sont réparties de manière égale entre toutes les orientations. Une piste d’animation qui ne tourne que légèrement peut obtenir de meilleurs résultats avec une plage adaptée à cette piste, ce qui est précisément ce que fait l’Animation Compression Library. Chaque code utilisé ici a la même longueur, contrairement à l’étape Huffman de la compression JPEG. Ainsi, un shader peut lire n’importe quelle rotation sans devoir décoder celles qui la précèdent.

Source

Carsten Faber, Bastian Kuth, Rinaldo Schuster, Daniel Pauls et Quirin Meyer, Compression of Uniformly Distributed Unit Quaternions, arXiv, octobre 2026, DOI 10.48550/arXiv.2610.22052, publié sous la licence CC BY 4.0. Chaque nombre, chaque mesure de temps et chaque borne de cette note en proviennent.

Plus tôt: Lancer de rayon