2.5D Oyun için Kamera


12

Birinin bunu bana 5 yaşında gibi açıklayabileceğini umuyorum, çünkü saatlerce bununla mücadele ediyorum ve yanlış yaptığımı anlayamıyorum.

Camera2.5D oyunum için bir sınıf yazdım . Amaç dünya ve ekran alanlarını şu şekilde desteklemektir:

resim açıklamasını buraya girin

Kamera sağdaki siyah şey. Bu görüntüde + Z ekseni yukarı, -Z aşağı doğru gidiyor. Gördüğünüz gibi, hem dünya hem de ekran alanı sol üstte (0, 0) var.

Fotoğraf makinemin beklendiği gibi çalıştığını kanıtlamak için bazı birim testleri yazmaya başladım ve işte bu noktada ... garipleşmeye başladı. Testlerim dünya, görünüm ve ekran alanlarındaki koordinatları çizer. Sonunda doğru olduklarını iddia etmek için görüntü karşılaştırmasını kullanacağım, ancak şimdilik testim sonucu gösteriyor.

Oluşturma mantığı, Camera.ViewMatrixdünya alanını alanı görüntülemek ve Camera.WorldPointToScreendünya alanını ekran alanına dönüştürmek için kullanır.

İşte bir örnek 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);
}

İşte bu testi yaptığımda ortaya çıkan:

resim açıklamasını buraya girin

Dünya ekseni iyi görünüyor, ancak z ekseninin görüntüleyici yerine ekrana gittiğinden şüpheleniyorum.

Görüş alanı beni tamamen şaşırttı. Kameranın yukarıda (0, 0) oturmasını ve sahnenin merkezine bakmasını bekliyordum. Bunun yerine, z ekseni yanlış bir yol gibi görünüyor ve kamera beklediğim şeyin ters köşesinde!

Ekran alanının tamamen başka bir şey olacağından şüpheleniyorum, ancak herkes Camerasınıfımda neyi yanlış yaptığımı açıklayabilir mi?


GÜNCELLEME

Bir şeylerin beklediğim gibi görsel olarak görünmesini sağlama konusunda biraz ilerleme kaydettim, ama sadece sezgiyle: yaptığımın gerçek bir kavrayışı ile değil. Herhangi bir aydınlanma büyük mutluluk duyacaktır.

Görüş alanımın beklediğimle karşılaştırıldığında hem dikey hem de yatay olarak çevrildiğini fark ettim, bu yüzden görünüm matrisimi buna göre ölçeklendirdim:

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

İki CreateScaleçağrıyı birleştirebilirim , ancak netlik için onları ayrı bıraktım. Yine, bunun neden gerekli olduğu hakkında hiçbir fikrim yok, ancak görüş alanımı düzeltti:

resim açıklamasını buraya girin

Ama şimdi ekran alanımın dikey olarak çevrilmesi gerekiyor, bu yüzden projeksiyon matrisimi buna göre değiştirdim:

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

Bu da ilk denememden beklediğim şeyle sonuçlanıyor:

resim açıklamasını buraya girin

Ben de sadece her şeyi orada da çalıştığından emin olmak Cameraiçin sprite bir render kullanarak kullanarak denedim SpriteBatch, ve öyle.

Ama soru hala duruyor: Uzay koordinatlarını beklediğim gibi elde etmek için neden tüm bu eksenleri çevirmem gerekiyor?


GÜNCELLEME 2

O zamandan bu yana test takımımdaki oluşturma mantığımı geliştirdim, böylece geometrileri destekliyor ve çizgiler kameradan uzaklaştıkça daha açık hale geliyor. Optik yanılsamalardan kaçınmak ve kendime düşündüğüm şeye baktığımı daha fazla kanıtlamak için bunu yapmak istedim.

İşte bir örnek:

resim açıklamasını buraya girin

Bu durumda, 3 geometrim var: bir küp, bir küre ve küpün üst yüzünde bir çoklu çizgi. Çizgilerin kararmasının ve açıklığının, geometrilerin kameraya daha yakın olan kısımlarını nasıl doğru tanımladığına dikkat edin.

Eğer koymak zorunda negatif ölçekleme kaldırmak, görüyorum:

resim açıklamasını buraya girin

Hala aynı teknede olduğumu görebiliyorsunuz - işlerin doğru görünmesi için hala matrislerimdeki dikey ve yatay döndürmelere ihtiyacım var.

İnsanlara oynayacakları bir repro vermek için, yukarıdakileri oluşturmak için gereken kodun tamamı burada. Test kayışını kullanarak çalıştırmak istiyorsanız, xunit paketini kurmanız yeterlidir:

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
Koordinat sistemlerinin elverişliliği kavramını biliyor musunuz ? Daha fazla bilgi için bağlantıya göz atın.
MooseBoys

Gönderinizi kontrol ediyordum ve dürüst olmak gerekirse, ne sormaya çalıştığınızı anlayamıyorum (belki de benim) ama örneğin "Niyet, dünya ve ekran alanlarını böyle <görüntü>" desteklemektir. ve ünite testlerine bakarken, etiketleri ters sırayla almaları gerektiği gibi bakıyorlar mı? Bir kamera sınıfının neden bir dünya matrisine sahip olduğunu başka bir not, görünüm matrisini oluşturabilmeniz için zaten dünyaya göre konumu ve döndürmeyi saklamıyorsunuz?
concept3d

ve bu yayının kamera matrisini daha iyi anlamanıza yardımcı olabileceğini düşünüyorum 3dgep.com/?p=1700
concept3d

