Caméra pour jeu 2.5D

12

J'espère que quelqu'un pourra m'expliquer cela comme si j'avais 5 ans, parce que je me bats avec ça depuis des heures et que je ne comprends tout simplement pas ce que je fais mal.

J'ai écrit un Cameracours pour mon jeu 2.5D. L'intention est de prendre en charge des espaces univers et écran comme celui-ci:

entrez la description de l'image ici

La caméra est la chose noire à droite. L'axe + Z est vers le haut dans cette image, avec -Z vers le bas. Comme vous pouvez le voir, l'espace monde et l'espace écran ont (0, 0) en haut à gauche.

J'ai commencé à écrire des tests unitaires pour prouver que ma caméra fonctionnait comme prévu, et c'est là que les choses ont commencé à devenir ... étranges. Mes tests tracent les coordonnées dans les espaces univers, vue et écran. Finalement, je vais utiliser la comparaison d'images pour affirmer qu'elles sont correctes, mais pour l'instant mon test n'affiche que le résultat.

La logique de rendu utilise Camera.ViewMatrixpour transformer l'espace du monde pour visualiser l'espace et Camera.WorldPointToScreenpour transformer l'espace du monde en écran.

Voici un exemple de 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);
}

Et voici ce qui apparaît lorsque je lance ce test:

entrez la description de l'image ici

L'espace mondial semble correct, bien que je soupçonne que l'axe z pénètre dans l'écran plutôt que vers le spectateur.

L'espace de vue m'a complètement déconcerté. Je m'attendais à ce que la caméra soit assise au-dessus (0, 0) et regarde vers le centre de la scène. Au lieu de cela, l'axe z semble être dans le mauvais sens, et la caméra est positionnée dans le coin opposé à ce que j'attends!

Je soupçonne que l'espace à l'écran sera tout autre chose, mais quelqu'un peut-il expliquer ce que je fais mal dans ma Cameraclasse?


MISE À JOUR

J'ai fait quelques progrès en ce qui concerne l'aspect visuel des choses, comme je m'y attendais, mais uniquement par l'intuition: pas une compréhension réelle de ce que je fais. Toute illumination serait grandement appréciée.

Je me suis rendu compte que mon espace de vue était inversé verticalement et horizontalement par rapport à ce que j'attendais, j'ai donc changé ma matrice de vue pour l'adapter en conséquence:

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

Je pourrais combiner les deux CreateScaleappels, mais les ai laissés séparés pour plus de clarté. Encore une fois, je ne sais pas pourquoi cela est nécessaire, mais cela a fixé mon espace de vue:

entrez la description de l'image ici

Mais maintenant, mon espace d'écran doit être inversé verticalement, j'ai donc modifié ma matrice de projection en conséquence:

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

Et cela se traduit par ce que j'attendais de ma première tentative:

entrez la description de l'image ici

J'ai également essayé d'utiliser Camerale rendu des sprites via un SpriteBatchpour m'assurer que tout y fonctionne aussi, et c'est le cas.

Mais la question demeure: pourquoi dois-je faire tout ce retournement d'axes pour obtenir les coordonnées de l'espace comme je l'attends?


MISE À JOUR 2

Depuis, j'ai amélioré ma logique de rendu dans ma suite de tests afin qu'elle prenne en charge les géométries et que les lignes s'éclaircissent à mesure qu'elles sont éloignées de la caméra. Je voulais faire cela pour éviter les illusions d'optique et pour me prouver davantage que je regarde ce que je pense être.

Voici un exemple:

entrez la description de l'image ici

Dans ce cas, j'ai 3 géométries: un cube, une sphère et une polyligne sur la face supérieure du cube. Remarquez comment l'assombrissement et l'éclaircissement des lignes identifient correctement les parties des géométries plus proches de la caméra.

Si je supprime l'échelle négative que je devais mettre, je vois:

entrez la description de l'image ici

Vous pouvez donc voir que je suis toujours dans le même bateau - j'ai toujours besoin de ces flips verticaux et horizontaux dans mes matrices pour que les choses apparaissent correctement.

Dans l'intérêt de donner aux gens une repro pour jouer avec, voici le code complet nécessaire pour générer ce qui précède. Si vous souhaitez exécuter via le faisceau de test, installez simplement le package 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
}
moi--
la source
3
Connaissez-vous le concept de la neutralité des systèmes de coordonnées? Consultez le lien pour plus d'informations.
MooseBoys
Je vérifiais votre message et pour être honnête, je ne comprends pas ce que vous essayez de demander (c'est peut-être moi), mais par exemple "L'intention est de prendre en charge les espaces du monde et de l'écran comme celui-ci <image>" ?? et en regardant les tests unitaires, ils me semblent comme s'ils devraient avoir les étiquettes dans l'ordre inverse? une autre note pourquoi une classe de caméras a une matrice de monde ne stockez-vous pas déjà la position et la rotation par rapport au monde afin que vous puissiez construire la matrice de vue?
concept3d
et je pense que ce poste peut vous aider à mieux comprendre la matrice de la caméra 3dgep.com/?p=1700
concept3d
@ MooseBoys: Je suis familier avec la neutralité, mais XNA prétend être droitier, ce qui, je crois, signifie que Z devrait sortir de l'écran vers le spectateur. Puisque j'ai utilisé (0,0,1) comme direction vers le haut de mon appareil photo, je ne comprends pas la nécessité de faire un retournement du résultat.
moi--
@ concept3d: peut-être moi aussi;) Ma question principale est en gras à la fin, mais je me rends compte que ce n'est pas ce que vous vouliez dire. Je ne sais pas si je comprends votre point sur le retournement des étiquettes dans les UT - de haut en bas, les rendus sont univers, vue, puis écran. Si je me trompe, je suis horriblement confus. Quant à l'inclusion d'une matrice mondiale dans la caméra, je suis d'accord: je ne comprends pas encore vraiment pourquoi j'ai besoin de cela, à part le fait que cela Viewport.Projectnécessite une matrice mondiale. Par conséquent, j'ai ajouté une matrice mondiale à mon API. Il se peut que je finisse par le supprimer si nécessaire.
moi--

