OpenGLは複数の重なり合うオブジェクトのアウトラインを取得します


10

私はc ++でopenglを使用して作成した進行中のゲームのアイデアを持っています。プレイヤーが何かに勝ったときに、複数の重複するオブジェクトに大きなアウトライン(5〜6ピクセル)が欲しいです。

最良の方法はステンシルバッファーを使用することだと思いましたが、ステンシルバッファーを画面外にレンダリングしようとするのは数時間であり、どんな種類の結果も達成できないので、大したことはありません。他にもいくつかテクニックがあります!

これは私が入手したいものです:

ここに画像の説明を入力してください

何か案は?


エッジ検出フィルターを使用して、太い色の線でエッジを塗りつぶしてから、レンダリングされた形状の画像を抽出し、色付きの線レイヤーの上にオーバーレイしますか?
Shotgun Ninja

エッジ検出フィルターの意味は何ですか?シェーダー?画像処理フィルター?opencv(テクスチャーにレンダリング、テクスチャーにフィルターを適用、変更されたテクスチャーを押し戻す)のように?
nkint

何も思いつきません; そもそも、私は3Dレンダリングに精通していません。
Shotgun Ninja

このようなステンシルバッファーの例はありますか?私は、ステンシルバッファを使用すると、きれいな方法だと思うが、私は任意のステンシルバッファ動作させるのはできませんよ
nkint

回答:


4
  1. ステンシルバッファを有効にしてクリアします。
  2. オブジェクトを描画し、ステンシルバッファーを設定します。オブジェクトは半透明などにすることができます
  3. 次に、ステンシルが設定されていないピクセルのみを書き込むようにステンシルモードを設定します。
  4. そして、それぞれのオブジェクトを、少し拡大して、テクスチャなしで希望の境界線の色で再度描画します。
  5. ステンシルバッファを無効にします。

ここに私が働いているいくつかのwebGLステンシルコードから適応されたコードがあります:

// drawing will set stencil stencil
    gl.enable(gl.STENCIL_TEST);
    gl.stencilFunc(gl.ALWAYS,1,1);
    gl.stencilOp(gl.KEEP,gl.KEEP,gl.REPLACE);
    gl.stencilMask(1);
    gl.clearStencil(0);
    gl.clear(gl.STENCIL_BUFFER_BIT);
// draw objects
for(var object in objects)
  objects[object].draw();
// set stencil mode to only draw those not previous drawn
    gl.stencilFunc(gl.EQUAL,0,1);
    gl.stencilOp(gl.KEEP,gl.KEEP,gl.KEEP);
    gl.stencilMask(0x00);
// draw object halo
for(var object in objects)
  objects[object].drawHalo(1.1,red); // each mesh should be individually scaled e.g. by 1.1
// done
    gl.disable(gl.STENCIL_TEST);

RTSゲームでこのアプローチを使用して、選択したユニットの周りにハローを描画したと思いますが、それはずっと前のことであり、問​​題やニュアンスがあったかどうかは覚えていません。


このようなステンシルバッファーの例はありますか?私は、ステンシルバッファを使用すると、きれいな方法だと思うが、私は任意のステンシルバッファ動作させるのはできませんよ
nkint

7
オブジェクトをわずかに拡大してレンダリングすると、線の太さが均一にならないことに注意してください。遠くのエッジは細くなります。オブジェクトをスケーリングするときにこれを考慮すると、遠くまで伸びる長いオブジェクトの厚さが不均一になります。このエフェクトには、ラインを美しく均一にするためにもう少しあります。
Sean Middleditch 2013

2
オブジェクトを単純にスケールアップするよりも、各頂点をその法線に沿って短い距離だけオフセットする頂点シェーダーを作成する方が優れています。これは滑らかなオブジェクトではかなりうまく機能しますが、ハードエッジにクラックが発生します。どこでもスムージングされた法線の代替セットを使用してメッシュを作成して、どこに到達するかを確認することができます。
Nathan Reed

2

オブジェクトのすべてのグループを見つけることから始めます。オブジェクトのグループは、重複するオブジェクトのコレクションです。標準の衝突検出でうまくいくはずです。各グループに一意の色を割り当てます。どの色でもかまいません。

すべてのオブジェクトを、グループカラーを使用して無地の色としてテクスチャにレンダリングします。

レンダーターゲットと同じサイズの新しいアウトラインテクスチャを作成します。レンダーターゲットの各テクセルをスキャンして、周囲のテクセルと異なる色かどうかを確認します。その場合は、アウトラインテクスチャの対応するテクセルを必要な線の色に変更します。

最後に、このアウトラインテクスチャを取得して、画面に描画したい画像の上にレンダリングします(もちろん、フラグメントシェーダーでのエッジ検出と同時にこれを行うことができ、最初のエッジテクスチャの作成を回避できます)場所)。

forループを使用してレンダーターゲットのテクセルを通過することにより、CPUでこの手順を実行する場合、これはかなり遅くなりますが、場合によってはテストして使用するのに十分です。これをリアルタイムで使用するには、これをシェーダーで処理するのが最善です。

このエッジ検出を行うフラグメントシェーダーは次のようになります。

precision mediump float;

uniform sampler2D s_texture;

varying vec2 v_texCoord;

void main()
{
    gl_FragColor = vec4(0.0);

    vec4 baseColor = texture2D(s_texture, v_texCoord);
    gl_FragColor += baseColor - texture2D(s_texture, top);
    gl_FragColor += baseColor - texture2D(s_texture, topRight);
    gl_FragColor += baseColor - texture2D(s_texture, right);
    gl_FragColor += baseColor - texture2D(s_texture, bottomRight);
    gl_FragColor += baseColor - texture2D(s_texture, bottom);
    gl_FragColor += baseColor - texture2D(s_texture, bottomLeft);
    gl_FragColor += baseColor - texture2D(s_texture, left);
    gl_FragColor += baseColor - texture2D(s_texture, topLeft);
}

texture2Dルックアップの2番目の値は、v_texCoordを基準とした2D座標です。これを適用するには、最初のレンダーターゲットをテクスチャとしてフルスクリーンクワッドにレンダリングします。これは、ガウスぼかしなどの全画面ぼかし効果を適用する方法に似ています。

最初のレンダーターゲットを単色で使用する理由は、異なるオブジェクトの間に重なるエッジが認識されないようにするためです。画面イメージで単純にエッジ検出を実行した場合は、オーバーラップでもエッジを検出していることがわかります(オブジェクトの色/テクスチャ/ライティングが異なる場合)。


2
申し訳ありませんが、「各テクセルをスキャン」とはどういう意味ですか?各ピクセルをループしますか?CPUで?だからそれは次のようなものです:単色でテクスチャにレンダリングし、画像をCPUに転送し、スキャンを行って、もう一度テクスチャに入れますか?またはシェーダーでそれを行いますか?
nkint

後処理のぼかし効果を実行するのと同様の方法で、レンダーターゲットをテクスチャとして使用してフルスクリーンクワッドをレンダリングすることにより、シェーダーでそれを実行することをお勧めします。それが十分に機能する場合。
OriginalDaemon
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.