鼠标坐标转世界坐标:为何不同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值变化。
透视射线生成的正确逻辑
从鼠标点生成世界空间射线,正确的步骤应该是这样的:
- 把鼠标坐标转成NDC坐标:你这部分是对的,
x=(2*mouseX/宽)-1,y=1-(2*mouseY/高),这个能把屏幕坐标转成[-1,1]范围的标准设备坐标。 - 取NDC空间的近远平面点:比如近平面Z=-1、远平面Z=1(不同图形API的NDC范围可能有差异,OpenGL是[-1,1],DX是[0,1],根据你的投影矩阵调整),这两个点的齐次坐标
w都设为1。 - 逆投影转视图空间+透视除法:用逆投影矩阵乘这两个点,然后把x/y/z分别除以
w分量,得到视图空间下的近远平面点。 - 逆视图转世界空间:用逆视图矩阵把视图空间的点转成世界空间的点,这两个点就定义了一条从相机出发的射线。
修正后的代码示例
我给你调整了代码,按照正确的逻辑来生成射线:
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,提问作者小邪薪邪械胁
相关产品推荐
相关产品推荐

