Камера для 2.5D Game


12

Я надеюсь, что кто-то может объяснить это мне, как будто мне 5 лет, потому что я боролся с этим часами и просто не мог понять, что я делаю неправильно.

Я написал Cameraкласс для моей 2.5D игры. Намерение состоит в том, чтобы поддерживать мир и экранные пространства следующим образом:

введите описание изображения здесь

Камера - это черная вещь справа. На этом изображении ось + Z направлена ​​вверх, а -Z направлена ​​вниз. Как вы можете видеть, как мировое пространство, так и экранное пространство имеют (0, 0) в верхнем левом углу.

Я начал писать тесты, чтобы доказать, что моя камера работает, как и ожидалось, и вот тут-то и началось ... странно. Мои тесты отображают координаты в пространстве мира, вида и экрана. В конце концов я буду использовать сравнение изображений, чтобы утверждать, что они правильные, но сейчас мой тест просто отображает результат.

Логика рендеринга используется Camera.ViewMatrixдля преобразования мирового пространства для просмотра пространства и Camera.WorldPointToScreenдля преобразования мирового пространства в пространство экрана.

Вот пример теста:

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

И вот что появляется, когда я запускаю этот тест:

введите описание изображения здесь

Пространство мира выглядит нормально, хотя я подозреваю, что ось z направлена ​​на экран, а не в сторону зрителя.

Вид космоса меня совершенно расстроил. Я ожидал, что камера будет сидеть выше (0, 0) и смотреть в центр сцены. Вместо этого, ось z кажется неправильной, и камера расположена в противоположном углу, что я ожидаю!

Я подозреваю, что пространство на экране будет совсем другой вещью, но кто-нибудь может объяснить, что я делаю неправильно в моем Cameraклассе?


ОБНОВИТЬ

Я добился некоторого прогресса в том, чтобы заставить вещи выглядеть визуально, как я ожидаю, но только благодаря интуиции: не фактическому пониманию того, что я делаю. Любое просвещение будет высоко ценится.

Я понял, что мое пространство просмотра было перевернуто как по вертикали, так и по горизонтали по сравнению с тем, что я ожидал, поэтому я изменил свою матрицу вида для масштабирования соответственно:

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

Я мог бы объединить два CreateScaleзвонка, но оставил их отдельно для ясности. Опять же, я понятия не имею, почему это необходимо, но это исправило мое пространство просмотра:

введите описание изображения здесь

Но теперь мое пространство экрана нужно перевернуть вертикально, поэтому я изменил свою матрицу проекции соответственно:

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

И это приводит к тому, что я ожидал от моей первой попытки:

введите описание изображения здесь

Я также только что попытался использовать Cameraдля рендеринга спрайтов через, SpriteBatchчтобы убедиться, что все работает там, и это работает.

Но остается вопрос: зачем мне все это переключать оси, чтобы получить пространственные координаты так, как я ожидаю?


ОБНОВЛЕНИЕ 2

С тех пор я улучшил свою логику рендеринга в своем тестовом наборе, чтобы он поддерживал геометрию и чтобы линии становились светлее по мере удаления от камеры. Я хотел сделать это, чтобы избежать оптических иллюзий и еще раз доказать себе, что я смотрю на то, что я думаю.

Вот пример:

введите описание изображения здесь

В этом случае у меня есть 3 геометрии: куб, сфера и ломаная линия на верхней грани куба. Обратите внимание на то, как затемнение и осветление линий правильно определяет те части геометрии, которые ближе к камере.

Если я уберу отрицательную шкалу, которую я должен был вставить, я увижу:

введите описание изображения здесь

Итак, вы можете видеть, что я все еще в одной лодке - мне все еще нужны эти вертикальные и горизонтальные смещения в моих матрицах, чтобы заставить вещи выглядеть правильно.

В интересах дать людям возможность поиграть, вот полный код, необходимый для генерации вышеупомянутого. Если вы хотите запустить тестовый жгут, просто установите пакет 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
Вы знакомы с концепцией управляемости систем координат? Проверьте ссылку для получения дополнительной информации.
MooseBoys

Я проверял ваш пост и, честно говоря, я не могу понять, что вы пытаетесь спросить (может быть, это я), но, например, "Намерение состоит в том, чтобы поддерживать мир и экранные пространства, подобные этому <image>" ?? и, глядя на модульные тесты, они выглядят так, как будто должны иметь метки в обратном порядке? еще одно замечание, почему класс камеры имеет матрицу мира. Разве вы уже не сохраняете положение и поворот относительно мира, чтобы вы могли построить матрицу вида?
concept3d

и я думаю, что этот пост поможет вам лучше понять матрицу камеры 3dgep.com/?p=1700
concept3d

