2.5D 게임용 카메라


12

나는 5 시간 동안 누군가에게 이것을 설명 할 수 있기를 바라고 있습니다. 왜냐하면 나는 몇 시간 동안이 문제로 어려움을 겪고 있고 내가 잘못하고있는 것을 단순히 이해할 수 없기 때문입니다.

Camera2.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);

그리고 이것은 첫 번째 시도에서 기대했던 결과를 가져옵니다.

여기에 이미지 설명을 입력하십시오

또한 방금 Cameraa 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 2018 년

나는 당신의 게시물을 확인하고 정직하기 위해 당신이 무엇을 묻고 있는지 이해할 수 없지만 (예를 들어 나일지도 모릅니다) 예를 들어 "이 이미지와 같은 세계와 화면 공간을 지원하는 것이 목적입니다"? 그들이 라벨을 역순으로 가져야하는 것처럼 보이는 유닛 테스트를 보았습니까? 카메라 클래스에 월드 매트릭스가있는 이유에 대한 또 다른 메모는 이미 월드에 대한 위치와 회전을 저장하지 않아 뷰 매트릭스를 구성 할 수 있습니까?
concept3d

이 게시물을 통해 카메라 매트릭스를보다 잘 이해할 수 있습니다. 3dgep.com/?p=1700
concept3d

@MooseBoys : 나는 손에 익숙하지만 XNA는 오른 손잡이를 취하기 위해 Z가 화면에서 뷰어쪽으로 나와야한다는 것을 이해합니다. 카메라의 위쪽 방향으로 (0,0,1)을 사용했기 때문에 결과를 뒤집을 필요성을 이해하지 못합니다.
가구 있구만

@ concept3d : 나도 될 수있다;) 나의 주요 질문은 마지막에 굵게 표시되지만, 그것이 당신이 의도 한 것이 아니라는 것을 알고 있습니다. UT의 레이블을 위에서 아래로 뒤집는 것에 대한 귀하의 요점을 이해하지 못합니다. 위에서 아래로, 렌더링은 세계,보기 및 화면입니다. 내가 틀렸다면 정말 혼란 스러워요. 카메라에 월드 매트릭스를 포함시키는 것에 관해서는 동의 Viewport.Project합니다. 월드 매트릭스 가 필요한 사실 외에는 왜 이것이 필요한지 아직 이해하지 못합니다 . 따라서 API에 월드 매트릭스를 추가했습니다. 필요할 경우 제거 할 수도 있습니다.
가구 있구만

답변:


1

다이어그램은 두 가지 방법 중 하나로 해석 될 수 있습니다. Necker 큐브라고하는 착시 현상입니다. 위키피디아 기사입니다. 이 때문에 상단을보고 있다고 생각할 때 실제로 하단을보고있는 것 같습니다.

가능하면 원래 코드에서 카메라 위치의 z- 값을 무시하십시오.


고마워, 그러나 나는 이것이 사실이라고 생각하지 않습니다. 나는 당신의 제안을 시도했고, 내가 기대했던 것을 보았습니다 : 아래에서 내 장면, x 및 y 축에서 잘못 뒤집 혔습니다. 또한 내 질문에서 업데이트 2를 참조하십시오.
가구 있구만

카메라가을 (를 this.viewport.Height)보고 있습니다. this.viewport.Height/2즉, 카메라가 -y 방향을 가리 킵니다. 카메라 위치를로 설정해보십시오 (this.viewport.Width / 2, 0, 100).
shade4159

곧 시도 할 것이지만 내 질문의 첫 번째 그림 에 따라 -y 방향으로 가리 키기를 원합니다 .
가구 있구만

그래, 작동하지 않았다. 원점을 왼쪽 아래에 배치하고 원하는 것은 왼쪽 상단에 (0,0,0)입니다. 내가 게시 한 코드로 재현 했습니까?
가구 있구만

1

이것이 2.5D라는 것을 감안할 때, 내가 이상하게 생각하는 두 가지는 다음과 같습니다.

this.projectionMatrix = Matrix.CreatePerspectiveFieldOfView(0.7853982f, viewport.AspectRatio, 1, 2)
* Matrix.CreateScale(1, -1, 1);
  1. FOV를 다음과 같이 변경하십시오. Math.PiOver4().
  2. 근거리 / 원거리 값에서 원거리 는 2로 설정됩니다. 해당 값을 더 크게 설정하십시오 (1000으로 시작).

0.785는 Pi / 4와 동일하지만 MathHelper.PiOver4코드를 약간 정리하도록 변경했습니다 . 뷰포트 깊이는 명시된 문제와 차이가 없으며, 왜 그런지 이해할 수 없습니다 ...
me--

이 2.5D는 2D처럼 시각적으로 2D처럼 동작하는 3D (평평한 표면의 등각 도면) 또는 2.5D처럼 보이는 2D입니까?
ChocoMan

후자의. 모든 수학은 3D이지만 3D 모델이 아닌 2D 스프라이트를 사용하여 렌더링합니다. 혼란에 대한 사과 ...
me--

0

음수 스케일과 같은 블라인드 변환을 적용하는 것은 문제를 이해하는 것이 좋지 않습니다.

원래 화면 캡처 및 업데이트 1에서 RGB 프레임을 보면 뷰 행렬의 두 축을 부정하면 결정 부호가 변경되지 않기 때문에 오른쪽 좌표계와 일치합니다.

업데이트 2 캡처에서는 투영 행렬의 한 축만 반전시킵니다. 이렇게하면 오른 손잡이에서 왼손잡이 시스템으로 이동하게됩니다. 엄지, 집게 손가락 및 가운데 손가락을 X, Y 및 Z로 사용하십시오.

XNA는 (+ X right, + Y up, -Z forward)와 함께 오른손 좌표를 사용하므로 실제로 표시하는 데 문제가 있음을 의미합니다.

Z 좌표가 위로 결정됩니다 (캡처의 월드 공간 부분에서 볼 수 있음). 즉, 월드 공간 (+ X 오른쪽, + Z 위쪽 및 + Y 앞으로)에서 XNA 영역으로 이동하려면 변환이 필요합니다.

손을 보면 PI/2X 축을 중심으로 회전합니다. 투사하기 전에 삽입해야합니다.

그것이 없으면 두 개의 다른 시스템으로 인해 비행기는 바닥이 아니라 벽입니다.


고맙지 만 "투영 전"이란 무슨 뜻입니까? 나는 시도하지 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);그리고 어느 쪽도했다.
가구 있구만

이 답변을 얻지 못했지만 귀하의 답변이 가장 깊이 들어가 실제 문제를 설명하려고 시도하여 현상금을 수여했습니다.
가구 있구만
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.