OpenGLを学びます。 レッスン5.2-ガンマ補正

OGL3

ガンマ補正



そこで、シーン内のすべてのピクセルの色を計算しました。今度はそれらをモニターに表示します。 デジタル画像処理の夜明けには、ほとんどのモニターにブラウン管(CRT)が搭載されていました。 このタイプのモニターには物理的な機能がありました。入力電圧を2倍に増やしても、輝度が2倍になるわけではありません。 入力電圧と輝度の関係は、モニターガンマとも呼ばれる約2.2の値を持つべき関数で表されました。









内容

パート1.はじめに







  1. Opengl
  2. ウィンドウ作成
  3. こんにちはウィンドウ
  4. こんにちはトライアングル
  5. シェーダー
  6. テクスチャー
  7. 変換
  8. 座標系
  9. カメラ


パート2.基本的な照明







  1. 照明の基本
  2. 素材
  3. テクスチャマップ
  4. 光源
  5. 複数の光源


パート3. 3Dモデルをダウンロードする







  1. Assimpライブラリ
  2. メッシュポリゴンクラス
  3. 3Dモデルクラス


パート4.高度なOpenGL機能







  1. 深度テスト
  2. ステンシルテスト
  3. 色混合
  4. 顔のクリッピング
  5. フレームバッファ
  6. キュービックカード
  7. 高度なデータ処理
  8. 高度なGLSL
  9. 幾何学シェーダー
  10. インスタンス化
  11. スムージング


パート5.高度な照明







  1. 高度な照明。 Blinn-Fongモデル。
  2. ガンマ補正
  3. シャドウカード
  4. 全方向シャドウマップ
  5. 法線マッピング
  6. 視差マッピング
  7. HDR
  8. ブルーム
  9. 遅延レンダリング
  10. SSAO


パート6. PBR







  1. 理論
  2. 分析光源
  3. IBL 拡散照射。
  4. IBL ミラー露光。




モニターのこの機能(偶然による)は、人々が明るさを知覚する方法に非常に似ています:同様の(しかし逆の)電力依存性を持ちます。 これをよりよく理解するには、次の画像をご覧ください。













一番上の行は、人間の目で明るさがどのように知覚されるかを示しています。明るさが2倍(たとえば、0.1から0.2)に増加すると、画像は実際に2倍の明るさのように見えます。 ただし、たとえば、光源から出る光子の数など、光の物理的な明るさについて話すとき、低いスケールは正しい画像を提供します。 その上、値を2倍にすると物理的な観点から正しい明るさが得られますが、私たちの目は暗い色の変化の影響を受けやすいため、これは少し奇妙に思えます。







上のバージョンは人間の目に馴染みがあるため、モニターは色を表示するときにべき乗則依存性を使用します。そのため、物理的な意味での元の輝度値は、上のスケールに示されている非線形の輝度値に変換されます。 これは主に見栄えが良いために行われます。







モニターのこの機能は実際に私たちの目に画像をより良くしますが、グラフィックスのレンダリングに関しては、1つの問題があります。アプリケーションで設定するすべての色と明るさの設定は、モニターの表示に基づいています。 そして、これは、これらすべてのパラメーターが実際に非線形であることを意味します。 チャートを見てください:













灰色の線は、線形空間の色の値に対応しています。 赤い実線は、モニターに表示される色空間を表しています。 線形空間で2倍明るい色を取得したい場合は、その値を取得して2倍にします。 たとえば、色ベクトルを取る  vecL=0.5,0.0,0.0.0 、つまり、濃い赤色。 線形空間で値を2倍にすると、等しくなります 1.0,0.0,0.0 。 一方、表示されると、モニターの色空間に変換されます 0.2180.00.0 グラフからわかるように。 ここで問題が発生します。線形空間で濃い赤色光を2倍にすることで、実際にモニターで4.5倍以上明るくします!







このチュートリアルの前に、線形空間で作業することを想定していましたが、実際にはモニターで定義された色空間で作業したため、設定したすべての色と照明変数は物理的に正しくなく、モニター上で特に正しく見えました。 この仮定に基づいて、私たち(およびアーティスト)は通常、照明値を本来よりも明るく設定します(モニターがそれらを不明瞭にするため)。その結果、線形空間での後続の計算のほとんどが不正確になります。 また、両方のグラフの開始点と終了点は同じポイントであり、ディスプレイでは中間色のみが暗く表示されることに注意してください。







すでに述べたように、色値はモニターに表示される画像に基づいて選択されるため、線形空間で実行されるすべての中間照明計算は物理的に正しくありません。 これは、より高度な照明アルゴリズムを使用し始めると、より明確になります。 画像を見てください:













ご覧のとおり、ガンマ補正を使用して(以前に更新した)色の値を互いに組み合わせると、暗い領域が明るくなり、詳細が向上します。 非常に小さな変更を加えるだけで、はるかに優れた画像品質が得られます。







適切に調整されたモニターの色域がないと、照明は正しく見えず、アーティストが現実的で美しい結果を得るのは非常に困難です。 この問題を解決するには、 ガンマ補正を適用する必要があります。







