Fotocamera per gioco 2.5D


12

Spero che qualcuno possa spiegarmelo come se avessi 5 anni, perché ho lottato con questo per ore e semplicemente non riesco a capire cosa sto facendo di sbagliato.

Ho scritto una Cameralezione per il mio gioco 2.5D. L'intenzione è di supportare spazi del mondo e dello schermo come questo:

inserisci qui la descrizione dell'immagine

La fotocamera è la cosa nera a destra. L'asse + Z è rivolto verso l'alto in quell'immagine, con -Z verso il basso. Come puoi vedere, sia lo spazio mondiale che lo spazio dello schermo hanno (0, 0) in alto a sinistra.

Ho iniziato a scrivere alcuni test unitari per dimostrare che la mia macchina fotografica funzionava come previsto, ed è qui che le cose hanno iniziato a diventare ... strane. I miei test tracciano coordinate nel mondo, nella vista e negli spazi dello schermo. Alla fine userò il confronto delle immagini per affermare che sono corrette, ma per ora il mio test mostra solo il risultato.

La logica di rendering utilizza Camera.ViewMatrixper trasformare lo spazio mondiale per visualizzare lo spazio e Camera.WorldPointToScreenper trasformare lo spazio mondiale in spazio dello schermo.

Ecco un esempio di test:

[Fact]
public void foo()
{
    var camera = new Camera(new Viewport(0, 0, 250, 100));
    DrawingVisual worldRender;
    DrawingVisual viewRender;
    DrawingVisual screenRender;

    this.Render(camera, out worldRender, out viewRender, out screenRender, new Vector3(30, 0, 0), new Vector3(30, 40, 0));
    this.ShowRenders(camera, worldRender, viewRender, screenRender);
}

Ed ecco cosa appare quando eseguo questo test:

inserisci qui la descrizione dell'immagine

Lo spazio mondiale sembra OK, anche se sospetto che l'asse z stia andando sullo schermo anziché verso lo spettatore.

View space mi ha completamente sconcertato. Mi aspettavo che la telecamera fosse seduta sopra (0, 0) e guardasse verso il centro della scena. Invece, l'asse z sembra essere nella direzione sbagliata e la fotocamera è posizionata nell'angolo opposto di quello che mi aspetto!

Sospetto che lo spazio sullo schermo sarà del tutto un'altra cosa, ma qualcuno può spiegare cosa sto facendo di sbagliato nella mia Cameraclasse?


AGGIORNARE

Ho fatto alcuni progressi in termini di aspetto visivo delle cose come mi aspetto, ma solo attraverso l'intuizione: non una reale comprensione di ciò che sto facendo. Qualsiasi illuminazione sarebbe molto apprezzata.

Mi sono reso conto che il mio spazio di visualizzazione era capovolto sia in verticale che in orizzontale rispetto a quello che mi aspettavo, quindi ho cambiato la mia matrice di visualizzazione per ridimensionarla di conseguenza:

this.viewMatrix = Matrix.CreateLookAt(this.location, this.target, this.up) *
    Matrix.CreateScale(this.zoom, this.zoom, 1) *
    Matrix.CreateScale(-1, -1, 1);

Potrei combinare le due CreateScalechiamate, ma le ho separate per chiarezza. Ancora una volta, non ho idea del perché questo sia necessario, ma ha risolto il mio spazio di vista:

inserisci qui la descrizione dell'immagine

Ma ora il mio spazio sullo schermo deve essere capovolto verticalmente, quindi ho modificato la mia matrice di proiezione di conseguenza:

this.projectionMatrix = Matrix.CreatePerspectiveFieldOfView(0.7853982f, viewport.AspectRatio, 1, 2)
    * Matrix.CreateScale(1, -1, 1);

E questo si traduce in quello che mi aspettavo dal mio primo tentativo:

inserisci qui la descrizione dell'immagine

