SharpDX GetPickRay 问题

SharpDX GetPickRay issues

我正在尝试从当前相机 position/view 获取拾取光线,以便我可以将其用于 3d 世界中的 select 对象。问题是当我四处移动相机时,光线变得不正确。我做错了什么还是浮点精度问题?

ViewportF viewport = new ViewportF();
viewport.Height=307.0
viewport.MaxDepth=1.0
viewport.MinDepth=0.0
viewport.Width=689.0
viewport.X=0.0
viewport.Y=0.0

Matrix viewProjection = new Matrix(new float[] {
    1.075709f, 0, 0, 85067.07f, 
    0, 2.414213f, 0, -146011.6f, 
    0, 0, 1, 14.90265f, 
    0, 0, 1, 15.00265});

Ray ray = Ray.GetPickRay(263, 77, viewport, matrix);

射线的结果是:

ray.Position {X:-79080,03 Y:60480,02 Z:-14,90265}
ray.Direction {X:-0,1926253 Y:0,1401343 Z:0,9712146}

在屏幕上,它在我的鼠标下方创建了以下行:
(左上角)

据我了解,我的鼠标下应该看不到射线吧?
显然,如果我移动相机并且不更改正在绘制的光线,它就会变得可见。

编辑:如果我不移动相机,则在我的鼠标下方看不到光线,即使在旋转时也是如此。
edit2:似乎 ray.Position 是正确的,Ray.Direction 似乎是错误的,但不要引用我的话。

最终它只不过是以下代码:

public Ray GetPickRay(float mouseX, float mouseY)
{
    return Ray.GetPickRay((int)mouseX, (int)mouseY, new ViewportF(0, 0, ViewportWidth, ViewportHeight), ViewProjectionMatrix);
}

我想提供我自己对SharpDX的光线投射功能的使用示例。以下是展示此类用法的一些代码:

        private void OnMouseDown(object sender, MouseEventArgs args)
        {
            if (args.Button == MouseButtons.Left)
            {
                // Ray Cast
                List<StaticGeometry> hits = new List<StaticGeometry>();
                Ray rayCast = Ray.GetPickRay(args.X, args.Y,
                    new ViewportF(
                        this.ClientRectangle.X,
                        this.ClientRectangle.Y,
                        this.ClientRectangle.Width,
                        this.ClientRectangle.Height,
                        0,1),
                    Camera.ModelViewProjectionMatrix);

                _rayCastMesh.Dispose();
                _rayCastMesh = new ModelMesh(_device, new Vertex[] {
                    new Vertex() {
                        Location = rayCast.Position },
                    new Vertex() {
                        Location = ( rayCast.Position + rayCast.Direction * Camera.FarZ) } });
                foreach (StaticGeometry geometry in this.StaticGeometry)
                {
                    if (rayCast.Intersects(geometry.BoundingBox.ToBoundingBox()))
                    {
                        hits.Add(geometry);
                    }
                }

                System.Console.WriteLine("Ray Cast Hit Test Results: ");
                System.Console.WriteLine("Count: " + hits.Count);
                foreach (StaticGeometry geometry in hits)
                {
                    System.Console.WriteLine(geometry.Identifier);
                }

            }
            if (args.Button == MouseButtons.Right)
            {
                Cursor.Hide();
                this._mouseLockLocation = new System.Drawing.Point(Cursor.Position.X, Cursor.Position.Y);
                IsMouseLookEnabled = true;
            }
        }

其中我从相机 class 中获取 ModelViewProjection 矩阵,定义如下:

using SharpDX;
using System;
using System.Diagnostics;

namespace VoidwalkerEngine.Framework.DirectX
{
    public enum CameraMode
    {
        FreeLook,
        Orbit
    }

