Wie richte ich die in SpriteBatch verwendete Matrix und die in BasicEffect verwendete Matrix aus?

7

Ich schreibe ein Windows-Tool, das XNA verwendet und es dem Benutzer ermöglicht, Texturen auf dem Bildschirm zu platzieren.

Ich verwende eine 'Kamera' für das SpriteBatch, mit dem die Texturen gezeichnet werden, z.

Matrix matrixForSpriteBatch = Matrix.CreateTranslation(
    -_cameraPosition.X, -_cameraPosition.Y, 0.0f) *
Matrix.CreateRotationZ(_rotation) *
Matrix.CreateScale(_scale) *
Matrix.CreateTranslation(
     GraphicsDevice.Viewport.Width / 2, 
     GraphicsDevice.Viewport.Height / 2, 0.0f);

Dies funktioniert einwandfrei - der Benutzer kann eine Textur auf die Leinwand legen und sie verschieben, drehen und in der Größe anpassen. Sie können die Kamera auch bewegen, um mehr Dinge zu sehen.

Was ich jetzt tun möchte, ist, die Benutzer herumklicken zu lassen, um Punkte eines Polygons zu definieren. Diese Punkte werden dann an meine DrawPolygonFilledMethode gesendet :

public void DrawPolygonFilled( SpriteBatch spriteBatch, 
        Vector2[ ] worldPoints, 
        Color color )
{
    Matrix matrixForVertices =
        Matrix.CreateTranslation( _cameraPosition.X, -_cameraPosition.Y, 0.0f ) *
            Matrix.CreateRotationZ( -rotation ) *
                Matrix.CreateScale( camera.Scale ) ;


    Vector2[] transformedPoints = worldPoints.Transform( matrixForVertices ) ;

    var vertices = new VertexPositionColor[ transformedPoints.Length ] ;

    for (int i = 0; i < transformedPoints.Length; i++)
    {
        vertices[ i ] = new VertexPositionColor( new Vector3( transformedPoints[ i ], 0 ), color ) ;
    }

    Vector2[ ] outputVertices ;
    short[ ] outputIndicies ;
    Vertices.Triangulate( transformedPoints, Vertices.WindingOrder.Clockwise, out outputVertices, out outputIndicies ) ;

    if (outputIndicies.Length > 0)
    {
        foreach( EffectPass pass in _basicEffect.CurrentTechnique.Passes )
        {
            pass.Apply( ) ;

            spriteBatch.GraphicsDevice.DrawUserIndexedPrimitives<VertexPositionColor>(
                Microsoft.Xna.Framework.Graphics.PrimitiveType.TriangleList,
                vertices,
                0,
                vertices.Length,
                outputIndicies,
                0,
                outputIndicies.Length /3 ) ;
        }
    }
}

Dies zeigt nichts an, da der Client-Speicherplatz BasicEffectoben links 0,0 und unten rechts 1-1 beträgt. Also habe ich folgendes hinzugefügt zu BasicEffect:

_basicEffect = new BasicEffect( GraphicsDevice )
    {
        VertexColorEnabled = true
    } ;

_viewMatrix = Matrix.CreateLookAt(
    new Vector3( 0, 0, GraphicsDevice.Viewport.Width ),
    Vector3.Zero,
    Vector3.Up
    ) ;

_projectionMatrix = Matrix.CreateOrthographic(
    GraphicsDevice.Viewport.Width,
    GraphicsDevice.Viewport.Height,
    .1f,
    GraphicsDevice.Viewport.Width ) ;

_basicEffect.Projection = _projectionMatrix ;
_basicEffect.View = _viewMatrix ;

Damit wird das Polygon angezeigt, aber es steht auf dem Kopf. Wenn sich die Position des Polygons ändert (der Benutzer kann das Polygon mit der Maus ziehen), wird das Polygon nach oben verschoben, wenn der Benutzer die Maus nach unten bewegt!

Ich bin ein bisschen dick (aber ich versuche weniger dick zu werden), wenn es um Matrizen und Geometrie geht. Vermisse ich etwas Ich habe versucht, Downzu Upin zu wechseln , CreateLookAtaber es wurde immer noch nicht richtig gezeichnet. Wenn der Benutzer die Maus verlässt, geht das Polygon nach rechts!

Hier ist ein Screenshot beim Zeichnen eines Polygons mit 0,0: Geben Sie hier die Bildbeschreibung ein

Die Punkte auf dem weißen Pfad sind die Stellen, an denen der Benutzer geklickt hat (und die mit SpriteBatch gezeichnet wurden), und das gelbe Polygon ist das, was mit meiner obigen Methode gezeichnet wurde.

Hier ist ein weiterer Screenshot, der zeigt, was passiert, wenn ich das Polygon auf 20,20 verschiebe: Geben Sie hier die Bildbeschreibung ein

Der weiße Pfad wird ab 20,20 gezeichnet, das gefüllte Polygon beginnt jedoch zwischen 20, -20 und wird verkehrt herum gezeichnet

Ich denke, dies ist ein Matrixproblem zwischen der Matrix, die von SpriteBatch(zum Zeichnen der weißen Pfade) und der Matrix BasicEffect(zum Zeichnen des gefüllten Polygons) verwendet wird.

Steve Dunn
quelle

Antworten:

5

Die Antwort von r2d2rigo ist fast richtig. Hier ist die richtige Code - Matrix ein einzurichten , BasicEffectdass Streichhölzer SpriteBatch( von Shawn Hargreaves' Blog ):

Matrix projection = Matrix.CreateOrthographicOffCenter(0, viewport.Width, viewport.Height, 0, 0, 1);
Matrix halfPixelOffset = Matrix.CreateTranslation(-0.5f, -0.5f, 0);

basicEffect.World = Matrix.Identity;
basicEffect.View = Matrix.Identity;
basicEffect.Projection = halfPixelOffset * projection;

Beachten Sie den halben Pixelversatz ( Erklärung , MSDN ).

Andrew Russell
quelle
Diese Multiplikation "halfPixelOffset * -Projektion" erzeugt falsche Ergebnisse, wenn das Ziel darin besteht, den Ursprung der Welt in der Koordinate des oberen linken Bildschirms zu haben.
Emir Lima
1
@EmirLima Ja, aber normalerweise ist das so nicht das Ziel. In den meisten Fällen - einschließlich des OP - besteht das Ziel darin, die Bildschirmpixel auf die gleiche Weise auszurichten wie dies der Fall istSpriteBatch. Weitere Informationen finden Sie unter den Links in meiner Antwort (und beachten Sie natürlich, dass dies für XNA und DirectX 9 gilt, nicht jedoch für DirectX 10 und OpenGL).
Andrew Russell
3

Die in SpriteBatch verwendete Matrix wird mit Matrix.CreateOrthographicOffCenter erstellt:

Matrix ortho = Matrix.CreateOrthographicOffCenter(0.0f, yourWidth, yourHeight, 0.0f, 0.0f, 1.0f);

Auf diese Weise behalten Sie Ihren Weltursprung in der oberen linken Ecke.

r2d2rigo
quelle