Ich versuche gerade, einige Sprites zu maskieren. Anstatt es in Worten zu erklären, habe ich ein paar Beispielbilder zusammengestellt:
Der zu maskierende Bereich (in Weiß)
Nun das rote Sprite, das abgeschnitten werden muss.
Das Endergebnis.
Nun ist mir bewusst, dass Sie in XNA zwei Dinge tun können, um dies zu erreichen:
- Verwenden Sie den Stencil Buffer.
- Verwenden Sie einen Pixel Shader.
Ich habe versucht, einen Pixel-Shader zu erstellen, der im Wesentlichen Folgendes ausführt:
float4 main(float2 texCoord : TEXCOORD0) : COLOR0
{
float4 tex = tex2D(BaseTexture, texCoord);
float4 bitMask = tex2D(MaskTexture, texCoord);
if (bitMask.a > 0)
{
return float4(tex.r, tex.g, tex.b, tex.a);
}
else
{
return float4(0, 0, 0, 0);
}
}
Dies scheint die Bilder zu beschneiden (wenn auch nicht korrekt, sobald sich das Bild zu bewegen beginnt), aber mein Problem ist, dass sich die Bilder ständig bewegen (sie sind nicht statisch), so dass dieses Beschneiden dynamisch sein muss.
Kann ich den Shader-Code ändern, um seine Position zu berücksichtigen?
Alternativ habe ich über die Verwendung des Stencil Buffer gelesen, aber die meisten Beispiele scheinen von der Verwendung eines Rendertargets abhängig zu sein, was ich wirklich nicht tun möchte. (Ich benutze bereits 3 oder 4 für den Rest des Spiels und das Hinzufügen eines weiteren erscheint übertrieben)
Das einzige Tutorial, das ich gefunden habe und das Rendertargets nicht verwendet, stammt von Shawn Hargreaves 'Blog hier. Das Problem dabei ist jedoch, dass es für XNA 3.1 ist und sich nicht gut auf XNA 4.0 übertragen lässt.
Mir scheint, der Pixel-Shader ist der richtige Weg, aber ich bin mir nicht sicher, wie ich die richtige Positionierung erreichen soll. Ich glaube, ich müsste meine Bildschirmkoordinaten (etwa 500, 500) ändern, um zwischen 0 und 1 für die Shader-Koordinaten zu liegen. Mein einziges Problem ist, herauszufinden, wie man die transformierten Koordinaten richtig verwendet.
Vielen Dank im Voraus für jede Hilfe!
Antworten:
Sie können Ihren Pixel-Shader-Ansatz verwenden, der jedoch unvollständig ist.
Sie müssen auch einige Parameter hinzufügen, die den Pixel-Shader darüber informieren, wo sich Ihre Schablone befinden soll.
In Ihrem C # -Code übergeben Sie vor dem
SpriteBatch.Draw
Aufruf Variablen wie folgt an den Pixel-Shader :quelle
maskCoord
Berechnung sollte eines der X ein Y gewesen sein. Ich habe meine ursprüngliche Antwort mit dem Fix bearbeitet und die UV-Klemmeinstellungen eingefügt, die Sie für Ihr X benötigenMaskTexture sampler
.Der erste Schritt besteht darin, der Grafikkarte mitzuteilen, dass wir den Schablonenpuffer benötigen. Um dies zu tun, wenn Sie GraphicsDeviceManager erstellen, setzen wir PreferredDepthStencilFormat auf DepthFormat.Depth24Stencil8, sodass tatsächlich eine Schablone zum Schreiben vorhanden ist.
Der AlphaTestEffect wird verwendet, um das Koordinatensystem festzulegen und die Pixel mit Alpha zu filtern, die den Alpha-Test bestehen. Wir werden keine Filter setzen und das Koordinatensystem auf den Ansichtsport setzen.
Als nächstes müssen wir zwei DepthStencilStates einrichten. Diese Zustände bestimmen, wann der SpriteBatch in die Schablone und wann der SpriteBatch in den BackBuffer gerendert wird. Wir interessieren uns hauptsächlich für zwei Variablen StencilFunction und StencilPass.
Für den ersten DepthStencilState setzen wir StencilFunction auf CompareFunction. Dies bewirkt, dass der StencilTest erfolgreich ist und wenn der StencilTest, den SpriteBatch ausführt, dieses Pixel gerendert wird. StencilPass ist auf StencilOperation eingestellt. Ersetzen bedeutet, dass bei erfolgreichem StencilTest dieses Pixel mit dem Wert von ReferenceStencil in den StencilBuffer geschrieben wird.
Zusammenfassend besteht der StencilTest immer, das Bild wird normal auf den Bildschirm gezeichnet und für auf den Bildschirm gezeichnete Pixel wird der Wert 1 im StencilBuffer gespeichert.
Der zweite DepthStencilState ist etwas komplizierter. Dieses Mal möchten wir nur dann auf dem Bildschirm zeichnen, wenn der Wert im StencilBuffer ist. Um dies zu erreichen, setzen wir die StencilFunction auf CompareFunction.LessEqual und die ReferenceStencil auf 1. Dies bedeutet, dass der StencilTest erfolgreich ist, wenn der Wert im Schablonenpuffer 1 ist. StencilPass auf StencilOperation setzen. Behalten bewirkt, dass der StencilBuffer nicht aktualisiert wird. Auf diese Weise können wir mit derselben Maske mehrere Male zeichnen.
Zusammenfassend lässt sich sagen, dass der StencilTest nur erfolgreich ist, wenn der StencilBuffer kleiner als 1 ist (Alpha-Pixel von der Maske), und den StencilBuffer nicht beeinflusst.
Jetzt haben wir unsere DepthStencilStates eingerichtet. Wir können tatsächlich mit einer Maske zeichnen. Zeichnen Sie einfach die Maske mit dem ersten DepthStencilState. Dies wirkt sich sowohl auf den BackBuffer als auch auf den StencilBuffer aus. Nachdem der Schablonenpuffer den Wert 0 hat, bei dem Sie Transparenz maskiert haben, und 1, bei dem es Farbe enthielt, können wir StencilBuffer verwenden, um spätere Bilder zu maskieren.
Der zweite SpriteBatch verwendet die zweiten DepthStencilStates. Unabhängig davon, was Sie zeichnen, bestehen nur die Pixel, bei denen der StencilBuffer auf 1 gesetzt ist, den Schablonentest und werden auf den Bildschirm gezeichnet.
Unten finden Sie den gesamten Code in der Draw-Methode. Vergessen Sie nicht, PreferredDepthStencilFormat = DepthFormat.Depth24Stencil8 im Spielkonstruktor festzulegen.
quelle
In der obigen Antwort passierten einige seltsame Dinge, als ich genau das tat, was erklärt wurde.
Das einzige, was ich brauchte, war die beiden
DepthStencilStates
.Und die Unentschieden ohne die
AlphaTestEffect
.Und alles war gut wie erwartet.
quelle