ディザリングの実装
ディザリング
トーンマップやガンマ補正を行うと, Float精度のHDRテクスチャから8bit精度のSDRテクスチャへのマッピングができるわけですが, シーンによっては精度不足であったりしてバンドが見えてしまうことがあります。
上の図は,画面の左から右にかけて,なめらかに0.0~1.0で明るさを変化させているシーンに対して, Logトーンマップを適用した結果です。精度不足なのかバンドが微妙に見えています。
SDRテクスチャは256分割の分解能しかないので,シーンによってはマッピング後に階段状に見えてしまうことがあります。
そこで今回実装するのがディザリングです。
https://en.wikipedia.org/wiki/Ordered_dithering
Bayer Matrix
以下のような行列を4x4のBayer行列と呼びます。
0/16 | 8/16 | 2/16 | 10/16 |
---|---|---|---|
12/16 | 4/16 | 14/16 | 6/16 |
3/16 | 11/16 | 1/16 | 9/16 |
15/16 | 7/16 | 13/16 | 5/16 |
実際行列演算をするわけではなくて,このタイルをスクリーン全体に敷き詰めて使います。
あるピクセルのトーンマップ後のカラーを256分割の整数値で表したとき,
その小数部分が対応する位置のBayer行列の値よりも大きいか小さいかを比較して,
出力色としてを採用するか,を採用するかを判断します。
シェーダー内部でif文など条件分岐で,この比較を行うと遅くなるので,このBayer行列を4x4のテクスチャに変換します。
あとはシェーダーで,Bayer行列テクスチャをWRAPサンプラでフェッチしながら,適当にスケールして足し算することでディザリングができます。
// SCREEN_SIZE : スクリーンのピクセルサイズ // DITHER_TEXTURE_SIZE : Bayer行列テクスチャサイズ. DITHER_TEXTURE_SIZE=4 // DITHER_SCALE : スケール.8bit整数ならば DITHER_SCALE=1/255 const float3 ditherMatrix = SampleLod0(g_BeyerMatrixTexture, g_BeyerMatrixSampler, uv*SCREEN_SIZE / DITHER_TEXTURE_SIZE).rgb; outputColor.rgb += ditherMatrix * DITHER_SCALE - DITHER_SCALE / 2.0f;
結果
ディザリング無し |
ディザリング有り |
---|
左右で並べてみると,よくわかりませんが,それは縮小して見ているからです。 フルサイズで比較すると,バンドが軽減されているのがわかります。
ディザリング有り/無しの結果の一部分をトリミングして,適当にレベル補正をした結果の比較です。 バンド付近の濃淡がドットで表現されています。