ガンマ補正



ガンマ補正の背後にある考え方は、モニターに表示する前に、モニターのガンマの逆を最終色に適用することです。 繰り返しますが、このレッスンの最初にあるガンマ曲線のグラフを見て、ストロークで示される別の線に注意してください。これは、モニターのガンマ曲線の逆です。 線形空間に表示される色の値にこの逆ガンマ曲線を掛けて(明るくします)、モニターに表示されるとすぐに、モニターのガンマ曲線が適用され、結果の色が再び線形になります。 実際、中間色をより明るくして、モニターのシェーディングのバランスを取ります。







もう1つの例を示します。 再び濃い赤色になったとしましょう 0.5,0.0,0,0.0 。 この色をモニターに表示する前に、まずガンマ補正曲線をそのコンポーネントに適用します。 線形空間の色の値は、モニターに表示されるときに約2.2の累乗になるため、反転するには値を1/2の累乗に上げる必要があります。 したがって、ガンマ補正を使用した暗赤色は、 0.5,0.0,0,0.01/2.2 = 0.5,0.0,0,0.00.45 = 0.73,0.0,0.0 。 この修正された色はモニターに表示され、その結果、次のように表示されます 0.73,0.0,0.02.2 = 0.5,0.0,0,0.0 。 ご覧のとおり、ガンマ補正を使用すると、アプリケーションの線形空間で設定したとおりにモニターに色が表示されます。







2.2のガンマはデフォルト値で、ほとんどのディスプレイの平均ガンマを大まかに表します。 この色域から生じる色空間は、sRGB色空間と呼ばれます。 各モニターには独自のガンマ曲線がありますが、ほとんどのモニターでは2.2の値で良好な結果が得られます。 これらの小さな違いのため、多くのゲームではプレイヤーがガンマ設定を変更できます。


シーンにガンマ補正を適用するには、2つの方法があります。









最初のオプションはよりシンプルですが、制御が少なくなります。 GL_FRAMEBUFFER_SRGBフラグを設定すると、次のすべての描画コマンドがsRGBカラースペースでガンマ補正を実行してからデータをカラーバッファーに書き込む必要があることをOpenGLに伝えます。 GL_FRAMEBUFFER_SRGBを有効にすると、デフォルトのフレームバッファーを含む後続のすべてのフレームバッファーに対して各フラグメントシェーダーが起動された後 OpenGLは自動的にガンマ補正を実行します。







GL_FRAMEBUFFER_SRGBフラグを有効にするに 、通常のglEnable呼び出しを使用します。







glEnable(GL_FRAMEBUFFER_SRGB);
      
      





これで、レンダリングされたカラーバッファの色域が調整されます。これはハードウェアで行われるため、コストはかかりません。 このアプローチで覚えておくべき唯一のことは(別のアプローチでも)、ガンマ補正は色を線形空間から非線形に変換することです。したがって、最後の最終段階でのみガンマ補正を実行することが非常に重要です。 最終出力の前にガンマ補正を適用すると、これらの色に対する以降のすべての操作は正しくない値で機能します。 たとえば、複数のフレームバッファーを使用する場合、中間結果を線形空間に残し、モニターに送信する前に最後のバッファーのみがガンマ補正を適用する必要があります。