    public class Camera
    {
        /// <summary>
        /// The name of this camera
        /// </summary>
        public string Name { get; set; }
        /// <summary>
        /// The eye location of this camera
        /// </summary>
        public Vector3 Location { get; set; }
        /// <summary>
        /// The Pitch of this Camera, as Radians
        /// </summary>
        public float Pitch { get; set; }
        /// <summary>
        /// The Yaw of this Camera, as Radians
        /// </summary>
        public float Yaw { get; set; }
        /// <summary>
        /// The Roll of this Camera, as Radians
        /// </summary>
        public float Roll { get; set; }
        /// <summary>
        /// The NearZ of this Camera
        /// </summary>
        public float NearZ { get; set; }
        /// <summary>
        /// The FarZ of this Camera
        /// </summary>
        public float FarZ { get; set; }
        /// <summary>
        /// The Field of View of this Camera, value should be
        /// between 0.70 and 1.20
        /// </summary>
        public float FieldOfView { get; set; }
        public float AspectRatio { get; set; }
        public float LookSpeed { get; set; }
        public float MoveSpeed { get; set; }
        /// <summary>
        /// Determines if this Camera is currently accelerating.
        /// </summary>
        public bool IsAccelerating { get; set; }
        /// <summary>
        /// The acceleration speed multiplier of this Camera.
        /// </summary>
        public float AccelerationMultiplier { get; set; }
        public CameraMode Mode { get; set; }
        public float ViewportWidth;
        public float ViewportHeight;
        public float SoftBoundsRadius { get; set; } = 16f;
        public float HardBoundsRadius { get; set; } = 2f;
        public float CollisionDistance { get; set; } = 128f;
        public bool IsCollisionEnabled { get; set; } = true;

        /// <summary>
        /// The BoundingSphere of this Camera
        /// </summary>
        public BoundingSphere SoftBounds
        {
            get
            {
                return new BoundingSphere()
                {
                    Center = this.Location,
                    Radius = SoftBoundsRadius
                };
            }
        }

        /// <summary>
        /// The BoundingSphere of this Camera
        /// </summary>
        public BoundingSphere HardBounds
        {
            get
            {
                return new BoundingSphere()
                {
                    Center = this.Location,
                    Radius = HardBoundsRadius
                };
            }
        }

        /// <summary>
        /// The Target Vector of this Camera
        /// </summary>
        public Vector3 Target
        {
            get
            {
                return new Vector3(
                    (float)Math.Sin(this.Yaw),
                    (float)Math.Tan(this.Pitch),
                    (float)Math.Cos(this.Yaw));
            }
        }

        /// <summary>
        /// The Frustum of this Camera
        /// </summary>
        public BoundingFrustum Frustum
        {
            get
            {
                return new BoundingFrustum(this.ModelViewProjectionMatrix);
            }
        }

        public Matrix ModelViewMatrix
        {
            get
            {
                return Matrix.LookAtLH(this.Location, Location + Target, Up);
            }
        }

        public Matrix ProjectionMatrix
        {
            get
            {
                return Matrix.PerspectiveFovRH(FieldOfView, AspectRatio, NearZ, FarZ);
            }
        }

        public Matrix ModelViewProjectionMatrix
        {
            get
            {
                return ModelViewMatrix * ProjectionMatrix;
            }
        }

        //public CardinalDirectionType Direction
        //{
        //    get
        //    {
        //        return VoidwalkerMath.GetCardinalDirection(VoidwalkerMath.ToDegrees(Yaw));
        //    }
        //}

        public Vector3 Forward
        {
            get
            {
                return new Vector3((float)Math.Cos(Pitch), 0, (float)Math.Sin(Pitch));
            }
        }

        public Vector3 Right
        {
            get
            {
                return new Vector3(Forward.Z, 0, -Forward.Z);
            }
        }

        public Vector3 Up
        {
            get
            {
                return new Vector3(-(float)Math.Sin(Roll), (float)Math.Cos(Roll), 0);
            }
        }

        public Camera()
        {

        }

        public Camera(string name)
            : this()
        {
            this.Name = name;
            this.Location = new Vector3();
        }

        public void ToOrigin()
        {
            Transform(Vector3.Zero, 0, 0, 0);
        }

        public void Transform(Vector3 location, float pitch, float yaw, float roll)
        {
            this.Location = location;
            this.Pitch = pitch;
            this.Yaw = yaw;
            this.Roll = roll;
        }

