alphakaz’s blog

個人的備忘録

DirectXの_SRGBフォーマットについて

はじめに

DirectX12のテクスチャフォーマットには、_SRGBというサフィックスがついたものがあります。

learn.microsoft.com

名前の通りSRGBガンマがかかった状態でデータを保存するフォーマットです。便利ですが、実際使ってみるといくつか引っかかりやすいポイントがあります。

 

重要なことは以下のページでほとんど書かれているので、このページはちょっとした躓きポイント的な部分を紹介できればと思います。

qiita.com

シェーダー上ではリニアな値

SRGBフォーマットで作られたテクスチャであろうと、シェーダでサンプルした際にはハードウェア側が自動でリニアの値に変換します。そのため、SRGBガンマがかかったRGB値を使いたい場合は、シェーダ上で自分で変換する必要があります。

逆に、*_SRGBのテクスチャをレンダーターゲットにして出力するときは自分でsRGB変換をする必要はありません。リニアの値を渡せばsRGBに自動で変換されます。

アルファブレンドはリニア空間で行われる

sRGB空間で保存されているわけなので、アルファブレンドが必要なポリゴンが描画されたときにも、sRGB空間でアルファブレンドが行われていることを期待するかもしれません。

が、それは間違いです。

*_SRGBフォーマットのテクスチャに半透明のピクセルを書き込むときは、

 

dest = srgbToLinear(RenderTarget.rgb)

src = (PixelShaderOut.rgb)

blended = alphaBlend(dest,src)

return linearToSrgb(blended) => テクスチャに書き込まれる

 

という経過を辿ります。

learn.microsoft.com

公式にも

When you use sRGB render targets, the runtime converts the render target color into linear space before it performs blending. The runtime converts the final blended value back into sRGB space before it saves the value back to the render target.

とあります。

 

これはSRGB空間でレイヤー間のアルファブレンドを行うPhotoshopのデフォルト挙動とは異なりますので、アーティストが作成した見た目と結果が異なるということがしばしば発生します。

 

じゃあどうするかというと、以下の回避策があります。

  • テクスチャフォーマットは*_SRGBのまま、RenderTargetViewのフォーマットを*_UNORMなどリニアなものにし、
  • シェーダでsRGBの値を書き出す

つまり「こいつはリニアなテクスチャだよ」とGPUを騙しつつ、SRGBフォーマットのテクスチャを渡すわけです。そうすると変換処理をスルーしてくれます。書き込み側のsRGBガンマ計算はシェーダで自分でやる必要がありますが。

ハードウェアの変換計算

ハードウェアが適用するsRGBガンマはIEC 61966-2-1 で定義されたsRGBガンマです。単純なcolor^1/2.2ではなく、分岐を含む正式な式です。

en.wikipedia.org

 

また、浮動小数点のピクセルシェーダ出力をUNORMに変換する際には、切り捨てではなくRound、つまり一番近い値に丸められます。