How do I convert a pick ray's intersection into a 3D World Space (x,y,z) coordinate?

Posted on 2003-12-01
Last Modified: 2013-12-26
I presume that everybody is quite familiar with picking rays, generated from a x,y point from a mouse coordinate. Take a look at the DX9SDK, and check out the Pick sample. For an even better example, take a look at :

The above website has a project download ( which you can check out. It generates a 3D ray in world space and determines if that particular ray intersects with a given mesh.

I want to know, how I can convert the 3D ray into a 3D line, with two endpoints, so that I can use DrawPrimitive() to render a line.

What I am trying to do ultimately, is get an (x,y,z) coordinate in 3D world space of the endpoint, other than the origin of the line. I want the (x,y,z) vertex exactly where the line intersects with the mesh object, in 3D space.

I'm writing a demo which renders a flat terrain/plane object, with a skinned mesh animation walking across it, following a linear path. I want to use the parameters from the D3DXIntersect() to calculate a new (x,z) coordinate (I'm not concerned about the y coordinate as there is no height yet to my terrain) to create a new destination for my character to walk to.

These are the steps I followed:
1. Subtracted the direction vector from the origin vector I passed to D3DXIntersect() to generate another point on the line.
2. Used these coordinates with a little bit of trigonometry to calculate the sharp angle, at the camera point.
3. Using the above calculated angle, I attemted to calculate the new (x(width),z(depth)) coordinate using theorem of Pythagoras and some trig.

D3DXIntersect() modifies a parameter which indicates the depth on the ray. I used this depth as the longest line in the rectangular triangle (the skew line) along with the angle of the sharp edge (closest to the eye point) to calculate the (x,z) coordinate of the right angle.

My calculations are not working perfectly, when I project the line using DrawPrimitive(), the line is only more or less right, I shouldn't be able to see the line as I'll be looking down the line from its originating point.

Here is my code for my Render() and SetMouseXY() functions, if it helps. I might be using the totally wrong approach, and if you know better, I would love to hear from you.

void CD3DApplication::SetMouseXY(CPoint point)
    D3DXMATRIXA16 matProj;
    m_pD3DDevice->GetTransform( D3DTS_PROJECTION, &matProj );

      // Set mouse coordinates
      m_ptMouse = CPoint(point);

    // Compute the vector of the pick ray in screen space
    D3DXVECTOR3 v;
    v.x =  (((2.0f * point.x) / m_d3dsdBackBuffer.Width ) - 1) / matProj._11;
    v.y = -(((2.0f * point.y) / m_d3dsdBackBuffer.Height) - 1) / matProj._22;
    v.z =  1.0f;

    // Get the inverse view matrix
    D3DXMATRIXA16 matView, m;
    m_pD3DDevice->GetTransform( D3DTS_VIEW, &matView );
    D3DXMatrixInverse( &m, NULL, &matView );

    // Transform the screen space pick ray into 3D space
    m_vPickRayDir.x  = v.x*m._11 + v.y*m._21 + v.z*m._31;
    m_vPickRayDir.y  = v.x*m._12 + v.y*m._22 + v.z*m._32;
    m_vPickRayDir.z  = v.x*m._13 + v.y*m._23 + v.z*m._33;

      m_vPickRayOrig.x = m._41;
    m_vPickRayOrig.y = m._42;
    m_vPickRayOrig.z = m._43;

      m_vPickRayOrig += m_vPickRayDir * m_SkinMesh.GetObjectRadius() / 64.0f;

    // Transform the screen space pick ray into 3D space

      // Check if ray intersects with floor.

      // Convert the ray to model space
      D3DXMATRIX matInv;
      D3DXVECTOR3 vPickRayDir_tmp, vPickRayOrig_tmp;

      D3DXMatrixInverse(&matInv, NULL, &m_FloorMesh.m_matComposite);
      D3DXVec3TransformCoord(&vPickRayOrig_tmp, &m_vPickRayOrig, &matInv);
      D3DXVec3TransformNormal(&vPickRayDir_tmp, &m_vPickRayDir, &matInv);

      BOOL bHit=false;
      DWORD dwIndex;
      float fU,fV;


            m_bTerrainHit = true;
            m_bTerrainHit = false;
            m_fDist = 0.0f;

