2021年6月15日火曜日

BC5 圧縮テクスチャ

はじめに

プログラムから, テクスチャ圧縮がしたいです. BC1, BC3, BC6h BC7についてはispc texture compressionを使おうと思っていましたが, BC5がないのです.

https://software.intel.com/content/www/us/en/develop/articles/fast-ispc-texture-compressor-update.html

ガイドに仕様がちゃんと書いてあります. 簡単そうなので作ってしまいます.

https://docs.microsoft.com/en-gb/windows/win32/direct3d10/d3d10-graphics-programming-guide-resources-block-compression#bc5

ファイルフォーマット

4x4のブロックごとに圧縮するブロック圧縮です. BC4はRだけの1チャネル, BC5はRGの2チャネルを扱います. 4x4x2 = 32 bytesを次の tablex2 = 16 bytesに圧縮します. BC5_UNORMとBC5_SNORMの2バリエーションです. 2つはシェーダでサンプルした場合, [0 1]の浮動小数点数になるか, [-1 1]の浮動小数点になるかが違います.

テーブルは, 4x4の最小値と最大値が先頭に2バイト, 次に3bits x 16のテーブルへのインデックスが並びます.

  1. /*
  2. MSB LSB
  3. | red_0 | 1 byte minimum color
  4. | red_1 | 1 byte maximum color
  5. | red_h | red_g | red_f | red_e | red_d | red_c | red_b | red_a | 3 bits x 8
  6. | red_p | red_o | red_n | red_m | red_l | red_k | red_j | red_i | 3 bits x 8
  7. */

テーブルは明示的に持たず最大値, 最小値から計算します. UNORMとSNORMで1箇所異なりますが, 圧縮するだけならばどちらでも同じなので統一します.

  1. static constexpr u32 Shift = 20;
  2. void createTable(s32 colors[8], s32 block[16])
  3. {
  4. colors[0] = block[0];
  5. colors[1] = block[0];
  6. for(u32 i = 1; i < 16; ++i) {
  7. colors[0] = minimum(colors[0], block[i]);
  8. colors[1] = maximum(colors[1], block[i]);
  9. }
  10. colors[0] <<= Shift;
  11. colors[1] <<= Shift;
  12.  
  13. if(colors[1] < colors[0]) {
  14. // 6 interpolated color values
  15. colors[2] = (6 * colors[0] + 1 * colors[1]) / 7; // bit code 010
  16. colors[3] = (5 * colors[0] + 2 * colors[1]) / 7; // bit code 011
  17. colors[4] = (4 * colors[0] + 3 * colors[1]) / 7; // bit code 100
  18. colors[5] = (3 * colors[0] + 4 * colors[1]) / 7; // bit code 101
  19. colors[6] = (2 * colors[0] + 5 * colors[1]) / 7; // bit code 110
  20. colors[7] = (1 * colors[0] + 6 * colors[1]) / 7; // bit code 111
  21. } else {
  22. // 4 interpolated color values
  23. colors[2] = (4 * colors[0] + 1 * colors[1]) / 5; // bit code 010
  24. colors[3] = (3 * colors[0] + 2 * colors[1]) / 5; // bit code 011
  25. colors[4] = (2 * colors[0] + 3 * colors[1]) / 5; // bit code 100
  26. colors[5] = (1 * colors[0] + 4 * colors[1]) / 5; // bit code 101
  27. colors[6] = 0; // bit code 110
  28. colors[7] = 255<<Shift; // bit code 111
  29. }
  30. }

ピクセル値に最も近いテーブルインデックスを検索して, 符号化します.

  1. u64 findNearest(s32 colors[8], s32 x)
  2. {
  3. u32 index = 0;
  4. s32 mind = absolute(colors[0] - x);
  5. for(u32 i = 1; i < 8; ++i) {
  6. s32 d = absolute(colors[i] - x);
  7. if(d < mind) {
  8. mind = d;
  9. index = i;
  10. }
  11. }
  12. return index;
  13. }

まとめ

GPUOpen Compressonatorと結果が異なるため, もっといい方法がありそうです.

0 件のコメント:

コメントを投稿