Ho anche appena provato a utilizzare Cameraper rendere gli sprite tramite a SpriteBatchper assicurarmi che tutto funzioni anche lì, e lo fa.

Ma la domanda rimane: perché devo fare tutto questo capovolgimento degli assi per ottenere le coordinate dello spazio come mi aspetto?


AGGIORNAMENTO 2

Da allora ho migliorato la mia logica di rendering nella mia suite di test in modo che supporti le geometrie e che le linee diventino più chiare quanto più sono lontane dalla telecamera. Volevo farlo per evitare illusioni ottiche e per dimostrare ulteriormente a me stesso che sto guardando ciò che penso di essere.

Ecco un esempio:

inserisci qui la descrizione dell'immagine

In questo caso, ho 3 geometrie: un cubo, una sfera e una polilinea sulla faccia superiore del cubo. Notare come l'oscuramento e l'illuminazione delle linee identifica correttamente quelle parti delle geometrie più vicine alla fotocamera.

Se rimuovo il ridimensionamento negativo che ho dovuto inserire, vedo:

inserisci qui la descrizione dell'immagine

Quindi puoi vedere che sono ancora nella stessa barca - ho ancora bisogno di quei capovolgimenti verticali e orizzontali nelle mie matrici per far apparire le cose correttamente.

Nell'interesse di dare alle persone una riproduzione con cui giocare, ecco il codice completo necessario per generare quanto sopra. Se si desidera eseguire tramite il cablaggio di test, è sufficiente installare il pacchetto xunit:

Camera.cs :

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System.Diagnostics;

public sealed class Camera
{
    private readonly Viewport viewport;
    private readonly Matrix projectionMatrix;
    private Matrix? viewMatrix;
    private Vector3 location;
    private Vector3 target;
    private Vector3 up;
    private float zoom;

    public Camera(Viewport viewport)
    {
        this.viewport = viewport;

        // for an explanation of the negative scaling, see: http://gamedev.stackexchange.com/questions/63409/
        this.projectionMatrix = Matrix.CreatePerspectiveFieldOfView(0.7853982f, viewport.AspectRatio, 1, 2)
            * Matrix.CreateScale(1, -1, 1);

        // defaults
        this.location = new Vector3(this.viewport.Width / 2, this.viewport.Height, 100);
        this.target = new Vector3(this.viewport.Width / 2, this.viewport.Height / 2, 0);
        this.up = new Vector3(0, 0, 1);
        this.zoom = 1;
    }

    public Viewport Viewport
    {
        get { return this.viewport; }
    }

    public Vector3 Location
    {
        get { return this.location; }
        set
        {
            this.location = value;
            this.viewMatrix = null;
        }
    }

    public Vector3 Target
    {
        get { return this.target; }
        set
        {
            this.target = value;
            this.viewMatrix = null;
        }
    }

    public Vector3 Up
    {
        get { return this.up; }
        set
        {               
            this.up = value;
            this.viewMatrix = null;
        }
    }

    public float Zoom
    {
        get { return this.zoom; }
        set
        {
            this.zoom = value;
            this.viewMatrix = null;
        }
    }

    public Matrix ProjectionMatrix
    {
        get { return this.projectionMatrix; }
    }

    public Matrix ViewMatrix
    {
        get
        {
            if (this.viewMatrix == null)
            {
                // for an explanation of the negative scaling, see: http://gamedev.stackexchange.com/questions/63409/
                this.viewMatrix = Matrix.CreateLookAt(this.location, this.target, this.up) *
                    Matrix.CreateScale(this.zoom) *
                    Matrix.CreateScale(-1, -1, 1);
            }

            return this.viewMatrix.Value;
        }
    }

    public Vector2 WorldPointToScreen(Vector3 point)
    {
        var result = viewport.Project(point, this.ProjectionMatrix, this.ViewMatrix, Matrix.Identity);
        return new Vector2(result.X, result.Y);
    }

