18

フレームバッファオブジェクトを介してテクスチャにレンダリングしています。透明なプリミティブを描画すると、そのプリミティブはその単一の描画ステップで描画された他のプリミティブと適切にブレンドされますが、フレームバッファの以前のコンテンツと適切にブレンドされません。

テクスチャのコンテンツを新しいデータと適切にブレンドする方法はありますか?

編集:より多くの情報が必要です、私はより明確に説明しようとします。

私が使用しているブレンドモードはGL_SRC_ALPHA、GL_ONE_MINUS_SRC_ALPHAです。(これは通常、標準のブレンドモードだと思います)

マウスの動きを追跡するアプリケーションを作成しています。前のマウスの位置と現在のマウスの位置を結ぶ線を描画します。フレームごとに線を描画したくないので、テクスチャに描画し、テクスチャをクリアせずに、それを使用して長方形を描画することにしました。それを表示するためのテクスチャ。

これはすべて正常に機能しますが、アルファが1未満のシェイプをテクスチャに描画すると、テクスチャの以前のコンテンツと適切にブレンドされません。テクスチャにalpha=.6の黒い線が描かれているとしましょう。数サイクル後に描画し、次にそれらの線上にalpha=.4の黒い円を描画します。円の「下」の線は完全に上書きされます。円は平らな黒ではありませんが(白い背景と適切に混ざり合っています)、予想どおり、円の下に「暗い線」はありません。

ただし、同じフレームに線と円を描くと、適切にブレンドされます。私の推測では、テクスチャは以前のコンテンツとブレンドされていません。まるでglclearcolorとブレンドしているだけのようです。(この場合、<1.0f、1.0f、1.0f、1.0f>です)

4

3 に答える 3

27

ここには2つの問題があると思います。

ここでは、すべてのオーバーレイラインが2回ブレンドされていることに注意してください。1回はFBOテクスチャにブレンドされたとき、もう1回はFBOテクスチャがシーン全体にブレンドされたときです。

したがって、最初の可能性は、FBOオーバーレイで1つの線を別の線の上に描画するときに、ブレンドが有効になっていないことです。ブレンドオフしてRGBAサーフェスに描画すると、現在のアルファがFBOオーバーレイのアルファチャネルに直接書き込まれます。その後、シーン全体でFBOテクスチャ全体をブレンドすると、そのアルファによってラインが半透明になります。したがって、「世界」に対してブレンドしているが、オーバーレイ要素間ではブレンドしていない場合は、ブレンドが行われていない可能性があります。

別の関連する問題:FBOの「標準」ブレンドモード(src alpha、1-src alpha)であるラインを別のラインにブレンドすると、「ブレンドされた」部分のアルファチャネルに2つのオーバーレイ要素。これはおそらくあなたが望むものではありません。

