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
我正在尝试从当前相机 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