首页 文章

在 ARKit 中将屏幕点取消投影到水平面

提问于
浏览
1

我正在尝试使用 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)

我不确定的唯一一个是 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 函数返回的值。我认为这只是世界坐标,但也许需要以某种方式进行缩放?

我希望获得一些帮助以获取正确的坐标,或者如果上面有任何错误,请指出来!

1 回答

  • 1

    对于以后遇到此问题的任何人,解决方案(如@rabbid76 所建议)正在翻转我的投影矩阵和视图矩阵,逆矩阵和 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)
    }
    

相关问题