OpenGL erhält den Umriss mehrerer überlappender Objekte

10

Ich hatte gerade eine Idee für mein laufendes Spiel, das mit opengl in c ++ erstellt wurde: Ich möchte einen großen Umriss (5-6 Pixel) auf mehreren überlappenden Objekten haben, wenn der Spieler etwas gewinnt.

Ich dachte, der beste Weg ist, Schablonenpuffer zu verwenden, aber es sind einige Stunden, in denen ich versuche, den Schablonenpuffer außerhalb des Bildschirms zu rendern, und ich kann kein so wahrscheinliches Ergebnis erzielen. Es gibt einige andere Techniken!

Das möchte ich bekommen:

Geben Sie hier die Bildbeschreibung ein

Irgendwelche Ideen?

nkint
quelle
Verwenden Sie einen Kantenerkennungsfilter, füllen Sie die Kanten mit dicken farbigen Linien aus und extrahieren Sie dann die gerenderten Bilder der Formen und überlagern Sie die Ebene mit den farbigen Linien.
Schrotflinte Ninja
Was meinst du mit einem Kantenerkennungsfilter? ein Shader? ein Bildverarbeitungsfilter? wie opencv (auf Textur rendern, Filter auf die Textur anwenden, geänderte Textur zurückschieben)?
Nkint
Ich habe keine Ahnung; Ich bin mit 3D-Rendering zunächst nicht sehr vertraut.
Schrotflinte Ninja
Haben Sie ein Beispiel für einen solchen Schablonenpuffer? Ich denke, die Verwendung des Schablonenpuffers wäre der sauberere Weg, aber ich bin nicht in der Lage, einen Schablonenpuffer zum
Laufen zu bringen

Antworten:

4
  1. Aktivieren und löschen Sie den Schablonenpuffer.
  2. Zeichnen Sie die Objekte und legen Sie den Schablonenpuffer fest. Objekte können halbtransparent usw. sein.
  3. Stellen Sie nun den Schablonenmodus so ein, dass nur Pixel geschrieben werden, bei denen die Schablone nicht eingestellt ist.
  4. Und zeichnen Sie jedes Objekt erneut, leicht vergrößert, in der gewünschten Rahmenfarbe und ohne Texturen.
  5. Deaktivieren Sie den Schablonenpuffer.

Hier ist der Code, der von einem WebGL-Schablonencode angepasst wurde, an dem ich arbeite:

// 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);

Ich glaube, ich habe diesen Ansatz in RTS-Spielen verwendet, um Halos um ausgewählte Einheiten zu zeichnen, aber es ist lange her und ich kann mich nicht erinnern, ob es Fallstricke und alle Nuancen gibt.

Wille
quelle
Haben Sie ein Beispiel für einen solchen Schablonenpuffer? Ich denke, die Verwendung des Schablonenpuffers wäre der sauberere Weg, aber ich bin nicht in der Lage, einen Schablonenpuffer zum
Laufen zu bringen
7
Beachten Sie, dass das Rendern von Objekten, die leicht vergrößert sind, nicht zu einer einheitlichen Liniendicke führt. Weiter entfernte Kanten werden dünner. Wenn Sie dies beim Skalieren von Objekten berücksichtigen, weisen lange Objekte, die sich in die Ferne erstrecken, eine ungleichmäßige Dicke auf. Es gibt ein bisschen mehr zu diesem Effekt, um die Linien schön und gleichmäßig zu machen.
Sean Middleditch
2
Besser als das einfache Skalieren von Objekten ist es, einen Scheitelpunkt-Shader zu schreiben, der jeden Scheitelpunkt ein kurzes Stück entlang seiner Normalen versetzt. Das funktioniert ziemlich gut für glatte Objekte, aber es erzeugt Risse an harten Kanten. Sie können versuchen, das Netz mit einem alternativen Satz von Normalen zu erstellen, die überall geglättet werden, und sehen, wohin Sie das führt.
Nathan Reed
2

Suchen Sie zunächst alle Objektgruppen, wobei eine Objektgruppe eine Sammlung von Objekten ist, die sich überlappen. Die Standardkollisionserkennung sollte den Job erledigen. Weisen Sie jeder Gruppe eine eindeutige Farbe zu. Jede Farbe würde reichen.

Rendern Sie alle Ihre Objekte als Volltonfarben mithilfe der Gruppenfarbe zu einer Textur.

Erstellen Sie eine neue Gliederungstextur mit denselben Abmessungen wie das Renderziel. Durchsuchen Sie jedes Texel des Renderziels und stellen Sie fest, ob es eine andere Farbe als die umgebenden Texel hat. Wenn dies der Fall ist, ändern Sie das entsprechende Texel in der Gliederungstextur in die gewünschte Linienfarbe.

Nehmen Sie abschließend diese Gliederungstextur und rendern Sie sie über das Bild, das Sie auf dem Bildschirm zeichnen möchten (Sie können dies natürlich gleichzeitig mit der Kantenerkennung in einem Fragment-Shader tun und vermeiden, die Kantenstruktur im ersten zu erstellen Platz).

Wenn Sie diesen Schritt auf der CPU ausführen, indem Sie eine for-Schleife verwenden, um die Texel des Rendering-Ziels zu durchlaufen, ist dies ziemlich langsam, aber wahrscheinlich gut genug, um es zu testen und in einigen Fällen sogar zu verwenden. Um dies in Echtzeit zu verwenden, sollten Sie dies am besten in einem Shader erledigen.

Ein Fragment-Shader für diese Kantenerkennung könnte folgendermaßen aussehen.

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);
}

Wobei der zweite Wert in der textur2D-Suche eine 2D-Koordinate relativ zu v_texCoord ist. Sie würden dies anwenden, indem Sie das erste Renderziel als Textur auf einem Vollbild-Quad rendern. Dies ähnelt dem Anwenden von Unschärfeeffekten im Vollbildmodus, z. B. einer Guassian-Unschärfe.

Der Grund für die Verwendung des ersten Renderziels mit Volltonfarben besteht einfach darin, sicherzustellen, dass zwischen verschiedenen überlappenden Objekten keine Kante wahrgenommen wird. Wenn Sie einfach eine Kantenerkennung auf dem Bildschirmbild durchführen, werden Sie wahrscheinlich feststellen, dass es auch Kanten an den Überlappungen erkennt (vorausgesetzt, die Objekte haben unterschiedliche Farben / Texturen / Beleuchtung).

OriginalDaemon
quelle
2
Entschuldigung, aber was meinst du mit "Durch jedes Texel scannen"? eine for-Schleife durch jedes Pixel? in der CPU? Es ist also so etwas wie: Mit einer Volltonfarbe auf eine Textur rendern, das Bild auf die CPU übertragen, den Scan durchführen, sie erneut in die Textur einfügen? oder in einem Shader?
nkint
Tun Sie dies vorzugsweise in einem Shader, indem Sie ein Vollbild-Quad mit dem Render-Ziel als Textur rendern, ähnlich wie bei einem Post-Process-Unschärfeeffekt. Sie können es jedoch zuerst mit einer for-Schleife auf der CPU zum Laufen bringen, nur um es zu sehen wenn es gut genug funktioniert.
OriginalDaemon