You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何基于D3DX将2D屏幕坐标逆转换为3D世界坐标?

Reversing 3D World to 2D Screen Transformation in Direct3D

Great question! Let’s start with a critical detail you need to wrap your head around first: a single 2D screen point corresponds to an infinite number of 3D world points. Every point along a straight ray from your camera through the screen pixel will project to that same 2D coordinate. To get a specific 3D position, you’ll either need the depth value of the point (from the z-buffer, for example) or to intersect this ray with a plane/object in your scene.

Step 1: Recap the Forward Transformation

Looking at your D3DXVec3TransformCoordImpl code, the forward process boils down to two key steps:

  1. Multiply the 3D world coordinate (represented as a homogeneous vector (x,y,z,1)) by the combined view-projection matrix.
  2. Perform perspective division by the resulting w component (your norm variable) to get normalized device coordinates (NDC), where x,y ∈ [-1, 1] and z ∈ [0, 1] (standard for Direct3D).

Reversing this means undoing these steps in reverse order.

Step 2: Convert Screen Coordinates to NDC

If your pScreen variable is already in NDC, skip this step. If it’s in pixel coordinates (e.g., ranging from (0,0) to (screenWidth-1, screenHeight-1)), convert it first:

float screenX = pScreen.x;
float screenY = pScreen.y;
float screenZ = pScreen.z; // This is the depth value from your forward transform (0-1 range)

float ndcX = (2.0f * screenX) / screenWidth - 1.0f;
float ndcY = 1.0f - (2.0f * screenY) / screenHeight;
float ndcZ = screenZ;

Step 3: Compute the Inverse View-Projection Matrix

The core of reversing the transform lies in using the inverse of your viewProjectionMatrix—this matrix undoes the combined effect of the camera view and projection transforms:

D3DXMATRIX invViewProj;
// Calculate the inverse of the view-projection matrix
if (D3DXMatrixInverse(&invViewProj, nullptr, &viewProjectionMatrix) == NULL) {
    // Handle inversion failure (e.g., singular matrix)
    D3DXVECTOR3 defaultWorld(0.0f, 0.0f, 0.0f);
    return defaultWorld;
}

Step 4: Transform Back to 3D World Coordinates

We’ll use a homogeneous NDC vector (ndcX, ndcY, ndcZ, 1) and multiply it by the inverse matrix, then perform perspective division again to get the final 3D world coordinate:

// Create a homogeneous NDC vector
D3DXVECTOR4 ndcHomogeneous(ndcX, ndcY, ndcZ, 1.0f);

// Transform by the inverse view-projection matrix
D3DXVECTOR4 worldHomogeneous;
D3DXVec4Transform(&worldHomogeneous, &ndcHomogeneous, &invViewProj);

// Undo perspective division to get the 3D world coordinate
D3DXVECTOR3 pWorld;
if (worldHomogeneous.w != 0.0f) {
    pWorld.x = worldHomogeneous.x / worldHomogeneous.w;
    pWorld.y = worldHomogeneous.y / worldHomogeneous.w;
    pWorld.z = worldHomogeneous.z / worldHomogeneous.w;
} else {
    // Handle division by zero (rare if matrix inversion succeeded)
    pWorld = D3DXVECTOR3(0.0f, 0.0f, 0.0f);
}

Step 5: Handling Cases Without Depth (Camera Ray)

If you don’t have the screenZ depth value, you can compute the camera ray that passes through the screen point. This ray can then be used to intersect with objects in your scene to find the corresponding 3D point:

  1. Get Camera Position: Transform the origin (0,0,0) with the inverse view matrix to get the camera’s world position:
    D3DXMATRIX invViewMatrix;
    D3DXMatrixInverse(&invViewMatrix, nullptr, &renderer->viewMatrix);
    D3DXVECTOR4 cameraHomogeneous(0.0f, 0.0f, 0.0f, 1.0f);
    D3DXVec4Transform(&cameraHomogeneous, &cameraHomogeneous, &invViewMatrix);
    D3DXVECTOR3 cameraPos(cameraHomogeneous.x / cameraHomogeneous.w, 
                          cameraHomogeneous.y / cameraHomogeneous.w, 
                          cameraHomogeneous.z / cameraHomogeneous.w);
    
  2. Calculate Ray Direction: Transform a far-plane NDC point (ndcX, ndcY, 1.0f) to world space, then subtract the camera position and normalize the result:
    D3DXVECTOR4 farNdcHomogeneous(ndcX, ndcY, 1.0f, 1.0f);
    D3DXVECTOR4 farWorldHomogeneous;
    D3DXVec4Transform(&farWorldHomogeneous, &farNdcHomogeneous, &invViewProj);
    D3DXVECTOR3 farWorld(farWorldHomogeneous.x / farWorldHomogeneous.w, 
                         farWorldHomogeneous.y / farWorldHomogeneous.w, 
                         farWorldHomogeneous.z / farWorldHomogeneous.w);
    D3DXVECTOR3 rayDir = farWorld - cameraPos;
    D3DXVec3Normalize(&rayDir, &rayDir);
    
    Now you have a ray starting at cameraPos with direction rayDir—any point along this ray will project to your original 2D screen point.

Why This Works

Your forward transform maps world space to NDC via the view-projection matrix, then applies perspective division to get screen coordinates. Reversing this requires:

  • Converting screen coordinates back to NDC
  • Using the inverse matrix to map NDC back to homogeneous world space
  • Undoing perspective division by dividing by the w component of the resulting homogeneous vector

内容的提问来源于stack exchange,提问作者Bob A

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 07:08:43