    public void WorldPointsToScreen(Vector3[] points, Vector2[] destination)
    {
        Debug.Assert(points != null);
        Debug.Assert(destination != null);
        Debug.Assert(points.Length == destination.Length);

        for (var i = 0; i < points.Length; ++i)
        {
            destination[i] = this.WorldPointToScreen(points[i]);
        }
    }
}

CameraFixture.cs :

using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using Xunit;
using XNA = Microsoft.Xna.Framework;

public sealed class CameraFixture
{
    [Fact]
    public void foo()
    {
        var camera = new Camera(new Viewport(0, 0, 250, 100));
        DrawingVisual worldRender;
        DrawingVisual viewRender;
        DrawingVisual screenRender;

        this.Render(
            camera,
            out worldRender,
            out viewRender,
            out screenRender,
            new Sphere(30, 15) { WorldMatrix = XNA.Matrix.CreateTranslation(155, 50, 0) },
            new Cube(30) { WorldMatrix = XNA.Matrix.CreateTranslation(75, 60, 15) },
            new PolyLine(new XNA.Vector3(0, 0, 0), new XNA.Vector3(10, 10, 0), new XNA.Vector3(20, 0, 0), new XNA.Vector3(0, 0, 0)) { WorldMatrix = XNA.Matrix.CreateTranslation(65, 55, 30) });

        this.ShowRenders(worldRender, viewRender, screenRender);
    }

    #region Supporting Fields

    private static readonly Pen xAxisPen = new Pen(Brushes.Red, 2);
    private static readonly Pen yAxisPen = new Pen(Brushes.Green, 2);
    private static readonly Pen zAxisPen = new Pen(Brushes.Blue, 2);
    private static readonly Pen viewportPen = new Pen(Brushes.Gray, 1);
    private static readonly Pen nonScreenSpacePen = new Pen(Brushes.Black, 0.5);
    private static readonly Color geometryBaseColor = Colors.Black;

    #endregion

    #region Supporting Methods

