Я работаю над проектом IOS с ARKit, в котором я пытаюсь получить мировую координату (3D) из экранной 2D-координаты.Я знаю, что мы можем легко получить это, используя непроектированную функцию SCNView или ARSCNView.но по некоторым другим причинам мне нужно получить координаты мира, используя только ARCamera и Screen Coordinates.Из интернета я нашел некоторые решения о том, как получить мировые координаты, используя матрицы просмотра и проекции камеры и точки экрана.Я почти близок к решению, но по какой-то причине значение X имеет некоторое смещение.
Ниже приведена реализация функции Unproject, которую я использую с https://stackoverflow.com/a/47897206/9719840
func unproject(screenPoint: float3, // see below for Z depth hint discussion
modelView: float4x4,
projection: float4x4,
viewport: CGRect) -> float3 {
// viewport to clip: subtract viewport origin, divide by size,
// scale/offset from 0...1 to -1...1 coordinate space
var clip = (screenPoint - float3(Float(viewport.minX), Float(viewport.minY), 1.0))
/ float3(Float(viewport.width), Float(viewport.height), 1.0)
* float3(repeating: 2)
clip = float3.init(clip.y - 1, clip.x - 1 , clip.z - 1)
print("clip: \(clip)")
// apply the reverse of the model-view-projection transform
let inversePM = (projection * modelView).inverse
let result = inversePM * float4(clip.x, clip.y, clip.z, 1.0)
return float3(result.x, result.y, result.z) / result.w // perspective divide
}
И вот как я вызываю вышеуказанную функцию
func screenToWorld(from point: CGPoint, camera:ARCamera){
let farPoint = unproject(screenPoint: float3.init(Float(point.x), Float(point.y), 1), modelView: camera.transform.inverse, projection: camera.projectionMatrix, viewport: self.arView.frame)
let nearPoint = unproject(screenPoint: float3.init(Float(point.x), Float(point.y), 0), modelView: camera.transform.inverse, projection: camera.projectionMatrix, viewport: self.arView.frame)
var worldPoint = SCNVector3(farPoint.x - nearPoint.x, farPoint.y - nearPoint.y, farPoint.z - nearPoint.z).normalized()
let camPos = lastFrame.camera.transform * float4(x: 0, y: 0, z: 0, w: 1)
let camPosition = SCNVector3(camPos.x, camPos.y, camPos.z)
//add campos to the result
worldPoint = SCNVector3(worldPoint.x + camPosition.x , worldPoint.y + camPosition.y , worldPoint.z + camPosition.z )
// adding a cube at the tapped position for debugging
let anchor = ARAnchor.init(name: "str", transform: float4x4(float4(1, 0, 0, 0),
float4(0, 1, 0, 0),
float4(0, 0, 1, 0),
float4(worldPoint.x,
worldPoint.y,
worldPoint.z,
1)))
self.arView.session.add(anchor: anchor)
}
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let touch = touches.first else { return }
let touchPoint = touch.location(in: self.arView)
if let frame = arView.session.currentFrame{
initPoint = touchPoint
savedFrame = frame
screenToWorld(from: touchPoint, camera: frame.camera)
}
}
Используя приведенный выше код, я могу размещать кубы там, где я касаюсь экрана, но есть некоторое смещение / смещение в позиции x.(например, если я коснусь рядом с центром, куб будет располагаться хорошо, но если я поставлю его рядом с углами, он будет расположен немного дальше вдоль оси x)