   How can I improve my picking precision?
  So I've decided to mess around with SlimDX, which is a great C# library for utilizing DirectX in a managed environment.  I'm using the release complimenting the DirectX SDK (March 2009) release.  I had the most difficult time getting picking to work because my card world matrix was not invertible.  I solved that and got picking working, however it's not as precise as I like.  If I move the camera out further, then my range of selecting a card becomes more narrow.  I created a bounding box in my Card class and everything seems correct to me.  Could anyone please give me some pointers as to how I could improve the precision of my pointing?  Thanks!CSSlimDX.CSusing System;using System.Drawing;using System.Globalization;using System.Windows.Forms; using SampleFramework;using SlimDX;using SlimDX.Direct3D9; namespace CSSlimDX{    class CSSlimDX : Game    {   const int InitialWidth = 1024;   const int InitialHeight = 768; Card [] cards;   public Card selectedCard = null;   Camera camera = new Camera();   Light light = new Light();   TextConsole console = new TextConsole(); public Device Device   {  get { return GraphicsDeviceManager.Direct3D9.Device; }   }    public Color ClearColor   {  get;  set;   }    public CSSlimDX()   {  ClearColor = Color.FromArgb(0, 45, 50, 170);   Window.ClientSize = new Size(InitialWidth, InitialHeight);  Window.Text = "CSSlimDX - Test";  Window.KeyDown += new KeyEventHandler(Window_KeyDown);  Window.MouseClick += new MouseEventHandler(Window_MouseClick);    DeviceSettings settings = new DeviceSettings();  settings.BackBufferWidth = InitialWidth;  settings.BackBufferHeight = InitialHeight;  settings.DeviceVersion = DeviceVersion.Direct3D9;  settings.Windowed = true;  settings.EnableVSync = true;  settings.MultisampleType = MultisampleType.EightSamples;   camera.FieldOfView = (float)(Math.PI / 4);  camera.NearPlane = 1.0f;  camera.FarPlane = 1000.0f;  camera.Location = new Vector3(0.0f, 0.0f, -200.0f);  camera.Target = Vector3.Zero;   Resources.Add(console);   //GraphicsDeviceManager.ChangeDevice(DeviceVersion.Direct3D9, true, InitialWidth, InitialHeight);  GraphicsDeviceManager.ChangeDevice(settings);   Device.SetRenderState(RenderState.ZEnable, true);light.Type = LightType.Directional;  light.Direction = new Vector3(0.0f, 0.0f, 1.0f);  light.Diffuse = Color.FromArgb(200, 200, 200);  light.Ambient = Color.FromArgb(200, 200, 200);   Material material = new Material();  material.Diffuse = Color.White;  material.Ambient = Color.White;  Device.Material = material;    Device.SetRenderState(RenderState.Lighting, true);  Device.SetLight(0, light);  Device.EnableLight(0, true);}    void Window_MouseClick(object sender, MouseEventArgs e)   {  if (selectedCard != null)  { selectedCard.locked = !selectedCard.locked;  }   }    protected override void Initialize()   {  base.Initialize();    cards = new Card[13];  cards[0] = new Card(Device, CardList.BLUE1);  cards[0].camera = camera;  cards[1] = new Card(Device, CardList.BLUE2);  cards[1].camera = camera;  cards[2] = new Card(Device, CardList.BLUE3);  cards[2].camera = camera;  cards[3] = new Card(Device, CardList.BLUE4);  cards[3].camera = camera;  cards[4] = new Card(Device, CardList.BLUE5);  cards[4].camera = camera;  cards[5] = new Card(Device, CardList.BLUE6);  cards[5].camera = camera;  cards[6] = new Card(Device, CardList.BLUE7);  cards[6].camera = camera;  cards[7] = new Card(Device, CardList.BLUE8);  cards[7].camera = camera;  cards[8] = new Card(Device, CardList.BLUE9);  cards[8].camera = camera;  cards[9] = new Card(Device, CardList.BLUE10);  cards[9].camera = camera;  cards[10] = new Card(Device, CardList.BLUE11);  cards[10].camera = camera;  cards[11] = new Card(Device, CardList.BLUE12);  cards[11].camera = camera;  cards[12] = new Card(Device, CardList.BLUEWILD);  cards[12].camera = camera;}    protected override void LoadContent()   {  base.LoadContent();   camera.AspectRatio = (float)Window.ClientSize.Width / Window.ClientSize.Height;    float positionX = (float)cards.Length / 2 * -5;  //int positionX = 0;  float positionZ = 3;  foreach (Card card in cards)  { card.OnResetDevice(); card.position.X = positionX; card.position.Y -= 50; card.selectedPosition = card.position; //card.position.Z = positionZ; card.selected = false; Components.Add(card); positionX += 5; positionZ += 1;  }   }    protected override void UnloadContent()   {  base.UnloadContent();   foreach (Card card in cards)  { card.OnLostDevice();  }   }    protected override void Dispose(bool disposing)   {  base.Dispose(disposing);    foreach (Card card in cards)  { card.Dispose();  }    }    void Window_KeyDown(object sender, KeyEventArgs e)   {  // F1 toggles between full screen and windowed mode  // Escape quits the application  if (e.KeyCode == Keys.F1) GraphicsDeviceManager.ToggleFullScreen();  else if (e.KeyCode == Keys.Escape) Exit();   }protected void CheckMousePicking()   {  foreach (Card card in cards)  { card.selected = false;  }Point cursorPos = Window.PointToClient(Cursor.Position);  int mouseX = cursorPos.X;  int mouseY = cursorPos.Y;    // Clamp mouse coordinates to viewport   if (mouseX < 0) mouseX = 0;  if (mouseY < 0) mouseY = 0;   if (mouseX > this.Device.Viewport.Width)  mouseX = this.Device.Viewport.Width;  if (mouseY > this.Device.Viewport.Height)  mouseY = this.Device.Viewport.Height;Vector3 near = new Vector3(mouseX, mouseY, 0f);  Vector3 far = new Vector3(mouseX, mouseY, 1f);   Vector3 position;  Vector3 direction;   Ray transformedRay;  bool found = false;  for (int i = cards.Length - 1; i >= 0 && !found; i--)  { // Transform points to world space  Matrix foo = Device.GetTransform(TransformState.Projection); Matrix temp = cards.worldMatrix;  position = Vector3.Unproject(near, this.Device.Viewport, camera.ProjectionMatrix, camera.ViewMatrix, cards.worldMatrix); direction = Vector3.Unproject(far, this.Device.Viewport, camera.ProjectionMatrix, camera.ViewMatrix, cards.worldMatrix);transformedRay = new Ray(position, direction);  float distance; if (Ray.Intersects(transformedRay, cards.box, out distance)) {cards.selected = true;selectedCard = cards;found = true; } else {cards.selected = false;selectedCard = null; }  }   }  protected override void Update(GameTime gameTime)   {  base.Update(gameTime);   CheckMousePicking();}    protected override void Draw(GameTime gameTime)   {  Device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, ClearColor, 1.0f, 0);  Device.BeginScene();   Device.SetTransform(TransformState.View, camera.ViewMatrix);  Device.SetTransform(TransformState.Projection, camera.ProjectionMatrix);   base.Draw(gameTime);   console.Begin();  console.Location = new Point(5, 5);  console.ForegroundColor = new Color4(1.0f, 1.0f, 1.0f, 0.0f);  console.WriteLine(GraphicsDeviceManager.DeviceInformation);  console.WriteLine(GraphicsDeviceManager.DeviceStatistics);  console.WriteLine(gameTime.FramesPerSecond.ToString(".00", CultureInfo.CurrentCulture));    console.End();   Device.EndScene();   }    }}Card.csusing System;using System.Drawing;using System.Collections.Generic;using System.Text; using SampleFramework;using SlimDX;using SlimDX.Direct3D9; namespace CSSlimDX {    class Card : IDisposable, IGameComponent    {   public Vector3 position, selectedPosition;   public Texture texture;   public BoundingBox box;   public bool selected = false;    public bool locked = false;    public Matrix worldMatrix   {  get;  set;   } private Device _theDevice;   public Size size = new Size(10, 10);    static CustomVertex[] BuildVertexData()   {  return new CustomVertex[6] { new CustomVertex(new Vector3(-1.0f, -1.5f, 0.0f), Color.White.ToArgb(), new Vector2(0f, 1f)),  new CustomVertex(new Vector3(-1.0f, 1.5f, 0.0f), Color.White.ToArgb(), new Vector2(0f, 0f)),  new CustomVertex(new Vector3(1.0f, -1.5f, 0.0f), Color.White.ToArgb(), new Vector2(1f, 1f)),  new CustomVertex(new Vector3(1.0f, -1.5f, 0.0f), Color.White.ToArgb(), new Vector2(1f, 1f)),  new CustomVertex(new Vector3(-1.0f, 1.5f, 0.0f), Color.White.ToArgb(), new Vector2(0f, 0f)), new CustomVertex(new Vector3(1.0f, 1.5f, 0.0f), Color.White.ToArgb(), new Vector2(1f, 0f)) };   }    private VertexBuffer _cardVertices;   public VertexBuffer CardVertices   {  get  { return _cardVertices;  }   }    public Camera camera   {  get;  set;   }public Card(Device theDevice, CardList card)   {  _theDevice = theDevice;   _cardVertices = new VertexBuffer(theDevice, 6 * CustomVertex.SizeInBytes, Usage.WriteOnly, CustomVertex.Format, Pool.Managed);  DataStream stream = _cardVertices.Lock(0, 0, LockFlags.None);  stream.WriteRange(BuildVertexData());  _cardVertices.Unlock();  box = new BoundingBox();   string filename = "images/" + card.ToString() + ".png";    position = new Vector3(0, 0, 0);  selectedPosition = new Vector3(0, 0, 0);   texture = Texture.FromFile(theDevice, filename);    box.Minimum += new Vector3(-1f, -1.5f, 0f);  box.Maximum += new Vector3(1f, 1.5f, 0f);   worldMatrix = Matrix.Identity;   }    public void OnResetDevice()   {}    public void OnLostDevice()   {}    public void Dispose()   {  _cardVertices.Dispose();  _cardVertices = null;  texture.Dispose();  texture = null;   }    public void Update(GameTime gameTime)   {  // Raise them up 10 units if selected or locked  if ((selected || locked) && selectedPosition.Y < position.Y + 10)  { selectedPosition.Y += 40 * gameTime.ElapsedGameTime;  }  else if(!selected && !locked && selectedPosition.Y > position.Y)  { selectedPosition.Y -= 40 * gameTime.ElapsedGameTime;  }   worldMatrix = Matrix.Multiply(Matrix.Identity, Matrix.Scaling(size.Width, size.Height, 1f));  worldMatrix = Matrix.Multiply(worldMatrix, Matrix.Translation(selectedPosition));   }    public void Draw()   {  _theDevice.SetTransform(TransformState.World, worldMatrix);   if (selected)  { _theDevice.SetRenderState(RenderState.Lighting, false);  }  _theDevice.SetTextureStageState(0, TextureStage.AlphaOperation, TextureOperation.SelectArg1);  _theDevice.SetTextureStageState(0, TextureStage.AlphaArg1, TextureArgument.Texture);    _theDevice.SetRenderState(RenderState.SourceBlend, Blend.SourceAlpha);  _theDevice.SetRenderState(RenderState.DestinationBlend, Blend.InverseSourceAlpha);  _theDevice.SetRenderState(RenderState.AlphaBlendEnable, true);    _theDevice.SetTexture(0, texture);_theDevice.SetStreamSource(0, _cardVertices, 0, CustomVertex.SizeInBytes);  _theDevice.VertexFormat = CustomVertex.Format;  _theDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, 2);   _theDevice.SetRenderState(RenderState.Lighting, true);   }    }}
  Solved it.CSSlimDX.CS - CheckMousePicking() position = Vector3.Unproject(near, this.Device.Viewport, camera.ProjectionMatrix, camera.ViewMatrix, cards.worldMatrix); direction = Vector3.Unproject(far, this.Device.Viewport, camera.ProjectionMatrix, camera.ViewMatrix, cards.worldMatrix); // Solution direction -= position; direction.Normalize();  transformedRay = new Ray(position, direction);I had to subtract the position from the direction and then normalize to get the correct direction of the ray.  Hope this helps anyone struggling with this.   