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

鼠标坐标转世界坐标:为何不同Z值下X、Y坐标保持一致?

为什么不同Z值下鼠标射线的X、Y坐标完全相同?

嘿,这个问题其实是因为你对透视投影的射线生成逻辑理解有点偏差,再加上代码里的几个操作破坏了逆变换的正确结果,才导致不管Z取什么值,X/Y都完全一样。咱们一步步拆解来看:

问题根源:错误跳过透视除法,硬修改齐次坐标值

透视投影的逆变换不是简单乘个矩阵就完事的——齐次坐标的w分量在这里至关重要。你看你的代码:

  • 创建tmpVec时直接把w设为0,乘完逆投影矩阵后又强行把z改成4、w改回0;
  • 处理Z=-100的tmpVec1时,先设w=1,乘完逆投影又硬改z和w。

这两步操作直接跳过了透视除法(也就是把齐次坐标的x/y/z除以w分量),而透视投影的核心就是通过w分量实现近大远小的缩放。当你硬改z和w时,逆投影矩阵的透视缩放效果完全没起作用,所以X/Y自然不会随着Z值变化。

透视射线生成的正确逻辑

从鼠标点生成世界空间射线,正确的步骤应该是这样的:

  1. 把鼠标坐标转成NDC坐标:你这部分是对的,x=(2*mouseX/宽)-1,y=1-(2*mouseY/高),这个能把屏幕坐标转成[-1,1]范围的标准设备坐标。
  2. 取NDC空间的近远平面点:比如近平面Z=-1、远平面Z=1(不同图形API的NDC范围可能有差异,OpenGL是[-1,1],DX是[0,1],根据你的投影矩阵调整),这两个点的齐次坐标w都设为1。
  3. 逆投影转视图空间+透视除法:用逆投影矩阵乘这两个点,然后把x/y/z分别除以w分量,得到视图空间下的近远平面点。
  4. 逆视图转世界空间:用逆视图矩阵把视图空间的点转成世界空间的点,这两个点就定义了一条从相机出发的射线。

修正后的代码示例

我给你调整了代码,按照正确的逻辑来生成射线:

private Vector3f[] getMouseRay(MouseInput mouseInput) {
    int windowWidth = 640;
    int windowHeight = 640;
    Vector2d mousePos = mouseInput.getCurrentPos();

    // 1. 转换为NDC坐标
    float x = (2.0f * (float)mousePos.x) / windowWidth - 1.0f;
    float y = 1.0f - (2.0f * (float)mousePos.y) / windowHeight;

    // 准备逆矩阵
    Matrix4f invProjection = new Matrix4f(pMatrix);
    invProjection.invert();
    Matrix4f invView = new Matrix4f(view);
    invView.invert();

    // 2. NDC空间的近、远平面点(w=1)
    Vector4f nearNdc = new Vector4f(x, y, -1.0f, 1.0f);
    Vector4f farNdc = new Vector4f(x, y, 1.0f, 1.0f);

    // 3. 逆投影转视图空间 + 透视除法
    nearNdc.mul(invProjection);
    nearNdc.div(nearNdc.w);
    farNdc.mul(invProjection);
    farNdc.div(farNdc.w);

    // 4. 逆视图转世界空间
    nearNdc.mul(invView);
    farNdc.mul(invView);

    // 转成Vector3f返回
    Vector3f nearWorld = new Vector3f(nearNdc.x, nearNdc.y, nearNdc.z);
    Vector3f farWorld = new Vector3f(farNdc.x, farNdc.y, farNdc.z);

    return new Vector3f[]{nearWorld, farWorld};
}

如何得到指定Z值的点

如果想得到Z=4或者Z=-100的点,你可以先从射线的起点(相机位置,可通过逆视图矩阵提取)和方向来计算:

// 假设已经得到了nearWorld和farWorld
Vector3f rayOrigin = new Vector3f(invView.m03(), invView.m13(), invView.m23()); // 相机世界位置
Vector3f rayDir = farWorld.sub(nearWorld);
rayDir.normalize();

// 计算Z=4的点:解rayOrigin.z + t*rayDir.z = 4,得到t后计算x/y
float t = (4 - rayOrigin.z) / rayDir.z;
Vector3f pointZ4 = rayOrigin.add(rayDir.mul(t));

// Z=-100同理
t = (-100 - rayOrigin.z) / rayDir.z;
Vector3f pointZMinus100 = rayOrigin.add(rayDir.mul(t));

这样得到的两个点,X/Y就会随着Z值变化,符合透视投影的近大远小规律了。

内容的提问来源于stack exchange,提问作者小邪薪邪械胁

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 10:04:51