我想在用户点击屏幕时创建一个新的SceneKit节点,并让它以预定距离直接出现在摄像机前面.对于测试,这将是一个SCNText读取"你在这里点击"的读数.它也应该与视线成直角 - 也就是说,"面向相机".
那么,鉴于self.camera.orientation
SCNVector4(或类似),我该如何:
我怀疑(2)的答案是它与`self.camera.orientation相同?但是(1)让我有点失落.
我看到了许多类似的问题,比如这个,但没有答案.
ric*_*ter 28
你不需要这么多的数学 - 这是SceneKit实际上已经做的所有数学运算,所以你只需要问它正确的结果.
[我如何]生成SCNVector3,将节点置于摄像机的"前方"给定距离?
在托管摄像机的节点的本地坐标系中,"前"是-Z方向.如果你把东西放在(0, 0, -10)
相机的相对位置,它就会直接位于相机的视线中心,距离10个单位.有两种方法可以做到这一点:
只需将文本节点作为摄像机的子节点即可.
cameraNode.addChildNode(textNode)
textNode.position = SCNVector3(x: 0, y: 0, z: -10)
Run Code Online (Sandbox Code Playgroud)
请注意,这也使其与相机一起移动:如果相机改变方向或位置,textNode
则为骑行而来.(想象一下反向自拍杆 - 在一端转动手机,另一端用它摆动.)
由于文本节点位于摄像机节点空间,因此您可能不需要任何其他内容来使其面向摄像机.(SCNText
相对于其父空间的相对默认方向使文本面向相机.)
(iOS 11/macOS 10.13/Xcode 9等中的新功能)SceneKit现在包含一些便捷的访问器和方法,可以用更少的步骤进行各种相对几何数学运算.大多数这些属性和方法都有新的风格,支持系统范围的SIMD库,而不是使用SCN向量/矩阵类型 - 而SIMD库具有很好的功能,如内置的重载运算符.将它们放在一起,这样的操作就变成了一个班轮:
textNode.simdPosition = camera.simdWorldFront * distance
Run Code Online (Sandbox Code Playgroud)
(simdWorldFront
是一个长度为1.0的向量,所以你可以通过缩放来改变距离.)
如果您仍然支持iOS 10/macOS 10.12/etc,您仍然可以使用节点层次结构进行坐标转换.文本节点可以是场景的子节点(rootNode
实际上是场景),但是您可以找到与所需的摄像机空间坐标对应的场景坐标空间位置.你真的不需要它的功能,但这可能会有所作为:
func sceneSpacePosition(inFrontOf node: SCNNode, atDistance distance: Float) -> SCNVector3 {
let localPosition = SCNVector3(x: 0, y: 0, z: CGFloat(-distance))
let scenePosition = node.convertPosition(localPosition, to: nil)
// to: nil is automatically scene space
return scenePosition
}
textNode.position = sceneSpacePosition(inFrontOf: camera, atDistance: 3)
Run Code Online (Sandbox Code Playgroud)
在这种情况下,文本节点最初相对于摄像机定位,但不与其保持"连接".如果移动相机,节点将保持原样.由于文本节点位于场景空间中,因此其方向也未连接到摄像机.
[我如何]生成SCNVector4(或SCNQuaternion),它定向节点以使其"面向"相机?
这部分你要么不需要做太多工作,要么可以完全交给SceneKit,具体取决于你使用的第一个问题的答案.
如果你做了上述(1) - 使文本节点成为相机节点的子节点 - 使其面向相机是一步一步的事情.正如我所指出的那样,SCNText
作为您的测试几何体,该步骤已经完成.在其局部坐标空间中,a SCNText
是直立且可从默认摄像机方向读取(即,沿-Z方向查看,+ Y向上,+ X向右).
如果您想要面向相机的某些其他几何体,则只需将其定向一次,但您必须确定适当的方向.例如,a SCNPyramid
的点位于+ Y方向,因此如果您希望该点面向相机(注意,它是锐利的),您将需要围绕其X轴旋转它.pi/2
.(你可以做到这一点eulerAngles
,rotation
,orientation
或pivot
).
因为它是相机的孩子,所以你给它的任何旋转都将相对于相机,所以如果相机移动它将保持指向相机.
如果您执行了上述(2),则可以尝试在场景空间中计算出面对摄像机所需的旋转,并在节点或摄像机移动时应用该旋转.但是有一个API可以帮助你.实际上,两个API:SCNLookAtConstraint
使任何节点"面向"任何其他节点(由受约束节点的-Z方向确定),并且SCNBillboardConstraint
有点相同,但有一些额外的考虑因素,假设您不想面对只是其他一些节点,但特别是相机.
只有默认选项才能很好地工作:
textNode.constraints = [ SCNBillboardConstraint() ]
Run Code Online (Sandbox Code Playgroud)如果您使用的sceneSpacePosition
功能之上,你的相机节点的方向设置在渲染时-也就是说,你不设置transform
,eulerAngles
,rotation
,或者orientation
直接就可以了,而是使用限制,动作,动画,或物理移动/定位相机 - 还有一件事需要考虑.
SceneKit实际上在任何给定时间都有两个节点层次结构:"模型"树和"表示"树.模型树是直接设置transform
(etc)的功能.所有其他移动/定向节点的方法都使用表示树.因此,如果您想使用相机相对空间计算位置,则需要相机的演示节点:
textNode.position = sceneSpacePosition(inFrontOf: camera.presentation, atDistance: 3)
~~~~~~~~~~~~~
Run Code Online (Sandbox Code Playgroud)
(为什么会这样?除此之外,你可以拥有隐式动画:设置position
在一个事务中,它将从旧位置动画到新位置.在那个动画中,模型position
是你设置的,并且演示节点不断改变它position
.)
Pab*_*blo 14
(斯威夫特4)
嘿,如果要将对象放在相对于另一个节点(例如摄像机节点)的某个位置,并且与参考节点的方向相同,则可以使用这个更简单的函数:
func updatePositionAndOrientationOf(_ node: SCNNode, withPosition position: SCNVector3, relativeTo referenceNode: SCNNode) {
let referenceNodeTransform = matrix_float4x4(referenceNode.transform)
// Setup a translation matrix with the desired position
var translationMatrix = matrix_identity_float4x4
translationMatrix.columns.3.x = position.x
translationMatrix.columns.3.y = position.y
translationMatrix.columns.3.z = position.z
// Combine the configured translation matrix with the referenceNode's transform to get the desired position AND orientation
let updatedTransform = matrix_multiply(referenceNodeTransform, translationMatrix)
node.transform = SCNMatrix4(updatedTransform)
}
Run Code Online (Sandbox Code Playgroud)
如果你想把'node'2m放在某个'cameraNode'前面,你可以这样称呼:
let position = SCNVector3(x: 0, y: 0, z: -2)
updatePositionAndOrientationOf(node, withPosition: position, relativeTo: cameraNode)
Run Code Online (Sandbox Code Playgroud)
编辑:获取摄像头节点
要获取摄像头节点,取决于您是否使用SCNKit,ARKit或其他框架.以下是ARKit和SceneKit的示例.
使用ARKit,您可以使用ARSCNView渲染与摄像机内容重叠的SCNScene的3D对象.您可以从ARSCNView的pointOfView属性获取相机节点:
let cameraNode = sceneView.pointOfView
Run Code Online (Sandbox Code Playgroud)
对于SceneKit,您有一个SCNView渲染SCNScene的3D对象.您可以创建摄像机节点并将它们放置在任何您想要的位置,因此您可以执行以下操作:
let scnScene = SCNScene()
// (Configure scnScene here if necessary)
scnView.scene = scnScene
let cameraNode = SCNNode()
cameraNode.camera = SCNCamera()
cameraNode.position = SCNVector3(0, 5, 10) // For example
scnScene.rootNode.addChildNode(cameraNode)
Run Code Online (Sandbox Code Playgroud)
设置摄像机节点后,您可以像访问ARKit一样访问当前摄像机:
let cameraNode = scnView.pointOfView
Run Code Online (Sandbox Code Playgroud)
Mau*_*itz 13
我通过拼凑其他几个答案得到它...从这些方法开始:
func pointInFrontOfPoint(point: SCNVector3, direction: SCNVector3, distance: Float) -> SCNVector3 {
var x = Float()
var y = Float()
var z = Float()
x = point.x + distance * direction.x
y = point.y + distance * direction.y
z = point.z + distance * direction.z
let result = SCNVector3Make(x, y, z)
return result
}
func calculateCameraDirection(cameraNode: SCNNode) -> SCNVector3 {
let x = -cameraNode.rotation.x
let y = -cameraNode.rotation.y
let z = -cameraNode.rotation.z
let w = cameraNode.rotation.w
let cameraRotationMatrix = GLKMatrix3Make(cos(w) + pow(x, 2) * (1 - cos(w)),
x * y * (1 - cos(w)) - z * sin(w),
x * z * (1 - cos(w)) + y*sin(w),
y*x*(1-cos(w)) + z*sin(w),
cos(w) + pow(y, 2) * (1 - cos(w)),
y*z*(1-cos(w)) - x*sin(w),
z*x*(1 - cos(w)) - y*sin(w),
z*y*(1 - cos(w)) + x*sin(w),
cos(w) + pow(z, 2) * ( 1 - cos(w)))
let cameraDirection = GLKMatrix3MultiplyVector3(cameraRotationMatrix, GLKVector3Make(0.0, 0.0, -1.0))
return SCNVector3FromGLKVector3(cameraDirection)
}
Run Code Online (Sandbox Code Playgroud)
现在你可以像这样调用它:
let rectnode = SCNNode(geometry: rectangle)
let dir = calculateCameraDirection(cameraNode: self.camera)
let pos = pointInFrontOfPoint(point: self.camera.position, direction:dir, distance: 18)
rectnode.position = pos
rectnode.orientation = self.camera.orientation
sceneView.scene?.rootNode.addChildNode(rectnode)
Run Code Online (Sandbox Code Playgroud)
距离18将其定位在半径为10的球体上的网格和25处的背景图像之间.它看起来非常好并且我的5s上的性能非常好.
归档时间: |
|
查看次数: |
17026 次 |
最近记录: |