您的位置:首页 > 其它

投影矩阵到底长什么样子?

2013-07-18 21:25 176 查看
首先很惭愧的说,我学了好多年图形学连这个问题都回答不出。所以回过头来补充一下这方面的知识。这个问题是我在看DirectX中的Pick sample中的Pick()函数时发现的。Pick()函数里把一条屏幕射线变换到模型局部空间。看它是怎么变的:
  const D3DXMATRIX* pmatProj = g_Camera.GetProjMatrix();

  POINT ptCursor;
  GetCursorPos( &ptCursor );
  ScreenToClient( DXUTGetHWND(), &ptCursor );

  // Compute the vector of the Pick ray in screen space
  D3DXVECTOR3 v;
  v.x = ( ( ( 2.0f * ptCursor.x ) / pd3dsdBackBuffer->Width ) - 1 ) / pmatProj->_11;
  v.y = -( ( ( 2.0f * ptCursor.y ) / pd3dsdBackBuffer->Height ) - 1 ) / pmatProj->_22;
  v.z = 1.0f;

  // Get the inverse view matrix
  const D3DXMATRIX matView = *g_Camera.GetViewMatrix();
  const D3DXMATRIX matWorld = *g_Camera.GetWorldMatrix();
  D3DXMATRIX mWorldView = matWorld * matView;
  D3DXMATRIX m;
  D3DXMatrixInverse( &m, NULL, &mWorldView );

  // Transform the screen space Pick ray into 3D space
  vPickRayDir.x = v.x * m._11 + v.y * m._21 + v.z * m._31;
  vPickRayDir.y = v.x * m._12 + v.y * m._22 + v.z * m._32;
  vPickRayDir.z = v.x * m._13 + v.y * m._23 + v.z * m._33;
  vPickRayOrig.x = m._41;
  vPickRayOrig.y = m._42;
  vPickRayOrig.z = m._43;

先回顾一些知识。1)DirectX和OpenGL中矩阵向量区别,详见这里 2)齐次坐标,3D变换,详见这里

3 ) 透视投影变换推导请看这里。4) view与world的区别



从Normalized device space 到 window space的具体变换公式是



xw,yw是屏幕坐标, 也就是ptCursor.x,ptCursor.y。

xnd,ynd是做完投影归一化后得到的坐标。

x, y是窗口左下角的坐标。在本例Pick例中x = y = 0。

现在来看

  v.x = ( ( ( 2.0f * ptCursor.x ) / pd3dsdBackBuffer->Width ) - 1 ) / pmatProj->_11;
  v.y = -( ( ( 2.0f * ptCursor.y ) / pd3dsdBackBuffer->Height ) - 1 ) / pmatProj->_22;

  ( ( ( 2.0f * ptCursor.x ) / pd3dsdBackBuffer->Width ) - 1 ),-( ( ( 2.0f * ptCursor.y ) / pd3dsdBackBuffer->Height ) - 1 )是将屏幕坐标变换到归一化的投影坐标空间,这个可以理解了。

( 这个归一化的空间是个axis-aligned cube. 在OpenGL中是[-1,-1,-1], [1, 1, 1]分别作为cube的minimum corner and maximum corner。在DirectX中是[-1,-1, 0],[1, 1, 0]。详情请查看Realtime Rendering 3rd version )

现在还差/pmatProj->_11, /pmatProj->_22暂时无法理解

OpenGL中透视矩阵为



DirectX中是以上矩阵的转置。

不论是从Realtime rendering 3rd version上还是某大牛的具体推导,都是以上矩阵形式。 那为什么会是/pmatProj->_11, /pmatProj->_22?

百思不得其解以后,我调试进入相应代码位置,发现这个pmatProj矩阵不同于以上所谓的投影矩阵,它是从函数

D3DXMatrixPerspectiveFovLH( &m_mProj, fFOV, fAspect, fNearPlane, fFarPlane )返回的。

m_mProj这个矩阵只有_11,_22, _33,_34, _43, 不为0,其他元素都为0。

回头一想,肯定是由于right = -left, top = -bottom, 所以_30 = 0, _31 = 0.

所以/pmatProj->_11, /pmatProj->_22之后就是投影之前的x, y坐标了。

最后的变换是将射线变换到局部空间,

// Get the inverse view matrix
const D3DXMATRIX matView = *g_Camera.GetViewMatrix();
const D3DXMATRIX matWorld = *g_Camera.GetWorldMatrix();
D3DXMATRIX mWorldView = matWorld * matView;
D3DXMATRIX m;
D3DXMatrixInverse( &m, NULL, &mWorldView );

// Transform the screen space Pick ray into 3D space
vPickRayDir.x = v.x * m._11 + v.y * m._21 + v.z * m._31;
vPickRayDir.y = v.x * m._12 + v.y * m._22 + v.z * m._32;
vPickRayDir.z = v.x * m._13 + v.y * m._23 + v.z * m._33;
vPickRayOrig.x = m._41;
vPickRayOrig.y = m._42;
vPickRayOrig.z = m._43;

最后一个问题是为什么vPickRayOrig只跟 m 有关系而跟拾取点无关呢?希望有大牛来帮忙解答一下。

今天看到OpenGPU坛子上有朋友也发了这个问题而且有好多朋友已经回答了。

原来view 空间其实应该是从视点发出来的一些斜线。
http://www.opengpu.org/bbs/forum.php?mod=viewthread&tid=15692
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: