DXTC(S3TC)圧縮のアルゴリズムとは?~前編~

初めまして、R&D部 上田と申します。
「OPTPiX Labs Blog」には初登場です。今回から、よく使われるいくつかの画像圧縮アルゴリズムについて解説していきたいと思います。

第一回目はDXTC(S3TC)圧縮について、です。

DXTCって何?

DXTCとは画像圧縮アルゴリズムの一種で、DirectX Texture Compressionの略称です。DXTCを使うと画像容量を小さくすることができます。ただし、非可逆圧縮なので画像が劣化します。また、S3 Graphics社の開発した技術なので、S3TC(=S3 Texture Compression)とも呼ばれています。

DXTCってどんなところで使われているの?

NINTENDO GAMECUBE®、Wii®、Wii U™、PSP®、PS3®、Xbox®、Xbox360®などの家庭用ゲーム機や、Windows PCのほとんどのグラフィックスカード、さらにNvidia Tegra搭載のスマートフォン・タブレットなどで、3Dグラフィックスを描画する際の圧縮フォーマットとして使われています。

なんでDXTCを使うの?(JPEGじゃダメなの?)

JPEGやPNGに比べるとDXTCの圧縮率は低いです。その代わり、DXTCは画像を圧縮したままGPUのVRAMに置いて使うことができる、という大きな利点があります。

これに対してJPEGなどでは、画像を無圧縮状態に展開しなければGPUで扱えないため、GPUを使って3Dグラフィックスのテクスチャを表示する場合、DXTCに比べてVRAM容量やメモリバンド幅(転送速度)を多く使ってしまいます。

DXTCの圧縮品質はどんな感じ?

DXTCを使って画像を圧縮してみましょう。

サンプルとして、24ビットRGBカラーで画像サイズが640×480の画像を用意しました。
それぞれの画像容量は、無圧縮で 921,600 バイト(=640×480×3)です。

flower_quarter cat_quarter renga_quarter
無圧縮の元画像(各 921,600 バイト)
(クリックで等倍表示)

DXTCで圧縮します。

flower_dxt1_quarter cat_dxt1_quarter renga_dxt1_quarter
DXTC圧縮後の画像(各 153,600 バイト)
(クリックで等倍表示)

圧縮するとそれぞれの画像容量は 153,600 バイト となり、圧縮前の6分の1になります。

DXTCは圧縮すると画像が劣化すると書きましたが、圧縮前後の画像を比較していただくとわかるように、劣化はかなり注意して見ないとわかりません。

注)ブログ内の画像は、ブラウザでそのまま表示できるように全てPNG形式に変換しているため、上記画像容量とは異なります。以下の画像についても同様です。

DXTCには苦手な画像がある?

今度は次のような画像をDXTCで圧縮してみましょう。上の3つの画像とは違ってアニメ調の画像です。

magicgirl_org

無圧縮の魔法少女イラスト(クリックで3倍表示)

magicgirl_dxt1_org

DXTC圧縮後の魔法少女イラスト(クリックで3倍表示)

パッと見た感じは綺麗に圧縮できている気がするんですが……、

キャラクターの輪郭あたりをよく見てみると(この辺 magicgirl_dxt1_upscale_half とか)、色がにじんでしまっているのがわかると思います。

実はDXTCにはアニメ調の画像を圧縮すると画像が劣化しやすい、という特徴があります。

写真のような画像よりもアニメ調の画像が劣化しやすいのは、DXTCの仕組みに原因があります。

DXTCってどんな仕組み?

それではDXTC圧縮の仕組みを説明しましょう。
圧縮前の画像として上で表示した画像(花)を使います。

flower_quarter

花 (クリックで等倍表示)

24ビット RGBカラーで画像サイズが640×480、画像容量は 921,600 バイト の画像です。

1.4×4ピクセルのブロックに分割する

DXTCでは、4×4ピクセルを1つのブロックとして、ブロック毎にそれぞれ独立して圧縮処理を行います。

upscale_half

画像を拡大して1ブロック(4×4ピクセル)取り出す

2.代表色を2色選び、それらの中間色を2色つくる