short CD3DApplication::Render()
      HRESULT hResult;

      D3DXVECTOR3 vPt2 = D3DXVECTOR3(0,0,0);

      // Validate device pointer
      if(m_pD3DDevice == NULL)
            return RENDER_FAILED;

      // Clear the backbuffer to blue
      m_pD3DDevice->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0,0,0), 1.0f, 0);

      // Retrieve elapsed time, 1st pass skips function body, only to set the time
      if(m_dwPreviousTime == RENDER_FIRSTPASS)
            m_dwPreviousTime = timeGetTime();
            return RENDER_SKIPPED;
      DWORD dwCurrentTime = timeGetTime();
      FLOAT fElapsedTime = (dwCurrentTime - m_dwPreviousTime) / 1000.0f;
      // Set animation track time to update frame hierarchy's transformation matrices

      // Begin rendering
            // Output stats
            // --------------------------------------------------

            char cBufferX[8], cBufferY[8];
            int nDec1 = 0, nSign1 = 0;
            int nDec2 = 0, nSign2 = 0;
            int nDec3 = 0, nSign3 = 0;
            int nDec4 = 0, nSign4 = 0;

            char strBuffer[32];

            m_pFont->DrawText(10,10,  D3DCOLOR_ARGB(255,0,255,0), "Skinned Mesh (by Josamoto Musashi)");
            m_pFont->DrawText(10,22,  D3DCOLOR_ARGB(255,0,255,0), "-------------------------------");
            m_pFont->DrawText(10,46,  D3DCOLOR_ARGB(255,0,255,0), "Mouse Coordinates...");

            m_pFont->DrawText(10,58,  D3DCOLOR_ARGB(255,0,255,0), CString("Mouse (x,y)         : ") +
                  _itoa(m_ptMouse.x, cBufferX, 10) + ", " + _itoa(m_ptMouse.y, cBufferY, 10) );
            sprintf(strBuffer, _T("Ray Direction       : %3.02f, %3.02f, %3.02f"), m_vPickRayDir.x, m_vPickRayDir.y, m_vPickRayDir.z);
            m_pFont->DrawText(10,72,  D3DCOLOR_ARGB(255,0,255,0), strBuffer);

            sprintf(strBuffer, _T("1st Endpoint        : %3.02f, %3.02f, %3.02f"), m_vPickRayOrig.x, m_vPickRayOrig.y, m_vPickRayOrig.z);
            m_pFont->DrawText(10,86,  D3DCOLOR_ARGB(255,0,255,0), strBuffer);

            // Calc second end point of line from direction
            vPt2 = m_vPickRayOrig + m_vPickRayDir;
            sprintf(strBuffer, _T("2nd Endpoint        : %3.02f, %3.02f, %3.02f"), vPt2.x, vPt2.y, vPt2.z);
            m_pFont->DrawText(10,100,  D3DCOLOR_ARGB(255,0,255,0), strBuffer);

            sprintf(strBuffer, _T("Distance            : %3.02f"), m_fDist);
            m_pFont->DrawText(10,114, D3DCOLOR_ARGB(255,0,255,0), strBuffer);

            // Calculate the angle in relation to the x,z axis
            float fT = vPt2.x - m_vPickRayOrig.x;
            float fA = vPt2.z - m_vPickRayOrig.z;
            float fAngleRad = (float)atan(fT/fA); // This is the angle in radians
            float fAngleDeg = (float)(fAngleRad * 180 / D3DX_PI);

            sprintf(strBuffer, _T("Angle(DEG)          : %3.02f"), fAngleDeg);
            m_pFont->DrawText(10,128,  D3DCOLOR_ARGB(255,0,255,0), strBuffer);
            sprintf(strBuffer, _T("Angle(RAD)          : %3.02f"), fAngleRad);
            m_pFont->DrawText(10,142,  D3DCOLOR_ARGB(255,0,255,0), strBuffer);

            // Use the angle to calculate the x(width), z(depth) coordinate. y(height) will be 0.0f
            float fX = (float)(m_fDist * sin(fAngleRad));
            float fZ = m_vPickRayOrig.z + (float)(m_fDist * cos(fAngleRad));
            D3DXVECTOR3 vPtNew = D3DXVECTOR3(fX,0.0f,fZ);
            sprintf(strBuffer, _T("New Endpoint        : %3.02f, %3.02f, %3.02f"), vPtNew.x, vPtNew.y, vPtNew.z);
            m_pFont->DrawText(10,154,  D3DCOLOR_ARGB(255,0,255,0), strBuffer);

                  m_pFont->DrawText(10,580, D3DCOLOR_ARGB(255,0,255,0), "Terrain hit!");
                  m_pFont->DrawText(10,580, D3DCOLOR_ARGB(255,0,255,0), "Terrain not hit!");
            // Vertices for rendering our 3d space reference axis
            CUSTOMVERTEX vertices[] =
                  // X-Axis (Red)
                  {  0.0f,  0.5f,  0.0f, },
                  { 25.0f,  0.5f,  0.0f, },
                  // Y-Axis (Green)
                  {  0.0f,  0.5f,  0.0f, },
                  {  0.0f, 30.0f,  0.0f, },

                  // Z-Axis (Blue)
                  {  0.0f,  0.5f, 45.0f, },
                  {  0.0f,  0.5f,  0.0f, },

                  // Projected ray (Yellow)
                  { vPtNew.x,  vPtNew.y,  vPtNew.z, },
                  { m_vPickRayOrig.x,  m_vPickRayOrig.y,  m_vPickRayOrig.z, },

            // Create vertex buffer
            if(FAILED(hResult = m_pD3DDevice->CreateVertexBuffer(8 * sizeof(CUSTOMVERTEX), 0, D3DFVF_CUSTOMVERTEX,
                  D3DPOOL_DEFAULT, &m_pVertexBuffer, NULL)))
                   return 0;

            // Fill the vertex buffer
            VOID* pVertices;
            if(FAILED(hResult = m_pVertexBuffer->Lock(0, sizeof(vertices), (void**)&pVertices, 0)))
                  return 0;
            memcpy(pVertices, vertices, sizeof(vertices));

            // Render the 3d space reference axis
            // --------------------------------------------------
            D3DMATERIAL9 matRed, matGreen, matBlue, matYellow;
            D3DXCOLOR colorRed(D3DCOLOR_RGBA(255,0,0,255));
            D3DXCOLOR colorGreen(D3DCOLOR_RGBA(0,255,0,255));
            D3DXCOLOR colorBlue(D3DCOLOR_RGBA(0,0,255,255));
            D3DXCOLOR colorYellow(D3DCOLOR_RGBA(255,255,0,255));
            ZeroMemory(&matRed, sizeof(D3DMATERIAL9));
            matRed.Diffuse.r = matRed.Ambient.r = colorRed.r;
            matRed.Diffuse.g = matRed.Ambient.g = colorRed.g;
            matRed.Diffuse.b = matRed.Ambient.b = colorRed.b;
            matRed.Diffuse.a = matRed.Ambient.a = colorRed.a;

            ZeroMemory(&matGreen, sizeof(D3DMATERIAL9));
            matGreen.Diffuse.r = matGreen.Ambient.r = colorGreen.r;
            matGreen.Diffuse.g = matGreen.Ambient.g = colorGreen.g;
            matGreen.Diffuse.b = matGreen.Ambient.b = colorGreen.b;
            matGreen.Diffuse.a = matGreen.Ambient.a = colorGreen.a;

            ZeroMemory(&matBlue, sizeof(D3DMATERIAL9));
            matBlue.Diffuse.r = matBlue.Ambient.r = colorBlue.r;
            matBlue.Diffuse.g = matBlue.Ambient.g = colorBlue.g;
            matBlue.Diffuse.b = matBlue.Ambient.b = colorBlue.b;
            matBlue.Diffuse.a = matBlue.Ambient.a = colorBlue.a;

            ZeroMemory(&matYellow, sizeof(D3DMATERIAL9));
            matYellow.Diffuse.r = matYellow.Ambient.r = colorYellow.r;
            matYellow.Diffuse.g = matYellow.Ambient.g = colorYellow.g;
            matYellow.Diffuse.b = matYellow.Ambient.b = colorYellow.b;
            matYellow.Diffuse.a = matYellow.Ambient.a = colorYellow.a;

            m_pD3DDevice->SetStreamSource(0, m_pVertexBuffer, 0, sizeof(CUSTOMVERTEX));
            m_pD3DDevice->SetTexture(0, NULL);

            m_pD3DDevice->DrawPrimitive(D3DPT_LINELIST, 0, 1);

            m_pD3DDevice->DrawPrimitive(D3DPT_LINELIST, 2, 1);

            m_pD3DDevice->DrawPrimitive(D3DPT_LINELIST, 4, 1);

            m_pD3DDevice->DrawPrimitive(D3DPT_LINELIST, 6, 1);

            D3DXMATRIX matWorld;
            D3DXVECTOR3 vObjectCenter;
            //D3DXMatrixTranslation(&matWorld, -vObjectCenter.x, -vObjectCenter.y, -vObjectCenter.z);
            D3DXMatrixTranslation(&matWorld, 0, 0, 0);
            // Render the mesh object
            // --------------------------------------------------

            // End scene rendering
            // --------------------------------------------------

      // Present the backbuffer contents to the display
      m_pD3DDevice->Present(NULL, NULL, NULL, NULL);

      // Save current time for our next render
      m_dwPreviousTime = dwCurrentTime;

      // Return success
      return RENDER_SUCCESS;

