3 回答
TA贡献1796条经验 获得超10个赞
3D图形管线中的典型深度缓冲区不是线性的。透视划分导致归一化设备坐标中的深度处于不同的比例。(另请参见此处。)
因此,您要输入的z坐标unprojectPoint实际上并不是您想要的。
那么,如何找到与世界空间中的平面匹配的归一化深度坐标呢?好吧,如果该平面与相机正交(这就是您的相机)是有帮助的。然后,您要做的就是在该平面上投影一个点:
let projectedOrigin = gameView.projectPoint(SCNVector3Zero)
现在,您可以在3D视图+归一化深度空间中找到世界原点的位置。要将2D视图空间中的其他点映射到该平面上,请使用此向量的z坐标:
let vp = gestureRecognizer.locationInView(scnView)
let vpWithZ = SCNVector3(x: vp.x, y: vp.y, z: projectedOrigin.z)
let worldPoint = gameView.unprojectPoint(vpWithZ)
这样可以在世界空间中获得一个点,该点将单击/点击位置映射到z = 0平面,position如果要向用户显示该位置,则适合用作节点的位置。
(请注意,此方法仅在映射到垂直于相机视线方向的平面上才有效。如果要将视线坐标映射到不同方向的表面上,则in中的归一化深度值vpWithZ将不会恒定)
TA贡献1828条经验 获得超6个赞
经过一些实验后,我们开发出了将触摸点投影到场景中给定点任意深度的工具。
您需要进行的修改是计算Z = 0平面与此线的交点,这就是您的观点。
private func touchPointToScenePoint(recognizer: UIGestureRecognizer) -> SCNVector3 {
// Get touch point
let touchPoint = recognizer.locationInView(sceneView)
// Compute near & far points
let nearVector = SCNVector3(x: Float(touchPoint.x), y: Float(touchPoint.y), z: 0)
let nearScenePoint = sceneView.unprojectPoint(nearVector)
let farVector = SCNVector3(x: Float(touchPoint.x), y: Float(touchPoint.y), z: 1)
let farScenePoint = sceneView.unprojectPoint(farVector)
// Compute view vector
let viewVector = SCNVector3(x: Float(farScenePoint.x - nearScenePoint.x), y: Float(farScenePoint.y - nearScenePoint.y), z: Float(farScenePoint.z - nearScenePoint.z))
// Normalize view vector
let vectorLength = sqrt(viewVector.x*viewVector.x + viewVector.y*viewVector.y + viewVector.z*viewVector.z)
let normalizedViewVector = SCNVector3(x: viewVector.x/vectorLength, y: viewVector.y/vectorLength, z: viewVector.z/vectorLength)
// Scale normalized vector to find scene point
let scale = Float(15)
let scenePoint = SCNVector3(x: normalizedViewVector.x*scale, y: normalizedViewVector.y*scale, z: normalizedViewVector.z*scale)
print("2D point: \(touchPoint). 3D point: \(nearScenePoint). Far point: \(farScenePoint). scene point: \(scenePoint)")
// Return <scenePoint>
return scenePoint
}
- 3 回答
- 0 关注
- 840 浏览
添加回答
举报