@MooseBoys: Handedness'e aşinayım, ancak XNA'nın sağ elini kullanmayı düşündüğü, Z'nin ekrandan izleyiciye doğru çıkması gerektiğini anladığımı düşünüyorum. Fotoğraf makinemin yukarı yönü olarak (0,0,1) kullandığımdan, sonucun ters çevrilmesi gerekliliğini anlamıyorum.
ben--

@ concept3d: ben de olabilirim;) Benim asıl sorum sonunda cesur, ama bunun ne demek istediğinin farkında değilim. UT'lerde etiketleri çevirme konusundaki fikrinizi anladığımı bilmiyorum - yukarıdan aşağıya, renderlar dünya, görünüm, sonra ekran. Eğer bunu yanlış anladıysam, o zaman çok kafam karıştı. Kameraya bir dünya matrisi eklemeye gelince, katılıyorum: Viewport.ProjectDünya matrisi gerektiren gerçeğin dışında, neden buna ihtiyacım olduğunu henüz anlamıyorum . Bu nedenle API'ma bir dünya matrisi ekledim. Sonunda gerekirse kaldırıyorum olabilir.
ben--

Yanıtlar:


1

Diyagramlarınız iki yoldan biriyle yorumlanabilir. Necker küpü adı verilen optik bir yanılsamadır. İşte wikipedia makalesi. Bu nedenle, tepeye baktığınızı düşündüğünüzde, aslında dibini görüyor olabileceğinizden şüpheleniyorum.

Yapabiliyorsanız, orijinal kodunuzda, kamera konumunuzun z değerini olumsuzlayın.


Teşekkürler, ama dürüstçe bunun olduğunu düşünmüyorum. Önerinizi denedim ve ne beklediğimi görüyorum: sahnem aşağıdan ve yanlış x ve y eksenlerine çevrildi. Ayrıca, lütfen sorumun 2. güncellemesine bakın.
ben--

Kameranız bulunmaktadır this.viewport.Heightbakarak, this.viewport.Height/2kameranız -y yönüne bakacak anlamına gelir. Kamera konumunuzu olarak ayarlamayı deneyin (this.viewport.Width / 2, 0, 100).
shade4159

Yakında deneyeceğim, ama sorumun ilk resmine göre , -y yönünde işaret etmesini istiyorum .
ben--

Evet, işe yaramadı. Kökeni sol altta koyarken, istediğim (0,0,0) sol üstte. Gönderdiğim kodla kopyalamayı başardın mı?
ben--

1

Bunun 2.5D olduğu göz önüne alındığında, burada garip bulduğum iki şey:

this.projectionMatrix = Matrix.CreatePerspectiveFieldOfView(0.7853982f, viewport.AspectRatio, 1, 2)
* Matrix.CreateScale(1, -1, 1);
  1. FOV'nuzu şu şekilde değiştirmeyi deneyin: Math.PiOver4().
  2. Senin yakınlarında / Uzak değerlerinden, sizin Uzak büyük değeri (1000 ile başlayan) olduğu 2. deneyin ayarına ayarlanır.

0.785 Pi / 4 ile aynı şey, ama MathHelper.PiOver4kodu biraz temizlemek için değiştirdim . Viewport derinliği belirtilen problemde hiçbir fark yaratmıyor ve neden böyle olacağını anlayamıyorum ...
ben--

Bu, 2B'de 3D (düz bir yüzeyde izometrik çizimler) gibi görünen 2.5D veya 3B'de görsel olarak 2D gibi davranan 2.5D mi?
ChocoMan

ikincisi. Tüm matematik 3D, ancak 3D modelleri yerine 2D sprite kullanarak render ediyorum. Herhangi bir karışıklık için özür dilerim ...
ben--

0

Kör dönüşümü negatif ölçek gibi uygulamak sorunu anlamak için iyi bir fikir değildir.

Orijinal ekran yakalamada ve güncellemede 1, RGB çerçevesine bakarsanız, sağ elle kullanılan bir koordinat sistemi ile eşleşir, çünkü görünüm matrisinin iki ekseni olumsuzlamak determinant işaretini değiştirmez.

Güncelleme 2 yakalamasında, projeksiyon matrisinin sadece bir eksenini ters çevirirsiniz, bunu yaparak sağ elini sola doğru sisteme taşırsınız. Baş parmağınızı, işaret parmağınızı ve orta parmağınızı X, Y ve Z olarak kullanın.

XNA (+ X sağ, + Y yukarı, -Z ileri) ile sağ elle koordinatlar kullandığından, gösterdiğiniz şeyde gerçekten bir sorun olduğu anlamına gelir.

Z koordinatınızın yukarı olduğuna karar verdiniz (yakalamanın dünya uzay kısmında görüldüğü gibi). Bu, dünya uzayımızdan (+ X sağ, + Z yukarı ve + Y ileri) XNA'ya geçmek için bir dönüşüme ihtiyacınız olduğu anlamına gelir.

Elinize bakarsanız PI/2, X ekseni etrafında bir dönüş gösterir. Yansıtmadan önce yerleştirmelisiniz.

Onsuz, iki farklı sistemden dolayı, uçağınız bir kat değil, bir duvardır.


Teşekkürler, ama "projeksiyondan önce" ile ne demek istiyorsun? Denedim this.ProjectionMatrix = Matrix.CreateRotationX(MathHelper.PiOver2) * Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4, viewport.AspectRatio, 1, 2);ve this.ProjectionMatrix = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4, viewport.AspectRatio, 1, 2) * Matrix.CreateRotationX(MathHelper.PiOver2);hiçbiri işe yaramadı.
ben--

Bundan cevap alamasam da sana ödül verdim çünkü cevabın en derinine gitti ve asıl sorunu açıklamaya çalıştı.
ben--
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.