        public float GetCurrentMoveSpeed()
        {
            if (IsAccelerating)
            {
                return this.MoveSpeed * this.AccelerationMultiplier;
            }
            return this.MoveSpeed;
        }

        public void TranslateLeft(float deltaTime)
        {
            float moveSpeed = GetCurrentMoveSpeed();
            this.Location = new Vector3(
                Location.X - (float)Math.Sin(Yaw + MathUtil.PiOverTwo) * moveSpeed * deltaTime,
                Location.Y,
                Location.Z - (float)Math.Cos(Yaw + MathUtil.PiOverTwo) * moveSpeed * deltaTime);
        }

        public void TranslateRight(float deltaTime)
        {
            float moveSpeed = GetCurrentMoveSpeed();
            this.Location = new Vector3(
                Location.X + (float)Math.Sin(Yaw + MathUtil.PiOverTwo) * moveSpeed * deltaTime,
                Location.Y,
                Location.Z + (float)Math.Cos(Yaw + MathUtil.PiOverTwo) * moveSpeed * deltaTime);
        }

        public void TranslateForward(float deltaTime)
        {
            float degreesX = MathUtil.RadiansToDegrees(Pitch) * 0.01745329F; // X rotation
            float degreesY = MathUtil.RadiansToDegrees(Yaw) * 0.01745329F; // Y rotation
            float moveSpeed = GetCurrentMoveSpeed();
            this.Location = new Vector3(
                this.Location.X - (float)(moveSpeed * Math.Sin(degreesY) * Math.Cos(degreesX)) * deltaTime,
                this.Location.Y - (float)(moveSpeed * Math.Sin(degreesX)) * deltaTime,
                this.Location.Z - (float)(moveSpeed * Math.Cos(degreesY) * Math.Cos(degreesX)) * deltaTime);
        }

        public void TranslateBackward(float deltaTime)
        {

            float degreesX = MathUtil.RadiansToDegrees(Pitch) * 0.01745329F; // X rotation
            float degreesY = MathUtil.RadiansToDegrees(Yaw) * 0.01745329F; // Y rotation
            float moveSpeed = GetCurrentMoveSpeed();
            this.Location = new Vector3(
                this.Location.X + (float)(moveSpeed * Math.Sin(degreesY) * Math.Cos(degreesX)) * deltaTime,
                this.Location.Y + (float)(moveSpeed * Math.Sin(degreesX)) * deltaTime,
                this.Location.Z + (float)(moveSpeed * Math.Cos(degreesY) * Math.Cos(degreesX)) * deltaTime);
        }

        public void TransformYawPitch(float dx, float dy)
        {
            Yaw -= dx * LookSpeed;
            Pitch += dy * LookSpeed;
            const float pitchClamp = 1.56f;
            if (Pitch <= -pitchClamp)
            {
                Pitch = -pitchClamp;
            }
            if (Pitch >= pitchClamp)
            {
                Pitch = pitchClamp;
            }
        }

        public void TranslateUp(float deltaTime)
        {
            this.Location = new Vector3(
                this.Location.X,
                this.Location.Y + GetCurrentMoveSpeed() * deltaTime,
                this.Location.Z);
        }

        public void TranslateDown(float deltaTime)
        {
            this.Location = new Vector3(
                this.Location.X,
                this.Location.Y - GetCurrentMoveSpeed() * deltaTime,
                this.Location.Z);
        }

        public void LookAt(Vector3 location, float pitch, float yaw, float roll)
        {
            this.Location = location;
            this.Pitch = pitch;
            this.Yaw = yaw;
            this.Roll = roll;
        }

        public void SetAspectRatio(int width, int height)
        {
            this.ViewportWidth = width;
            this.ViewportHeight = height;
            this.AspectRatio = width / (float)height;
        }
    }
}

最后,这是一个视频,它提供了上述代码一起执行的操作的可视化示例。 https://www.youtube.com/watch?v=q5KFGdLtceo