    private void Render(Camera camera, out DrawingVisual worldRender, out DrawingVisual viewRender, out DrawingVisual screenRender, params Geometry[] geometries)
    {
        var worldDrawingVisual = new DrawingVisual();
        var viewDrawingVisual = new DrawingVisual();
        var screenDrawingVisual = new DrawingVisual();
        const int axisLength = 15;

        using (var worldDrawingContext = worldDrawingVisual.RenderOpen())
        using (var viewDrawingContext = viewDrawingVisual.RenderOpen())
        using (var screenDrawingContext = screenDrawingVisual.RenderOpen())
        {
            // draw lines around the camera's viewport
            var viewportBounds = camera.Viewport.Bounds;
            var viewportLines = new Tuple<int, int, int, int>[]
            {
                Tuple.Create(viewportBounds.Left, viewportBounds.Bottom, viewportBounds.Left, viewportBounds.Top),
                Tuple.Create(viewportBounds.Left, viewportBounds.Top, viewportBounds.Right, viewportBounds.Top),
                Tuple.Create(viewportBounds.Right, viewportBounds.Top, viewportBounds.Right, viewportBounds.Bottom),
                Tuple.Create(viewportBounds.Right, viewportBounds.Bottom, viewportBounds.Left, viewportBounds.Bottom)
            };

            foreach (var viewportLine in viewportLines)
            {
                var viewStart = XNA.Vector3.Transform(new XNA.Vector3(viewportLine.Item1, viewportLine.Item2, 0), camera.ViewMatrix);
                var viewEnd = XNA.Vector3.Transform(new XNA.Vector3(viewportLine.Item3, viewportLine.Item4, 0), camera.ViewMatrix);
                var screenStart = camera.WorldPointToScreen(new XNA.Vector3(viewportLine.Item1, viewportLine.Item2, 0));
                var screenEnd = camera.WorldPointToScreen(new XNA.Vector3(viewportLine.Item3, viewportLine.Item4, 0));

                worldDrawingContext.DrawLine(viewportPen, new Point(viewportLine.Item1, viewportLine.Item2), new Point(viewportLine.Item3, viewportLine.Item4));
                viewDrawingContext.DrawLine(viewportPen, new Point(viewStart.X, viewStart.Y), new Point(viewEnd.X, viewEnd.Y));
                screenDrawingContext.DrawLine(viewportPen, new Point(screenStart.X, screenStart.Y), new Point(screenEnd.X, screenEnd.Y));
            }

            // draw axes
            var axisLines = new Tuple<int, int, int, int, int, int, Pen>[]
            {
                Tuple.Create(0, 0, 0, axisLength, 0, 0, xAxisPen),
                Tuple.Create(0, 0, 0, 0, axisLength, 0, yAxisPen),
                Tuple.Create(0, 0, 0, 0, 0, axisLength, zAxisPen)
            };

            foreach (var axisLine in axisLines)
            {
                var viewStart = XNA.Vector3.Transform(new XNA.Vector3(axisLine.Item1, axisLine.Item2, axisLine.Item3), camera.ViewMatrix);
                var viewEnd = XNA.Vector3.Transform(new XNA.Vector3(axisLine.Item4, axisLine.Item5, axisLine.Item6), camera.ViewMatrix);
                var screenStart = camera.WorldPointToScreen(new XNA.Vector3(axisLine.Item1, axisLine.Item2, axisLine.Item3));
                var screenEnd = camera.WorldPointToScreen(new XNA.Vector3(axisLine.Item4, axisLine.Item5, axisLine.Item6));

                worldDrawingContext.DrawLine(axisLine.Item7, new Point(axisLine.Item1, axisLine.Item2), new Point(axisLine.Item4, axisLine.Item5));
                viewDrawingContext.DrawLine(axisLine.Item7, new Point(viewStart.X, viewStart.Y), new Point(viewEnd.X, viewEnd.Y));
                screenDrawingContext.DrawLine(axisLine.Item7, new Point(screenStart.X, screenStart.Y), new Point(screenEnd.X, screenEnd.Y));
            }

            // for all points in all geometries to be rendered, find the closest and furthest away from the camera so we can lighten lines that are further away
            var distancesToAllGeometrySections = from geometry in geometries
                                                 let geometryViewMatrix = geometry.WorldMatrix * camera.ViewMatrix
                                                 from section in geometry.Sections
                                                 from point in new XNA.Vector3[] { section.Item1, section.Item2 }
                                                 let viewPoint = XNA.Vector3.Transform(point, geometryViewMatrix)
                                                 select viewPoint.Length();
            var furthestDistance = distancesToAllGeometrySections.Max();
            var closestDistance = distancesToAllGeometrySections.Min();
            var deltaDistance = Math.Max(0.000001f, furthestDistance - closestDistance);

            // draw each geometry
            for (var i = 0; i < geometries.Length; ++i)
            {
                var geometry = geometries[i];

                // there's probably a more correct name for this, but basically this gets the geometry relative to the camera so we can check how far away each point is from the camera
                var geometryViewMatrix = geometry.WorldMatrix * camera.ViewMatrix;

                // we order roughly by those sections furthest from the camera to those closest, so that the closer ones "overwrite" the ones further away
                var orderedSections = from section in geometry.Sections
                                      let startPointRelativeToCamera = XNA.Vector3.Transform(section.Item1, geometryViewMatrix)
                                      let endPointRelativeToCamera = XNA.Vector3.Transform(section.Item2, geometryViewMatrix)
                                      let startPointDistance = startPointRelativeToCamera.Length()
                                      let endPointDistance = endPointRelativeToCamera.Length()
                                      orderby (startPointDistance + endPointDistance) descending
                                      select new { Section = section, DistanceToStart = startPointDistance, DistanceToEnd = endPointDistance };

                foreach (var orderedSection in orderedSections)
                {
                    var start = XNA.Vector3.Transform(orderedSection.Section.Item1, geometry.WorldMatrix);
                    var end = XNA.Vector3.Transform(orderedSection.Section.Item2, geometry.WorldMatrix);
                    var viewStart = XNA.Vector3.Transform(start, camera.ViewMatrix);
                    var viewEnd = XNA.Vector3.Transform(end, camera.ViewMatrix);

                    worldDrawingContext.DrawLine(nonScreenSpacePen, new Point(start.X, start.Y), new Point(end.X, end.Y));
                    viewDrawingContext.DrawLine(nonScreenSpacePen, new Point(viewStart.X, viewStart.Y), new Point(viewEnd.X, viewEnd.Y));

                    // screen rendering is more complicated purely because I wanted geometry to fade the further away it is from the camera
                    // otherwise, it's very hard to tell whether the rendering is actually correct or not
                    var startDistanceRatio = (orderedSection.DistanceToStart - closestDistance) / deltaDistance;
                    var endDistanceRatio = (orderedSection.DistanceToEnd - closestDistance) / deltaDistance;

                    // lerp towards white based on distance from camera, but only to a maximum of 90%
                    var startColor = Lerp(geometryBaseColor, Colors.White, startDistanceRatio * 0.9f);
                    var endColor = Lerp(geometryBaseColor, Colors.White, endDistanceRatio * 0.9f);

                    var screenStart = camera.WorldPointToScreen(start);
                    var screenEnd = camera.WorldPointToScreen(end);

                    var brush = new LinearGradientBrush
                    {
                        StartPoint = new Point(screenStart.X, screenStart.Y),
                        EndPoint = new Point(screenEnd.X, screenEnd.Y),
                        MappingMode = BrushMappingMode.Absolute
                    };
                    brush.GradientStops.Add(new GradientStop(startColor, 0));
                    brush.GradientStops.Add(new GradientStop(endColor, 1));
                    var pen = new Pen(brush, 1);
                    brush.Freeze();
                    pen.Freeze();

                    screenDrawingContext.DrawLine(pen, new Point(screenStart.X, screenStart.Y), new Point(screenEnd.X, screenEnd.Y));
                }
            }
        }

        worldRender = worldDrawingVisual;
        viewRender = viewDrawingVisual;
        screenRender = screenDrawingVisual;
    }

