注意書き

本サイトでは、アフィリエイト広告およびGoogleアドセンスを利用しています。

GenerateMipMaps (DirectXTex) の罠

プログラミング

DirectXTexに用意されているミップマップを作成する関数 GenerateMipMaps の挙動で振り回された点があります。今回はそれをこのブログで共有します。

スポンサーリンク

どのようなことが起こったのか

最初に観測できた症状が、ミップマップを生成したら、特定のエリアが消えてしまった!というものです。そしてよく見ると他にも破損しているような印象をうけるところがあって、なにかバグの気配を感じるものでした。

ここでは私が遭遇した画像を並べて表示したものを以下に掲載します。左側がベースレベル、右側が1つ先のミップレベルのものです。縞模様の上部が破損しているような気配や、チェックマークのパートが1つ消えてることが分かります。

このスクショからピンと来た人は、すごいと思います。私はかなり悩まされてしまいました…なお画像データからミップマップを生成しているコードは以下の通りです。

DirectX::LoadFromWICMemory(data, size, flags, &metadata, image);
// ミップマップを作成する.
DirectX::ScratchImage mipChain;
DirectX::GenerateMipMaps(image.GetImages(), image.GetImageCount(), image.GetMetadata(), DirectX::TEX_FILTER_DEFAULT, 0, mipChain);
image = std::move(mipChain);
metadata = image.GetMetadata();

原因や対策など

色々と調べてみて、ようやく分かったこととして、関係しているのはアルファ値でした。ミップマップ側で消えたパートを見てみると、アルファ値は0でした。RGBカラーはあるがアルファは0という状態のところで、先ほど示した使い方で GenerateMipMaps関数を使うと、このような症状に出遭ってしまうようです。アルファ0のピクセルは次のミップマップ計算の時点でカラーも0,0,0の扱いになってしまうようで、消えてしまいました。

他のツールでは

アルファ=0となる状態で、カラー値を見られるツールというのはあまりないようです。ペイントにしろPhotoshopにしろ、いわゆる乗算済みアルファの状態で表示してしまうようで、今回のようなベースレベルの画像の確認にも不適でした。

そのような中、NVIDIA Texture Tools Exporter であれば、カラーとアルファチャンネルでの切り替え表示が可能で、さらにミップマップの生成とその確認もできるというものでした。以下にミップマップを作ったときの様子を示しています。先ほどはベースレベルから生成したミップマップにおいて、消えてしまった箇所が正しく残っています。またここに記載はしませんが、アルファチャンネル表示してみると期待通りのアルファが入っていました。

DirectXTex のワークアラウンド

DirectXTex のミップマップ生成(GenerateMipMaps) において、可能なら WIC (Windows Imaging Component) によるスケーラーでダウンサンプリング処理されるような実装となっているようです。これが事前乗算済みアルファの振る舞いとなる記載があったので、その関係が影響していそうです。

そこで対策としては、WICのスケーラを使わないで処理をするということをしてみると、期待通りの結果が得られました。コードとしては以下のようなものになります。TEX_FILTER_FORCE_NON_WIC を指定しているだけですね。

// ミップマップを作成する.
DirectX::ScratchImage mipChain;
DirectX::GenerateMipMaps(image.GetImages(), image.GetImageCount(), image.GetMetadata(), DirectX::TEX_FILTER_BOX | TEX_FILTER_FORCE_NON_WIC, 0, mipChain);

余談

私がこの症状をみたのは、glTFのサンプルモデル(Alpha Blend Mode Test)を描画したときでした。以下の画像に示すように、概ね正しい結果が得られたのに、Opaqueのチェックマークが表示されない!というものでした。これは、ミップマップ生成時にアルファ値が0のピクセルで失われたカラー情報が、ミップマップ有効で描画した際に使用されたのが原因です。

コメント

タイトルとURLをコピーしました