たとえば、オーバーレイで2本の50%アルファ線を重ねて描画する場合、FBOをブリットしたときに同等の効果を得るには、FBOのアルファを75%にする必要があります。(つまり、1-(1-.5)*(1-0.5)は、シーン上に2本の50%アルファ線を描画した場合に発生します。ただし、2本の50%線を描画すると、 FBOで50%のアルファを取得します(50%と... 50%のブレンド。

これにより、最後の問題が発生します。世界中で線をブレンドする前に線を事前に混合することで、描画順序を変更します。あなたが持っていたかもしれないのに対して:

ブレンド(ブレンド(ブレンド(背景色、モデル)、最初の行)、2番目の行);

今、あなたは持っているでしょう

blend(blend(1行目、2行目)、blend(背景色、モデル))。

つまり、オーバーレイラインをFBOにプレミキシングすると、ブレンディングの順序が変更され、最終的な外観が望ましくない方法で変更されます。

まず、これを回避する簡単な方法:FBOを使用しないでください。これは「アプリを再設計する」という答えだと思いますが、FBOを使用するのは最も安価な方法ではなく、最新のGLカードは線を引くのに非常に優れています。したがって、1つのオプションは次のようになります。ラインをFBOにブレンドする代わりに、ラインジオメトリを頂点バッファオブジェクト(VBO)に書き込みます。毎回VBOを少し拡張するだけです。たとえば、一度に40,000本未満の線を描画する場合、これはほぼ確実に以前と同じ速度になります。

(このルートを使用する場合のヒント:glMapBufferではなくglBufferSubDataを使用して行を書き込みます-マッピングはコストがかかる可能性があり、多くのドライバーのサブ範囲では機能しません...ドライバーにいくつかの新しい頂点をコピーさせる方がよい。)

それがオプションではない場合(たとえば、形状タイプを組み合わせて描画したり、GL状態を組み合わせて使用​​したりして、頂点を蓄積するよりも、行ったことを「記憶」する方がはるかに複雑な場合)、 VBOへの描画方法を変更します。

基本的に、あなたがする必要があるのは、別々のブレンドを有効にすることです。オーバーレイを黒+0%アルファ(0,0,0,0)に初期化し、RGBを「標準ブレンド」してブレンドしますが、アルファチャネルを加法ブレンドします。これはまだアルファチャネルには完全には正しくありませんが、一般的にははるかに近いです-これがないと、過剰に描画された領域は透明になりすぎます。

次に、FBOを描画するときに、「事前に乗算された」アルファ、つまり(one、one-minus-src-alph)を使用します。

その最後のステップが必要な理由は次のとおりです。FBOに描画するとき、すべての描画呼び出しにそのアルファチャネルをすでに乗算しています(ブレンディングがオンの場合)。黒の上に描画しているため、緑(0,1,0,0.5)の線は濃い緑(0,0.5,0,0.5)になります。アルファがオンで、通常どおりに再度ブレンドすると、アルファが再適用され、0,0.25,0,0.5になります。)FBOカラーをそのまま使用するだけで、2回目のアルファ乗算を回避できます。

これは、アルファがすでにRGBカラーに乗算されているため、「事前乗算」アルファと呼ばれることもあります。この場合、正しい結果が得られるようにしたいのですが、それ以外の場合、プログラマーは速度を上げるためにそれを使用します。(事前に乗算することにより、ブレンド操作が実行されたときにピクセルごとのマルチを削除します。)

お役に立てば幸いです。レイヤーが順番に混合されていないときにブレンドを正しく行うことは非常に難しいことであり、古いハードウェアでは個別のブレンドを使用できないため、毎回線を引くだけで最も悲惨な道はないかもしれません。

于 2010-01-31T23:00:35.927 に答える
21

透明な黒(0、0、0、0)でFBOをクリアし、後ろから前に引き込みます。

glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);

とFBOを描画します

glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);

正確な結果を得るには。

Ben Supnikが書いたように、FBOにはすでにアルファチャネルで乗算された色が含まれているため、GL_SRC_ALPHAで再度実行する代わりに、GL_ONEで描画されます。宛先の色は通常、GL_ONE_MINUS_SRC_ALPHAで減衰されます。

この方法でバッファにアルファチャネルをブレンドする理由は異なります。

透明性を組み合わせる式は次のとおりです。

resultTr = sTr * dTr

(OpenGLのソースと宛先に類似しているため、sとdを使用しますが、ご覧のとおり、順序は重要ではありません。)

不透明度(アルファ値)で書かれると、

    1 - rA = (1 - sA) * (1 - dA)
<=> rA = 1 - (1 - sA) * (1 - dA)
       = 1 - 1 + sA + dA - sA * dA
       =         sA + (1 - sA) * dA

これは、デフォルトのブレンド方程式GL_FUNC_ADDを使用したブレンド関数(ソースおよび宛先ファクター)(GL_ONE、GL_ONE_MINUS_SRC_ALPHA)と同じです。


余談として:

上記は質問からの特定の問題に答えますが、描画順序を簡単に選択できる場合は、理論的には、事前に乗算された色を前から後ろにバッファに描画する方が良い場合があります。

    glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_ONE);

それ以外は同じ方法を使用します。

この背後にある私の理由は、グラフィックカードがすでに安定している領域のシェーダーの実行をスキップできる可能性があるということです。私はこれをテストしていませんので、実際には違いがないかもしれません。

于 2013-08-28T20:23:27.410 に答える
5

Ben Supnikが言ったように、これを行う最良の方法は、カラーとアルファの別々のブレンド関数を使用してシーン全体をレンダリングすることです。従来の事前乗算されていないブレンド関数を使用している場合は、glBlendFuncSeparateOES(GL_SRC_ALPHA、GL_ONE_MINUS_SRC_ALPHA、GL_ONE、GL_ONE)を試してシーンをFBOにレンダリングしてください。およびglBlendFuncSeparateOES(GL_ONE、GL_ONE_MINUS_SRC_ALPHA)を使用して、FBOを画面にレンダリングします。

100%正確ではありませんが、ほとんどの場合、予期しない透明性は作成されません。

古いハードウェアと一部のモバイルデバイス(ほとんどの場合、元のiPhoneや3GなどのOpenGL ES 1.xデバイス)は、個別のブレンド機能をサポートしていないことに注意してください。:(

于 2010-11-02T09:15:25.423 に答える