Question by:josamoto
1 Comment
LVL 45

Accepted Solution

sunnycoder earned 500 total points
ID: 9856603
I am not into directx so cant appreciate your code much but I could make out what you are after
see if this helps

Featured Post

Free Tool: Port Scanner

Check which ports are open to the outside world. Helps make sure that your firewall rules are working as intended.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Suggested Solutions

Title # Comments Views Activity
Is freelancing coding for projects dangerous? 8 188
Game needs - How much would Reese Witherspoon or Buffy charge per hour? 4 158
scoresClump  challenge 31 144
copyEndy  challenge 15 97
What is RenderMan: RenderMan is a not any particular piece of software. RenderMan is an industry standard, defining set of rules that any rendering software should use, to be RenderMan-compliant. Pixar's RenderMan is a flagship implementation of …
Recently, in one of the tech-blogs I usually read, I saw a post about the best-selling video games through history. The first place in the list is for the classic, extremely addictive Tetris. Well, a long time ago, in a galaxy far far away, I was…
Two types of users will appreciate AOMEI Backupper Pro: 1 - Those with PCIe drives (and haven't found cloning software that works on them). 2 - Those who want a fast clone of their boot drive (no re-boots needed) and it can clone your drive wh…
Finds all prime numbers in a range requested and places them in a public primes() array. I've demostrated a template size of 30 (2 * 3 * 5) but larger templates can be built such 210  (2 * 3 * 5 * 7) or 2310  (2 * 3 * 5 * 7 * 11). The larger templa…

809 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question