Проблемы с SharpDX GetPickRay
Я пытаюсь получить луч луча из текущей позиции / вида камеры, чтобы использовать его для выбора объектов в трехмерном мире. Проблема в том, что когда я перемещаю камеру, луч становится неправильным. Я делаю что-то не так или это проблема точности с плавающей запятой?
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 кажется неправильным, хотя не цитируйте меня об этом.
1 ответ
Я искал другой способ выбора лучей в Интернете и обнаружил следующее:
http://www.toymaker.info/Games/html/picking.html
Код выглядит менее сложным (по сравнению с источником SharpDX) и отлично работает в моем случае использования.
Как примечание, луч теперь появляется на экране, его начало или конец находится прямо под мышью.
Я хотел предоставить свой собственный пример использования функции Raycasting 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;
}
}
Из которых я получаю матрицу ModelViewProjection из класса Camera, который определен как таковой:
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