我正在尝试使用ARKit将屏幕上的某个点投影到世界空间中ARPlaneAnchor上的坐标上。本质上,我想根据相机指向的位置在地平面上绘制标记。 ARKit提供了一些方法来执行此操作,但是它们是附加到ARCamera类的,但是不幸的是,在我的场景中,我只能访问摄像机的投影,视图和变换矩阵,而没有引用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中,他们将此值称为modelView矩阵,在glm中它仅称为model,因此我对应该是什么感到有些困惑。
使用从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函数返回的值。我认为那只是世界坐标,但也许需要以某种方式进行缩放?
我希望获得一些帮助以获取正确的坐标,或者如果上面有任何错误,请指出来!
答案 0 :(得分:1)
For anyone who runs across this in the future, the solution (as suggested by @rabbid76) was flipping the order of multiplication of my projection and view matrices, as well as my inverse matrix and the inPoint. I also needed to flip the y coord of the screenPoint I wanted to check.
Here's the complete function:
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)
}