@MooseBoys: я знаком с руково- дством, но XNA претендует на то, чтобы быть правым, что, как я понимаю, означает, что Z должен выйти из экрана к зрителю. Поскольку я использую (0,0,1) в качестве направления вверх моей камеры, я не понимаю необходимости делать какие-либо изменения результата.
я--

@ concept3d: это может быть и я;) В конце мой главный вопрос выделен жирным шрифтом, но я понимаю, что вы не это имели в виду. Я не знаю, понимаю ли я вашу точку зрения о том, чтобы переключать метки в UT - сверху вниз, рендеры - это мир, вид, затем экран. Если я ошибаюсь, я ужасно растерялся. Что касается включения мировой матрицы в камеру, я согласен: я пока не очень понимаю, зачем мне это нужно, кроме того факта, что Viewport.Projectтребуется мировая матрица. Поэтому я добавил мировую матрицу к своему API. Может быть, я в конечном итоге удаляю его, если это необходимо.
я--

Ответы:


1

Ваши диаграммы можно интерпретировать одним из двух способов. Это оптическая иллюзия, называемая кубом Неккера. Вот статья в Википедии. Из-за этого, когда вы думаете, что смотрите на вершину, я подозреваю, что на самом деле вы видите дно.

Если вы можете, в своем исходном коде отмените значение z положения вашей камеры.


Спасибо, но я, честно говоря, не думаю, что это так. Я попробовал ваше предложение, и я вижу то, что ожидал: моя сцена снизу неправильно перевернулась по осям x и y. Также, пожалуйста, смотрите обновление 2 в моем вопросе.
я--

Ваша камера расположена на this.viewport.Height, глядя на this.viewport.Height/2, что означает, что ваша камера направлена ​​в направлении -y. Попробуйте установить местоположение камеры на (this.viewport.Width / 2, 0, 100).
shade4159

Попробую в ближайшее время, но, согласно первой картинке в моем вопросе, я хочу , чтобы она указывала в -ю сторону.
я--

Да, это не сработало. Это помещает источник внизу слева, тогда как я хочу (0,0,0) вверху слева. Вам удалось воспроизвести код, который я разместил?
я--

1

Учитывая, что это 2.5D, две вещи здесь я нахожу странными:

this.projectionMatrix = Matrix.CreatePerspectiveFieldOfView(0.7853982f, viewport.AspectRatio, 1, 2)
* Matrix.CreateScale(1, -1, 1);
  1. Попробуйте изменить поле зрения на Math.PiOver4().
  2. Исходя из значений ближнего / дальнего, значение Far установлено на 2. Попробуйте установить это значение больше (начните с 1000).

0.785 - это то же самое, что и Pi / 4, но я изменил его, чтобы MathHelper.PiOver4немного очистить код. Глубина области просмотра не имеет значения для заявленной проблемы, и я не могу понять, почему это будет ...
я--

Это 2.5D как в 2D, который выглядит 3D (изометрические рисунки на плоской поверхности) или 2.5D как в 3D, который визуально ведет себя как 2D?
ChocoMan

последний. Вся математика 3D, но я рендеринг использую 2D спрайты, а не 3D модели. Извиняюсь за любую путаницу ...
я--

0

Применение слепого преобразования, такого как отрицательная шкала, не является хорошей идеей для понимания проблемы.

На исходном снимке экрана и обновлении 1, если вы посмотрите на кадр RGB, он будет соответствовать правой системе координат, как и должно быть, потому что отрицание двух осей матрицы представления сохраняет знак определителя неизменным.

В захвате обновления 2 вы инвертируете только одну ось матрицы проекции, выполняя это, вы переходите от правосторонней к левосторонней системе. Используйте большой, указательный и средний пальцы как X, Y и Z.

Поскольку XNA использует правосторонние координаты с помощью (+ X вправо, + Y вверх, -Z вперед), это означает, что действительно есть проблема в том, что вы показываете.

Вы решаете, что ваша Z-координата - это верх (как видно в мировой космической части захвата). Это означает, что вам нужно преобразование для перемещения из нашего мирового пространства (+ X вправо, + Z вверх и + Y вперед) в пространство XNA.

Если вы посмотрите на свою руку, она покажет PI/2вращение вокруг оси X. Вы должны вставить его перед проекцией.

Без этого из-за двух разных систем ваш самолет - не пол, а стена.


Спасибо, но что вы подразумеваете под "до проекции"? Я попробовал this.ProjectionMatrix = Matrix.CreateRotationX(MathHelper.PiOver2) * Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4, viewport.AspectRatio, 1, 2);и this.ProjectionMatrix = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4, viewport.AspectRatio, 1, 2) * Matrix.CreateRotationX(MathHelper.PiOver2);и ни работал.
я--

Несмотря на то, что я не получил ответа от этого, я наградил вас за вознаграждение, потому что ваш ответ был самым глубоким и попытался объяснить реальную проблему.
я--
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.