了解 SCNCamera 的非投影坐标
Understanding unprojecting coordinates of an SCNCamera
我正在尝试确定世界坐标 space 在 SCNCamera 的不同 Z 深度处的宽度。据我了解,Z值为0是近平面,1是远平面。
同样,我的理解是,平截头体是一个顶点在SCNCamera处的倒四方金字塔。
让我失望的是,我获得的宽度不是线性的,而是随着我接近 Z = 1 而呈指数增长。对我来说,这与我将平截头体视为正方形的心理形状相反基于金字塔。
这是游乐场示例代码:
import UIKit
import SceneKit
import PlaygroundSupport
let scene = SCNScene()
let leftCamera = SCNCamera()
let leftCameraNode = SCNNode()
let boxGeometry = SCNBox(width: 10, height: 10, length: 10, chamferRadius: 1.0)
let box = SCNNode(geometry: boxGeometry)
box.position = SCNVector3(0,0,0)
scene.rootNode.addChildNode(box)
scene.rootNode.addChildNode(leftCameraNode)
leftCameraNode.position = SCNVector3(0, 0, 50)
leftCameraNode.eulerAngles = SCNVector3(0, 0, 0)
leftCameraNode.camera = leftCamera
let leftCameraView = SCNView(frame: CGRect(x: 0, y: 0, width: 700, height: 500))
PlaygroundPage.current.liveView = leftCameraView
leftCameraView.scene = scene
leftCameraView.autoenablesDefaultLighting = true
leftCameraView.pointOfView = leftCameraNode
var widths = [CGFloat : Float]()
func widthForDepth(_ depth: CGFloat) {
let topLeftUnprojectedPoint = leftCameraView.unprojectPoint(SCNVector3(0, 0, depth))
let topRightUnprojectedPoint = leftCameraView.unprojectPoint(SCNVector3(leftCameraView.bounds.width, 0, depth))
let width = topRightUnprojectedPoint.x - topLeftUnprojectedPoint.x
widths[depth] = width
}
widthForDepth(0)
widthForDepth(0.5)
widthForDepth(0.75)
widthForDepth(0.9)
widthForDepth(0.95)
widthForDepth(1)
这让我得到了不同深度的以下值:
Z=0 -> 1.61
Z=0.5 -> 3.2
Z=0.75 -> 6.2
Z=0.9 -> 14.8
Z=0.95 -> 27.1
Z=1 -> 161.6
我的理解是 Z=1 的这个值是正确的,但是其他值对我来说似乎没有意义。
我的盒子大小为 10 个单位,适合屏幕,相机深度值为 0.5(对于 zFar 为 100 且 zNear 为 1 的相机,距离为 50 个单位)。但是,当非投影计算表明屏幕在 0.5 时为 3.2 个单位宽时,它在屏幕上完全可见。
我知道这是一个简单的问题,但我已经研究了几天,无法弄明白。
嗯,原来我原来的前提是有缺陷的。
相机深度(在远平面 Z=1 和近平面 Z=0 之间测量)与世界 Z 值不成线性比例。
因此,我发现我需要先使用projectPoint
从相机深度确定世界Z值。
我正在尝试确定世界坐标 space 在 SCNCamera 的不同 Z 深度处的宽度。据我了解,Z值为0是近平面,1是远平面。
同样,我的理解是,平截头体是一个顶点在SCNCamera处的倒四方金字塔。
让我失望的是,我获得的宽度不是线性的,而是随着我接近 Z = 1 而呈指数增长。对我来说,这与我将平截头体视为正方形的心理形状相反基于金字塔。
这是游乐场示例代码:
import UIKit
import SceneKit
import PlaygroundSupport
let scene = SCNScene()
let leftCamera = SCNCamera()
let leftCameraNode = SCNNode()
let boxGeometry = SCNBox(width: 10, height: 10, length: 10, chamferRadius: 1.0)
let box = SCNNode(geometry: boxGeometry)
box.position = SCNVector3(0,0,0)
scene.rootNode.addChildNode(box)
scene.rootNode.addChildNode(leftCameraNode)
leftCameraNode.position = SCNVector3(0, 0, 50)
leftCameraNode.eulerAngles = SCNVector3(0, 0, 0)
leftCameraNode.camera = leftCamera
let leftCameraView = SCNView(frame: CGRect(x: 0, y: 0, width: 700, height: 500))
PlaygroundPage.current.liveView = leftCameraView
leftCameraView.scene = scene
leftCameraView.autoenablesDefaultLighting = true
leftCameraView.pointOfView = leftCameraNode
var widths = [CGFloat : Float]()
func widthForDepth(_ depth: CGFloat) {
let topLeftUnprojectedPoint = leftCameraView.unprojectPoint(SCNVector3(0, 0, depth))
let topRightUnprojectedPoint = leftCameraView.unprojectPoint(SCNVector3(leftCameraView.bounds.width, 0, depth))
let width = topRightUnprojectedPoint.x - topLeftUnprojectedPoint.x
widths[depth] = width
}
widthForDepth(0)
widthForDepth(0.5)
widthForDepth(0.75)
widthForDepth(0.9)
widthForDepth(0.95)
widthForDepth(1)
这让我得到了不同深度的以下值: Z=0 -> 1.61
Z=0.5 -> 3.2
Z=0.75 -> 6.2
Z=0.9 -> 14.8
Z=0.95 -> 27.1
Z=1 -> 161.6
我的理解是 Z=1 的这个值是正确的,但是其他值对我来说似乎没有意义。
我的盒子大小为 10 个单位,适合屏幕,相机深度值为 0.5(对于 zFar 为 100 且 zNear 为 1 的相机,距离为 50 个单位)。但是,当非投影计算表明屏幕在 0.5 时为 3.2 个单位宽时,它在屏幕上完全可见。
我知道这是一个简单的问题,但我已经研究了几天,无法弄明白。
嗯,原来我原来的前提是有缺陷的。
相机深度(在远平面 Z=1 和近平面 Z=0 之间测量)与世界 Z 值不成线性比例。
因此,我发现我需要先使用projectPoint
从相机深度确定世界Z值。