    private static float Lerp(float start, float end, float amount)
    {
        var difference = end - start;
        var adjusted = difference * amount;
        return start + adjusted;
    }

    private static Color Lerp(Color color, Color to, float amount)
    {
        var sr = color.R;
        var sg = color.G;
        var sb = color.B;
        var er = to.R;
        var eg = to.G;
        var eb = to.B;
        var r = (byte)Lerp(sr, er, amount);
        var g = (byte)Lerp(sg, eg, amount);
        var b = (byte)Lerp(sb, eb, amount);

        return Color.FromArgb(255, r, g, b);
    }

    private void ShowRenders(DrawingVisual worldRender, DrawingVisual viewRender, DrawingVisual screenRender)
    {
        var itemsControl = new ItemsControl();
        itemsControl.Items.Add(new HeaderedContentControl { Header = "World", Content = new DrawingVisualHost(worldRender)});
        itemsControl.Items.Add(new HeaderedContentControl { Header = "View", Content = new DrawingVisualHost(viewRender) });
        itemsControl.Items.Add(new HeaderedContentControl { Header = "Screen", Content = new DrawingVisualHost(screenRender) });

        var window = new Window
        {
            Title = "Renders",
            Content = itemsControl,
            ShowInTaskbar = true,
            SizeToContent = SizeToContent.WidthAndHeight
        };

        window.ShowDialog();
    }

    #endregion

    #region Supporting Types

    // stupidly simple 3D geometry class, consisting of a series of sections that will be connected by lines
    private abstract class Geometry
    {
        public abstract IEnumerable<Tuple<XNA.Vector3, XNA.Vector3>> Sections
        {
            get;
        }