Réponses:

1

Vos diagrammes peuvent être interprétés de deux manières. C'est une illusion d'optique appelée cube Necker. Voici l'article wikipedia. Pour cette raison, lorsque vous pensez que vous regardez en haut, je soupçonne que vous voyez en fait le bas.

Si vous le pouvez, dans votre code d'origine, annulez la valeur z de la position de votre caméra.

ombre4159
la source
Merci, mais honnêtement, je ne pense pas que ce soit ça. J'ai essayé votre suggestion, et je vois ce que j'attendais: ma scène d'en bas, et inversée de façon incorrecte sur les axes x et y. Consultez également la mise à jour 2 dans ma question.
moi--
Votre caméra est située en this.viewport.Heightregardant this.viewport.Height/2, ce qui signifie que votre caméra est pointée dans la direction -y. Essayez de définir l'emplacement de votre caméra sur (this.viewport.Width / 2, 0, 100).
shadow4159
J'essaierai bientôt, mais selon la première image de ma question, je veux qu'elle pointe dans la direction -y.
moi--
Ouais, ça n'a pas marché. Il met l'origine en bas à gauche, alors que ce que je veux c'est (0,0,0) en haut à gauche. Avez-vous réussi à reprocher avec le code que j'ai publié?
moi--
1

Étant donné qu'il s'agit de 2.5D, deux choses ici que je trouve étranges sont les suivantes:

this.projectionMatrix = Matrix.CreatePerspectiveFieldOfView(0.7853982f, viewport.AspectRatio, 1, 2)
* Matrix.CreateScale(1, -1, 1);
  1. Essayez de changer votre FOV en Math.PiOver4().
  2. À partir de vos valeurs Near / Far, votre Far est réglé sur 2. Essayez de définir une valeur plus grande (commencez par 1000).
ChocoMan
la source
0.785 est la même chose que Pi / 4, mais je l'ai changé MathHelper.PiOver4pour nettoyer un peu le code. La profondeur de la fenêtre ne fait aucune différence pour le problème déclaré, et je ne comprends pas pourquoi cela ...
moi--
Est-ce 2.5D comme en 2D qui ressemble à 3D (dessins isométriques sur une surface plane) ou 2.5D comme en 3D qui se comporte visuellement comme 2D?
ChocoMan
le dernier. Tout le calcul est en 3D, mais je rend en utilisant des sprites 2D plutôt que des modèles 3D.
Toutes mes
0

Appliquer une transformation aveugle comme une échelle négative n'est pas une bonne idée pour comprendre le problème.

Sur la capture d'écran d'origine et la mise à jour 1, si vous regardez le cadre RVB, il correspondra à un système de coordonnées droitier comme il se doit car la négation de deux axes de la matrice de vue conserve le signe déterminant inchangé.

Lors de la capture de la mise à jour 2, vous inversez un seul axe de la matrice de projection, ce faisant, vous passez d'un système droitier à un système gaucher. Utilisez votre pouce, votre index et votre majeur comme X, Y et Z.

Parce que XNA utilise les coordonnées de droite avec (+ X à droite, + Y vers le haut, -Z vers l'avant), cela signifie qu'il y a vraiment un problème dans ce que vous montrez.

Vous décidez que votre coordonnée Z est le haut (comme on le voit dans la partie espace mondial de la capture). Cela signifie que vous avez besoin d'une transformation pour passer de notre espace mondial (+ X à droite, + Z vers le haut et + Y vers l'avant) à celui de XNA.

Si vous regardez votre main, elle révélera une PI/2rotation autour de l'axe X. Vous devez l'insérer avant la projection.

Sans lui, à cause des deux systèmes différents, votre avion n'est pas un plancher mais un mur.

galop1n
la source
Merci, mais que voulez-vous dire par "avant la projection"? J'ai essayé this.ProjectionMatrix = Matrix.CreateRotationX(MathHelper.PiOver2) * Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4, viewport.AspectRatio, 1, 2);et this.ProjectionMatrix = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4, viewport.AspectRatio, 1, 2) * Matrix.CreateRotationX(MathHelper.PiOver2);ni travaillé.
moi--
Même si je n'ai pas obtenu la réponse de cela, je vous ai accordé la prime parce que votre réponse est allée le plus loin et a tenté d'expliquer le problème réel.
moi--