さて、この1ブロックのそれぞれのピクセルには、それぞれ別の色が入っています。こうやって拡大すると色の違いが目に見える形になるのですが、拡大前の画像を見てこのピクセル毎の色の違いが分かるような人はほとんどいないでしょう。

DXTC圧縮ではこの1ブロック(16ピクセル)を4色で表現します。
まずブロックの代表色となる2色を16ビットのRGBカラー(RGB565)から選び、この2色から中間色となる2色を作ります。

select_color_half

代表色と中間色

中間色を生成するのは情報量を減らすためで、2色分の情報量(16ビット × 2色 = 32ビット)で4色を表現することができます。

このとき、代表色C0>C1となるように代表色を選んでいます。(C0≦C1とした場合については後で説明します)

この4色をどのように選ぶか、というアルゴリズムについてはDXTCでは規定されておらず、圧縮ツールによって色々な手法が採られています。OPTPiX imésta では、独自のアルゴリズムを用い、より高画質な圧縮を行うことができます。

3.代表色と中間色を使って、ブロック内の色を置き換える

こうしてできた4色を使ってブロックの色を置き換えていきます。

color_replace_half

代表色と中間色を使って色を置き換える

各ピクセルの色が変化しているのが分かるでしょうか?ブロック内の全てのピクセルがC0~C3のどれかの色に置き換わっています。

4.ブロック内のピクセルにインデックスを付ける

そして最後に4色のうちどの色を使ったのかを各ピクセル毎にインデックス情報として記憶していきます。
4色が区別できればいいので、2ビット(00,01,10,11)で表現できます。

index_half

各色に対してインデックスを付ける

5.完成!

以上1~4の処理を行うことで、各ブロック毎に代表色2色(16ビット RGBカラー)と各ピクセルに付けたインデックスを記憶しておけば、そのブロックを再現できるということになります。

conclusion_half

圧縮終了!

これにより、圧縮前の画像では1ブロックを表現するために、(16ピクセル)×(24ビット RGBカラー) = 384 ビット = 48 バイトが必要だったのに対し、

圧縮後の画像では1ブロックを表現するために、(16ピクセル)×(2ビット のインデックス)+(16ビット RGBカラー)×(2色) = 64 ビット = 8 バイトだけで大丈夫になりました。

全体の画像容量も 921,600 バイト → 153,600 バイト(6分の1) となります。

結局、なんでアニメ調の画像が苦手なの?

ということになりますね。
例えば、次のような1ブロックにDXTC圧縮をするとしましょう。

block_quarter

DXTC「苦手なヤツです」

このとき、どのような代表色2色を選べばいいのでしょう?
とりあえず、ブロック内に黒色・水色・茶色の3色があるのでそれらから代表色2色を選んで中間色を作ってみます。

color_pair_half

代表色を選んで中間色を作ってみる

どの2色を選んだとしても、その2色の中間色ではもう1つの色を作りだせないですね。
実は、この1ブロックの画像をDXTC圧縮すると、こんな画像ができます。

block_dxt1_quarter

DXTC「苦手だって言ったのに…」

このように、DXTCでは代表色とその中間色を使ってブロック内の色を置換するので、ブロック内の色によっては置換がうまくできない場合があります。

そして、写真のような画像よりもアニメ調の画像の方がこのようなブロックが多いのです(特に輪郭線を黒で描いている部分など)。

結果としてDXTCはアニメ調の画像が苦手ということになります。

C0≦C1だった場合について

DXTCの仕組み 手順2で「C0≦C1」とした場合についてですが、このように代表色を選ぶと中間色の計算方法が変わります。

dxt1_alpha_half

C0とC1の大小による中間色の変化

中間色が1色になるかわりに、透過色(α=0)が使えるようになります。

さらに続く…

今回の解説は、実はDXTCの中のDXT1と呼ばれる規格で、DXTCにはDXT1の他に DXT2~DXT5までの合計5つの規格があります。

次回はDXT2~DXT5の解説記事を書いていきたいと思います。

DXTC(S3TC)圧縮のアルゴリズムとは?~後編~

タグ , , , | 2020/06/16 更新 |