        public XNA.Matrix WorldMatrix
        {
            get;
            set;
        }
    }

    private sealed class Line : Geometry
    {
        private readonly XNA.Vector3 magnitude;

        public Line(XNA.Vector3 magnitude)
        {
            this.magnitude = magnitude;
        }

        public override IEnumerable<Tuple<XNA.Vector3, XNA.Vector3>> Sections
        {
            get
            {
                yield return Tuple.Create(XNA.Vector3.Zero, this.magnitude);
            }
        }
    }

    private sealed class PolyLine : Geometry
    {
        private readonly XNA.Vector3[] points;

        public PolyLine(params XNA.Vector3[] points)
        {
            this.points = points;
        }

        public override IEnumerable<Tuple<XNA.Vector3, XNA.Vector3>> Sections
        {
            get
            {
                if (this.points.Length < 2)
                {
                    yield break;
                }

                var end = this.points[0];

                for (var i = 1; i < this.points.Length; ++i)
                {
                    var start = end;
                    end = this.points[i];

                    yield return Tuple.Create(start, end);
                }
            }
        }
    }

    private sealed class Cube : Geometry
    {
        private readonly float size;

        public Cube(float size)
        {
            this.size = size;
        }

        public override IEnumerable<Tuple<XNA.Vector3, XNA.Vector3>> Sections
        {
            get
            {
                var halfSize = this.size / 2;
                var frontBottomLeft = new XNA.Vector3(-halfSize, halfSize, -halfSize);
                var frontBottomRight = new XNA.Vector3(halfSize, halfSize, -halfSize);
                var frontTopLeft = new XNA.Vector3(-halfSize, halfSize, halfSize);
                var frontTopRight = new XNA.Vector3(halfSize, halfSize, halfSize);
                var backBottomLeft = new XNA.Vector3(-halfSize, -halfSize, -halfSize);
                var backBottomRight = new XNA.Vector3(halfSize, -halfSize, -halfSize);
                var backTopLeft = new XNA.Vector3(-halfSize, -halfSize, halfSize);
                var backTopRight = new XNA.Vector3(halfSize, -halfSize, halfSize);

                // front face
                yield return Tuple.Create(frontBottomLeft, frontBottomRight);
                yield return Tuple.Create(frontBottomLeft, frontTopLeft);
                yield return Tuple.Create(frontTopLeft, frontTopRight);
                yield return Tuple.Create(frontTopRight, frontBottomRight);

                // left face
                yield return Tuple.Create(frontTopLeft, backTopLeft);
                yield return Tuple.Create(backTopLeft, backBottomLeft);
                yield return Tuple.Create(backBottomLeft, frontBottomLeft);

                // right face
                yield return Tuple.Create(frontTopRight, backTopRight);
                yield return Tuple.Create(backTopRight, backBottomRight);
                yield return Tuple.Create(backBottomRight, frontBottomRight);

                // back face
                yield return Tuple.Create(backBottomLeft, backBottomRight);
                yield return Tuple.Create(backTopLeft, backTopRight);
            }
        }
    }

    private sealed class Sphere : Geometry
    {
        private readonly float radius;
        private readonly int subsections;

        public Sphere(float radius, int subsections)
        {
            this.radius = radius;
            this.subsections = subsections;
        }

