通常の 24 bpp ディスプレイでチャネルあたり 8 ビットを超えるグラフィックスを最も忠実に表示するにはどうすればよいでしょうか?
1 に答える
私が考えることができる最善の解決策は、フレームごとに変化するランダムなディザリングに基づいています。これは、ディザリングの利点と、固定されたディザリング パターンを持たないことを組み合わせたものです。特定のピクセルが 1 秒間に何度も値を変更するため、これらのさまざまな値の平均に近くなり、元の「濃い色」の値に近くなります。任意の 24 bpp 値。
どのように見えるか
緑のグラデーション、ディザなし、ディザあり (10 フレームが表示されています)、両方とも可視性のために同じ方法で強化されています。
ディザリング
ディザリングは、各チャネルのガンマ圧縮されたディープ カラー値をランダムな値と加算してから、最も近い 8 ビット値に丸めることによって実現されます。-0.5 から 0.5 の間の一様分布の乱数を使用するのが自然に思えます (0 と 1 または 254 と 255 の違いのように、8 ビットのガンマ圧縮された値の 1 に相当する単位で話しています)。 、ただし、これにより、8 ビット値に近い勾配の値にはノイズがほとんどなく、8 ビット値から最も離れた値にはより多くのノイズが表示される、一種のバンディング アーティファクトが発生します。ガウス ノイズは、ノイズ レベルがより滑らかになるため、より適しています。私は 1.0 のシグマを選択しましたが、ノイズを減らすには 0.8 のシグマで十分かもしれません。
2 つの乱数n1
とを取りn2
、それぞれを [-1 , 1] の範囲にフィッティングし、それらが単位円内の点を表している場合 (sum
それらの平方和が 1 以下である場合)、ガウス PRNG を作成できます。 、それ以外の場合はやり直します) return sqrt(-2. * log(sum) / sum) * n1
.
実用化
これを実装するために、チャネルあたり 15 ビットのリニア RGB フレームバッファをチャネルあたり 8 ビットの sRGB フレームバッファに変換することにしました。線形から sRGB への部分は単なる詳細です。ルックアップ テーブルを使用して、線形値をガンマ圧縮値に変換します (これらの中間値に 13 ビットを使用するように選択しました。sRGB 値の 8.5 固定小数点表記として表示できます)。 )。
言うまでもなく、ピクセルごとに新しいランダムなガウス数を生成するつもりはありません。それらの束を事前に計算して循環バッファーに入れたいと思うでしょう。私は 16384 個を作成することにしました。はい、16384 個だけです。このバッファー内のランダムなエントリ ポイント、通過するランダムな長さ (100 から 1123 の間、これはかなり恣意的です) を選択することで、パターンの繰り返しを回避します。長さの終わり 新しいランダムな開始点と新しいランダムな長さを選びました。このようにして、比較的小さな数字のバッファからかなりランダムな繰り返しのないパターンを取得します。バッファ内の数値は 2.5 固定小数点形式で格納されます。このようにして、それらはすべて -4.0 から 4.0 の間にあり、必要なガウス乱数の範囲をカバーします。必ず 0 を追加してください。
基本的に、各ピクセルと各チャネルでどのように機能するかを次に示します。
15 ビットの線形値 -- LUT 経由 --> 13 ビット (8.5 固定小数点) ガンマ圧縮値、次に 2.5 固定小数点乱数を追加し、次に 5 ビットを右にシフトします。
-4 から 260 の間の整数値を取得し、if() を使用してそれらを制限できますが、負の数に対して 0 を返す 264 要素の LUT を使用する方がはるかに高速です (割り当てによって負の数をインデックスとして使用できます)。あなたのバッファは buffer = &buffer[4] を実行すると、追加を節約できます)、255 を超える数値に対しては 255 を返します。また、3 つのカラー チャネルのそれぞれに同じ乱数を使用します。これらの 3 つが独立した数値を使用する場合、結果のノイズが多少少なくなるかもしれません。
単一ピクセルの赤チャンネルの場合、私のコードは次のように
sfb[i].r = bytecheck_l.lutb[lsrgb_l.lutint[fb[i].r] + dither_l.lutint[id] >> 5];
なります。sfb は sRGB 24 bpp バッファー、fb は 45 bpp リニア RGB バッファー、lsrgb_l.lutint[] はリニアからガンマ圧縮された LUT、dither_l.lutint[] は LUT です。 2.5 固定小数点形式のランダムなガウス数と、[0 , 255] にクリップされた値を返す bytecheck_l.lutb[] を含みます。
パフォーマンス
2.4 GHz コア 2 クアッド Q6600 の 1 つのコアとデュアル チャネル 800 MHz DDR2 メモリを使用したテスト勾配で、1400x820 SDL ウィンドウで 50 FPS を超える結果が得られました。コンピュータ。
私の説明で明確化が必要な場合はお知らせください。