   Problems with Sphere - Ray intersection test [sovled]
  I'm currently trying to learn how those intersection test work. For that, I've created a little application that displays a set of shapes and tells me if the mouse hovers them or not.My current approach seems to work pixel perfect, when the radius is exactly 1, but once I set the radius to something like 10, I need to move the mouse like 10 pixels inside the sphere on the screen to get an actual hit. This is really strange, so I hope someone can help me with this.First, I create a ray that shoots through the scene, starting at the camera position, flying "through" the mouse cursor. However I doubt the error is here, as this code is independent of the sphere size.// Calculate the mouse's relative position: (-1, -1) is the lower left corner, (1, 1) is the upper right onemath::float2 screenPos = inputDevice->mousePositionRelative();math::float4x4 viewProj = camera->viewProjection();// And now transform the screen point into world space, where it represents the direction// a ray would face, when it shoots through the camera's center and the "mouse position".math::float3 direction = inverse( viewProj ) * math::float3( screenPos.x, screenPos.y, camera->clippingPlanes().x );direction.norm();// Create a ray that shoots through the entire clipping volume in world spacemath::Ray<float> ray( camera->position(),  camera->position() + direction * camera->clippingPlanes().y );Now here's the actual intersection code for Sphere - Ray:class Sphere{/// Position of the spherevector3_t position;/// Radius of the spherefloat_t   radius;...boost::optional<vector3_t> rayCast( const Ray_t &ray ) const{vector3_t delta= position - ray.start;float_t   delta_lsq = delta.lengthSquared();float_t   radius_sq = radius * radius;float_t   ray_lsq   = ray.length * ray.length;// When the distance of the sphere's surface to// the start point of the ray is longer than the// ray's length, then there is no hitif( delta_lsq - radius_sq > ray_lsq )return boost::optional<vector3_t>();// Now we calculate to what extent we need to multiply// the ray's length, to get the closest point to the// sphere's center.float_t d = dot( delta, ray.direction );// Now this is our closest point to the spherevector3_t closest = ray.start + d * ray.direction;delta   = closest - position;float_t   dist    = delta.lengthSquared();// If the distance of the closest point to the sphere's// center is bigger than the radius, then we don't have a hitif( dist > radius_sq )return boost::optional<vector3_t>();// Yay, there is a hit :)// Calculate to which extent the closest point needs to travel// along the ray to exactly hit the sphere's surface// This involves a little trigonometry: we take the triangle, formed// by the two vectors:// -closest => center// -closest => sphere's surface where the ray hits// We do know the distance of the center to the sphere's surface, it's the radius :)// we also know the distance of the closest point to the sphere's center, so// we can calculate the missing one's lengthfloat_t s = sqrt( radius_sq - dist );// Again, we need to multiply the line to this extent, however this time it's// in the other side, to the start againvector3_t hit = closest - s * ray.direction;// Now we have our hit point, YAYreturn boost::optional<vector3_t>( hit );}When I set the radius to 10, the test fails on the line "if( dist > radius_sq )", dist is about 0.7 bigger than radius, although the mouse pointer is clearly inside the sphere on screen.When it comes to rendering, I multiply the scale matrix (3x3 matrix) with the rotation matrix (also 3x3 matrix) and create the world matrix my using the result and the position of the sphere.math::float4x4 matWorld = math::float4x4( m_matScale * m_matRotation, m_position );...pass.params.set( "object_to_raster", matWorld * matViewProj );As a side note: The vector and matrix classes are written by myself (to learn the math behind them) and unit tested against the directx implementations (as far as vector2, vector3, vector3 and matrix4x4 go, as there is no matrix3x3 directx implementation).[Edited by - SiS-Shadowman on June 29, 2009 4:41:06 PM]
  I'm too busy to look through all your code, but assuming your camera can move around:When the user presses a key, save the current ray_start and ray_end.Draw that as a line in 3d, move your camera up close to the sphere and see where they ray actually went to. It seems like your inverse is incorrect since something seems so scale as screen distance grows.   ;  Thank you so much for your help :)You were completely right: I positioned the mouse just inside the sphere (a view pixels), pressed my button, and the line was outside the sphere, when I moved the camera.So what can I say about the inverse function... writing it was a chore. I don't understand how it is calculated, I simply took the implementation off the net and tested my result against D3DXMatrix... it seemed to work then (I just ran the unit test for assurance).Maybe my unprojection is incorrect, so this is how I do this:math::float4x4 view = camera->view();math::float4x4 projection = camera->projection();math::float4x4 viewProjection = view * projection;math::float4x4 viewProjectionInv = inverse( viewProjection );math::float3 direction = viewProjectionInv * math::float3( screenPos.x, screenPos.y, camera->clippingPlanes().x );This is the inverse function:/** * Calculate the determinant of this matrix. * * @see   http://www.cvl.iis.u-tokyo.ac.jp/~miyazaki/tech/teche23.html */float_t determinant() const{return _11 * _22 * _33 * _44 + _11 * _23 * _34 * _42 + _11 * _24 * _32 * _43 + _12 * _21 * _34 * _43 + _12 * _23 * _31 * _44 + _12 * _24 * _33 * _41 + _13 * _21 * _32 * _44 + _13 * _22 * _34 * _41 + _13 * _24 * _31 * _42 + _14 * _21 * _33 * _42 + _14 * _22 * _31 * _43 + _14 * _23 * _32 * _41 - _11 * _22 * _34 * _43 - _11 * _23 * _32 * _44 - _11 * _24 * _33 * _42 - _12 * _21 * _33 * _44 - _12 * _23 * _34 * _41 - _12 * _24 * _31 * _43 - _13 * _21 * _34 * _42 - _13 * _22 * _31 * _44 - _13 * _24 * _32 * _41 - _14 * _21 * _32 * _43 - _14 * _22 * _33 * _41 - _14 * _23 * _31 * _42;}/** * Set this matrix to its inverse matrix. * * @see   http://www.cvl.iis.u-tokyo.ac.jp/~miyazaki/tech/teche23.html */void inverse(){float_t A = determinant();assert( A != 0.0f );*this = 1 / A * matrix4x4_t( _22 * _33 * _44 + _23 * _34 * _42 + _24 * _32 * _43 - _22 * _34 * _43 - _23 * _32 * _44 - _24 * _33 * _42, // 11  _12 * _34 * _43 + _13 * _32 * _44 + _14 * _33 * _42 - _12 * _33 * _44 - _13 * _34 * _42 - _14 * _32 * _43, // 12  _12 * _23 * _44 + _13 * _24 * _42 + _14 * _22 * _43 - _12 * _24 * _43 - _13 * _22 * _44 - _14 * _23 * _42, // 13  _12 * _24 * _33 + _13 * _22 * _34 + _14 * _23 * _32 - _12 * _23 * _34 - _13 * _24 * _32 - _14 * _22 * _33, // 14  _21 * _34 * _43 + _23 * _31 * _44 + _24 * _33 * _41 - _21 * _33 * _44 - _23 * _34 * _41 - _24 * _31 * _43, // 21  _11 * _33 * _44 + _13 * _34 * _41 + _14 * _31 * _43 - _11 * _34 * _43 - _13 * _31 * _44 - _14 * _33 * _41, // 22  _11 * _24 * _43 + _13 * _21 * _44 + _14 * _23 * _41 - _11 * _23 * _44 - _13 * _24 * _41 - _14 * _21 * _43, // 23  _11 * _23 * _34 + _13 * _24 * _31 + _14 * _21 * _33 - _11 * _24 * _33 - _13 * _21 * _34 - _14 * _23 * _31, // 24  _21 * _32 * _44 + _22 * _34 * _41 + _24 * _31 * _42 - _21 * _34 * _42 - _22 * _31 * _44 - _24 * _32 * _41, // 31  _11 * _34 * _42 + _12 * _31 * _44 + _14 * _32 * _41 - _11 * _32 * _44 - _12 * _34 * _41 - _14 * _31 * _42, // 32  _11 * _22 * _44 + _12 * _24 * _41 + _14 * _21 * _42 - _11 * _24 * _42 - _12 * _21 * _44 - _14 * _22 * _41, // 33  _11 * _24 * _32 + _12 * _21 * _34 + _14 * _22 * _31 - _11 * _22 * _34 - _12 * _24 * _31 - _14 * _21 * _32, // 34  _21 * _33 * _42 + _22 * _31 * _43 + _23 * _32 * _41 - _21 * _32 * _43 - _22 * _33 * _41 - _23 * _31 * _42, // 41  _11 * _32 * _43 + _12 * _33 * _41 + _13 * _31 * _42 - _11 * _33 * _42 - _12 * _31 * _43 - _13 * _32 * _41, // 42  _11 * _23 * _42 + _12 * _21 * _43 + _13 * _22 * _41 - _11 * _22 * _43 - _12 * _23 * _41 - _13 * _21 * _42, // 43  _11 * _22 * _33 + _12 * _23 * _31 + _13 * _21 * _32 - _11 * _23 * _32 - _12 * _21 * _33 - _13 * _22 * _31  // 44 );}*Edit*There's another thing that is really strange.I thought that when I unproject a vector in screen space, I should get it in world space instead. So I rendered the following 2 points as a line:math::float3   start = viewProjectionInv * math::float3( screenPos.x, screenPos.y, 0 );math::float3   end   = viewProjectionInv * math::float3( screenPos.x, screenPos.y, 1 );Shouldn't the line start at the near plane (where the mouse) is and end at the far plane (at the mouse position, still)? It looks like the line starts at the mouse, but moves to (0,0,0) in world space instead: to the sphere's center in this case. This is really odd.*Edit2* Okay, so I solved the problem.Again, thanks so much for pointing me in the right direction: I could solve the problem with this thread in the xna forums. Basically, my first method of extracting the ray is flawed. I don't really understand why, but the second method I presented (using a point on the near and far plane) works, however I forgot this cruical point:Quote:Original post by dexyAnd thats when it all flashed back to me. I read my earlier post in here where I said I had to do something with the W values. OF COURSE!! I remembered reading, while I was learning about how to perform a matrix inversion (a while ago), that when unprojecting you have to use 4-component vectors...!!!!! And then after you have transformed your 4d vector, to convert it into the 3d one you simply divide by W. And that's what the "float a=..." is in the Viewport.Unproject.So this is the final code I use to create the ray (which fits pixel perfect):math::float4 tmp = viewProjectionInv * math::float4( screenPos.x, screenPos.y, 0, 1 );math::float3 start = math::float3( tmp.x, tmp.y, tmp.z );start /= tmp.w;tmp = viewProjectionInv * math::float4( screenPos.x, screenPos.y, 1, 1 );math::float3 end = math::float3( tmp.x, tmp.y, tmp.z );end /= tmp.w;math::Ray<float> ray( start, end ); [Edited by - SiS-Shadowman on June 29, 2009 4:43:54 PM]