        public override IEnumerable<Tuple<XNA.Vector3, XNA.Vector3>> Sections
        {
            get
            {
                var latitudeLines = this.subsections;
                var longitudeLines = this.subsections;

                // see http://stackoverflow.com/a/4082020/5380
                var results = from latitudeLine in Enumerable.Range(0, latitudeLines)
                              from longitudeLine in Enumerable.Range(0, longitudeLines)
                              let latitudeRatio = latitudeLine / (float)latitudeLines
                              let longitudeRatio = longitudeLine / (float)longitudeLines
                              let nextLatitudeRatio = (latitudeLine + 1) / (float)latitudeLines
                              let nextLongitudeRatio = (longitudeLine + 1) / (float)longitudeLines
                              let z1 = Math.Cos(Math.PI * latitudeRatio)
                              let z2 = Math.Cos(Math.PI * nextLatitudeRatio)
                              let x1 = Math.Sin(Math.PI * latitudeRatio) * Math.Cos(Math.PI * 2 * longitudeRatio)
                              let y1 = Math.Sin(Math.PI * latitudeRatio) * Math.Sin(Math.PI * 2 * longitudeRatio)
                              let x2 = Math.Sin(Math.PI * nextLatitudeRatio) * Math.Cos(Math.PI * 2 * longitudeRatio)
                              let y2 = Math.Sin(Math.PI * nextLatitudeRatio) * Math.Sin(Math.PI * 2 * longitudeRatio)
                              let x3 = Math.Sin(Math.PI * latitudeRatio) * Math.Cos(Math.PI * 2 * nextLongitudeRatio)
                              let y3 = Math.Sin(Math.PI * latitudeRatio) * Math.Sin(Math.PI * 2 * nextLongitudeRatio)
                              let start = new XNA.Vector3((float)x1 * radius, (float)y1 * radius, (float)z1 * radius)
                              let firstEnd = new XNA.Vector3((float)x2 * radius, (float)y2 * radius, (float)z2 * radius)
                              let secondEnd = new XNA.Vector3((float)x3 * radius, (float)y3 * radius, (float)z1 * radius)
                              select new { First = Tuple.Create(start, firstEnd), Second = Tuple.Create(start, secondEnd) };

                foreach (var result in results)
                {
                    yield return result.First;
                    yield return result.Second;
                }
            }
        }
    }

    #endregion
}

3
Conoscete il concetto di maneggevolezza dei sistemi di coordinate? Controlla il link per maggiori informazioni.
MooseBoys,

Stavo controllando il tuo post e, a dire il vero, non riesco a capire cosa stai cercando di chiedere (forse sono io) ma per esempio "L'intenzione è quella di supportare spazi del mondo e dello schermo come questo <immagine>" ?? e guardando i test unitari mi sembrano come se dovessero avere le etichette in ordine inverso? un'altra nota del motivo per cui una classe di telecamere ha una matrice mondiale non stai già memorizzando la posizione e la rotazione rispetto al mondo in modo da poter costruire la matrice della vista?
concept3d

e penso che questo post possa aiutarti a capire meglio la matrice della fotocamera 3dgep.com/?p=1700
concept3d

@MooseBoys: ho familiarità con la mano, ma XNA pretende di essere destrimano, cosa che intendo dire che Z dovrebbe uscire dallo schermo verso lo spettatore. Dato che ho usato (0,0,1) come la mia direzione verso l'alto, non capisco la necessità di fare qualsiasi capovolgimento del risultato.
io--

@ concept3d: potrei essere anch'io;) La mia domanda principale è in grassetto alla fine, ma mi rendo conto che non intendevi. Non so di aver capito il tuo punto di rovesciare le etichette negli UT: dall'alto verso il basso, i rendering sono mondo, vista, quindi schermo. Se ho sbagliato, allora sono orribilmente confuso. Per quanto riguarda l'inclusione di una matrice mondiale nella fotocamera, sono d'accordo: non capisco ancora davvero perché ne abbia bisogno, a parte il fatto che Viewport.Projectrichiede una matrice mondiale. Pertanto, ho aggiunto una matrice mondiale alla mia API. Forse finisco per rimuoverlo se necessario.
io--

Risposte:


1

I tuoi diagrammi possono essere interpretati in due modi. È un'illusione ottica chiamata cubo Necker. Ecco l'articolo di Wikipedia. Per questo motivo, quando pensi di guardare in alto, sospetto che potresti effettivamente vedere il fondo.

