我正在尝试使用 ARKit 将屏幕上的某个点取消投影到世界空间中 ARPlaneAnchor 上的坐标。本质上,我想根据相机指向的位置在地平面上绘制一个标记。 ARKit 提供了一些方法来做到这一点,但是它们附加到 ARCamera 类,不幸的是,在我的场景中,我只能访问相机的投影、 View 和变换矩阵,并且没有引用 ARFrame 或 ARCamera 对象本身。我写了一些与 gluUnproject() 类似的东西(据我所知)。
func screenToWorld(screenPoint: CGPoint, depth: Float, screenSize: CGSize, projectionMatrix: simd_float4x4, viewMatrix:simd_float4x4) -> float3{
let viewProj = viewMatrix * projectionMatrix
let inverse = simd_inverse(viewProj)
let x = (2.0 * screenPoint.x ) / screenSize.width - 1.0
let y = (2.0 * screenPoint.y) / screenSize.height - 1.0
let inPoint = float4(Float(x), Float(y), depth * 2.0 - 1.0, 1.0)
var position = inPoint * inverse
position.w = 1.0 / position.w
position.x *= position.w
position.y *= position.w
position.z *= position.w
return float3(position.x, position.y, position.z)
}
let pos = screenToWorld(screenPoint: CGPoint(360, 640), depth: depthBufferValue, screenSize: CGSize(720, 1280), projectionMatrix: projectionMatrix, viewMatrix: viewMatrix)
projectionMatrix(for:.portrait, viewportSize: CGSize(720,1280),zNear: 0.001, zFar: 1000)
viewMatrix(for:.portrait)
我唯一不确定的是 viewMatrix 属性。我唯一能想到的可能是附加到相机的变换矩阵。但是,我已经尝试过了,它似乎也没有给我正确的位置。在 gluUnproject 他们将此值称为模型 View 矩阵,在 glm 中它只是称为模型,所以我对这应该是什么感到有点困惑。
使用从我的 unProject 函数中获得的值,我可以像这样转换一个单位矩阵
var modelMatrix = matrix_identity_float4x4
modelMatrix = modelMatrix.translatedBy(x: pos.x, y: pos.y, z: pos.z)
然后像这样将值发送到着色器......
encoder.setVertexBytes(&projectionMatrix, length: MemoryLayout<simd_float4x4>.stride, index: 1)
encoder.setVertexBytes(&viewMatrix, length: MemoryLayout<simd_float4x4>.stride, index: 2)
encoder.setVertexBytes(&modelMatrix, length: MemoryLayout<simd_float4x4>.stride, index: 3)
在我的顶点着色器里面我做
VertexOut vertexOut;
float4x4 modelViewMatrix = viewMatrix * modelMatrix;
vertexOut.position = projectionMatrix * modelViewMatrix * vertexIn.position;
return vertexOut;
我认为我的 z 值是正确的(深度缓冲区中的深度值),所以实际上我只需要找到我的 x 和 y。我也不是 100% 确定如何处理我的 unproject 函数返回的值。我认为这只是世界坐标,但也许需要以某种方式缩放?
我很想得到一些帮助来获得正确的坐标,或者如果上面有任何错误,请指出!
最佳答案
对于将来遇到此问题的任何人,解决方案(如@rabbid76 所建议的)正在翻转我的投影和 View 矩阵以及我的逆矩阵和 inPoint 的乘法顺序。我还需要翻转要检查的 screenPoint 的 y 坐标。
这是完整的功能:
func screenToWorld(screenPoint: CGPoint, depth: Float, screenSize: CGSize, projectionMatrix: simd_float4x4, viewMatrix:simd_float4x4) -> float3{
let viewProj = projectionMatrix * viewMatrix
let inverse = simd_inverse(viewProj)
let x = (2.0 * screenPoint.x ) / screenSize.width - 1.0
let y = 1.0 - (2.0 * screenPoint.y) / screenSize.height
let inPoint = float4(Float(x), Float(y), depth * 2.0 - 1.0, 1.0)
var position = inverse * inPoint
position.w = 1.0 / position.w
position.x *= position.w
position.y *= position.w
position.z *= position.w
return float3(position.x, position.y, position.z)
}
关于matrix - 在 ARKit 中将屏幕点取消投影到水平面,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52461052/