2番目のアプローチではもう少し作業が必要ですが、ガンマ操作を完全に制御できます。 フラグメントシェーダーの対応する段階でガンマ補正を適用するため、モニターに送信する直前に結果の色にガンマ補正が適用されます。







 void main() { //     [...] //  - float gamma = 2.2; FragColor.rgb = pow(fragColor.rgb, vec3(1.0/gamma)); }
      
      





コードの最後の行は、各fragColorカラーコンポーネントを累乗します。 1.0/ 、このシェーダーの結果を調整します。







このアプローチの問題は、最終出力に寄与する各フラグメントシェーダーにガンマ補正を適用する必要があるため、複数のオブジェクトに1ダースのフラグメントシェーダーがある場合、それぞれにガンマ補正コードを追加する必要があることです。それら。 より合理的な解決策は、レンダリングサイクルに後処理ステップを追加し、最後のステップとして最後のクワッドにガンマ補正を適用することです。 その後、これを一度だけ行う必要があります。







実際、これらの2行のコードは、ガンマ補正の技術的な実装を表しています。 あまり印象的ではありませんか? ちょっと待って、ガンマ補正の際に考慮すべき微妙な違いがいくつかあります。







sRGBテクスチャ



コンピューターで画像を描画または編集するときは常に、モニターに表示されるものに基づいて色を選択します。 実際、これは、作成または編集したすべての画像が線形空間ではないことを意味しますが、sRGB空間では、つまり、知覚する明るさに基づいて画面の濃い赤色を2倍にすることは、実際には色の赤色成分を2倍にすることとは異なります。







その結果、テクスチャアーティストはsRGB空間でそれらを作成し、アプリケーションでこれらのテクスチャをそのまま使用する場合、これを考慮する必要があります。 ガンマ補正を適用する前は、テクスチャはsRGB空間で見栄えがよく、ガンマ補正なしではこの空間でも作業を行っていたため、問題は発生しませんでした。 ただし、すべてを線形空間で表示しているため、次の画像に示すように、テクスチャの色が正しく表示されません。













テクスチャが露出オーバーになっているのは、実際にはガンマ補正が2回適用されたためです! 自分で判断する:モニターで見たものに基づいて画像を作成するとき、画像の色の値の範囲を調整して、画面上で正しく見えるようにします。 レンダリング時に再びガンマ補正を行うため、画像が明るすぎます。







この問題を解決するには、テクスチャアーティストが線形空間で動作することを確認する必要があります。 ただし、ほとんどのアーティストはガンマ補正とは何なのかさえ知らず、sRGBスペースで作業する方が簡単なので、これはほとんどの場合オプションではありません。







別の解決策は、色の操作を行う前に、これらのsRGBテクスチャを線形空間に調整または変換して戻すことです。 これは次のように実行できます。







 float gamma = 2.2; vec3 diffuseColor = pow(texture(diffuse, texCoords).rgb, vec3(gamma));
      
      





ただし、sRGB空間のすべてのテクスチャに対してこれを行うのは非常に面倒です。 幸いなことに、OpenGLは内部テクスチャ形式GL_SRGBおよびGL_SRGB_ALPHAを提供することにより、問題に対する別の解決策を提供します。







指定された2つのsRGBテクスチャ形式のいずれかを使用してOpenGLでテクスチャを作成すると、OpenGLは使用するとすぐに自動的に色を線形空間に変換します。 次のように、テクスチャをsRGBとして宣言できます。







 glTexImage2D(GL_TEXTURE_2D, 0, GL_SRGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image);
      
      





テクスチャでアルファコンポーネントを使用する場合は、内部テクスチャ形式をGL_SRGB_ALPHAとして指定する必要があります。







すべてのテクスチャがsRGB空間にあるわけではないため、テクスチャをsRGBとして宣言するときは注意が必要です。 拡散マップなどのオブジェクトの色付けに使用されるテクスチャは、ほとんどの場合sRGB空間にあります。 逆に、 グレアや法線マップなどの照明パラメーターを抽出するために使用されるテクスチャは、ほとんど常に線形空間にあるため、sRGBとして宣言すると、照明が適用されます。 テクスチャタイプを指定するときは注意してください。







拡散テクスチャをsRGBとして宣言すると、期待どおりの結果が得られますが、今回はガンマ補正を1回だけ適用すれば十分です。







減衰



ガンマ補正を使用する場合に異なるもう1つのポイントは、照明の調光です。 実際の物理的な世界では、照明は光源からの距離の2乗とほぼ逆にフェードアウトします。 人間の言語では、これは、以下に示すように、光源からの距離とともに光強度が減少することを意味します。







 float attenuation = 1.0 / (distance * distance);
      
      





ただし、この方程式を使用すると、減衰効果が強すぎて、光スポットの半径が小さくなり、物理的に信頼できるようには見えません。 そのため、減衰に他の方程式を使用し( 照明基本に関するチュートリアルでこれを説明しました)、より多くのチューニングオプション、または線形バージョンを提供します。







 float attenuation = 1.0 / distance;
      
      





ガンマ補正なしでは、線形バージョンは2次のものよりもはるかに妥当な結果を提供しますが、ガンマ補正をオンにすると、線形減衰が弱すぎるように見え、2次を物理的に修正すると予想外により良い結果が得られます。 次の図は、オプションの違いを示しています。













この違いの理由は、光の減衰機能が明るさを変えるためであり、線形空間でシーンを表示しなかったため、物理的には正しくありませんが、モニターで最もよく見える減衰機能を選択しました。 ガンマ補正なしで2次減衰関数を使用すると、実際には、 1.0/22.2 モニターに表示すると、はるかに大きなフェード効果が得られます。 これはまた、線形バージョンがガンマ補正なしでより良い結果を与える理由も説明します。 1.0/2.2 = 1.0/2.2 、これは物理的に正しい関係のようです。







照明の基本で説明したより高度な減衰関数は、減衰のより正確な実装のためのより多くの制御を提供するため、ガンマ補正シーンで依然として有用です(もちろん、ガンマ補正を使用する場合は他のパラメーターが必要です)。


簡単なデモシーンを作成しました 。ソースコードはここにあります 。 スペースバーを押すことで、ガンマ補正のあるシーンとないシーンを切り替えることができます。それぞれのシーンは独自のテクスチャと減衰機能を使用します。 これは最も印象的なデモンストレーションではありませんが、これらの手法を適用する方法を示しています。







要約すると、ガンマ補正を使用すると、線形空間で色を操作できます。 線形空間は物理的な世界に固有のものであるため、ほとんどの物理的な計算では、光の減衰の計算など、より良い結果が得られます。 ガンマ補正を使用すると、適用される照明技術がより複雑になるため、現実的な結果を得ることがはるかに簡単になります。 そのため、ガンマ設定で動作するように照明設定をすぐに調整することをお勧めします。







追加資料







オリジナル記事








All Articles