Se è possibile, nel codice originale, annullare il valore z della posizione della videocamera.


Grazie, ma sinceramente non penso che sia così. Ho provato il tuo suggerimento e vedo cosa mi aspettavo: la mia scena dal basso, e giravo erroneamente sugli assi xey. Inoltre, consulta l'aggiornamento 2 nella mia domanda.
io--

La fotocamera è posizionata this.viewport.Height, guardando this.viewport.Height/2, il che significa che la fotocamera è puntata nella direzione -y. Prova a impostare la posizione della fotocamera su (this.viewport.Width / 2, 0, 100).
shade4159,

Ci proverò presto, ma come per la prima foto nella mia domanda, voglio che sia puntato nella mia direzione.
io--

Sì, non ha funzionato. Mette l'origine in basso a sinistra, mentre quello che voglio è (0,0,0) in alto a sinistra. Sei riuscito a riprodurre con il codice che ho pubblicato?
io--

1

Dato che questo è 2.5D, qui trovo strano due cose:

this.projectionMatrix = Matrix.CreatePerspectiveFieldOfView(0.7853982f, viewport.AspectRatio, 1, 2)
* Matrix.CreateScale(1, -1, 1);
  1. Prova a cambiare il tuo FOV in Math.PiOver4().
  2. Dai valori Vicino / Lontano, il tuo Lontano è impostato su 2. Prova a impostare quel valore più grande (inizia con 1000).

0.785 è la stessa cosa di Pi / 4, ma l'ho cambiato MathHelper.PiOver4per ripulire un po 'il codice. La profondità della vista non fa alcuna differenza per il problema dichiarato e non riesco a capire perché dovrebbe ...
io--

È 2.5D come in 2D che sembra 3D (disegni isometrici su una superficie piana) o 2.5D come in 3D che si comporta visivamente come 2D?
ChocoMan

l'ultimo. Tutta la matematica è 3D, ma sto eseguendo il rendering utilizzando sprite 2D anziché modelli 3D. Ci scusiamo per qualsiasi confusione ...
io--

0

Applicare la trasformazione cieca come una scala negativa non è una buona idea per capire il problema.

Nella schermata originale e nell'aggiornamento 1, se si osserva la cornice RGB, corrisponderà a un sistema di coordinate destrorso come dovrebbe perché negare due assi della matrice della vista mantenendo invariato il segno determinante.

Durante la cattura dell'aggiornamento 2, si inverte solo un asse della matrice di proiezione, facendo ciò si passa da un sistema per destrorsi a un sistema per mancini. Usa il pollice, l'indice e il medio come X, Y e Z.

Poiché XNA utilizza le coordinate della mano destra con (+ X destra, + Y su, -Z in avanti), significa che c'è davvero un problema in ciò che stai mostrando.

Decidi che la tua coordinata Z è in alto (come si vede nella parte dello spazio mondiale della cattura). Significa che hai bisogno di una trasformazione per spostarti dal nostro spazio mondiale (+ X a destra, + Z su e + Y in avanti) a quello XNA.

Se guardi la tua mano, rivelerà una PI/2rotazione attorno all'asse X. Dovresti inserirlo prima della proiezione.

Senza di essa, a causa dei due diversi sistemi, il tuo aereo non è un pavimento ma un muro.


Grazie, ma cosa intendi con "prima della proiezione"? Ho provato this.ProjectionMatrix = Matrix.CreateRotationX(MathHelper.PiOver2) * Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4, viewport.AspectRatio, 1, 2);e this.ProjectionMatrix = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4, viewport.AspectRatio, 1, 2) * Matrix.CreateRotationX(MathHelper.PiOver2);né lavorato.
io--

Anche se non ho ottenuto la risposta da questo, ti ho assegnato la generosità perché la tua risposta è stata la più profonda e ha tentato di spiegare il problema reale.
io--
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.