该项目的目标是创建一个绘图应用程序。我想要它,以便当我触摸屏幕并移动我的手指时,它将跟随手指并留下青色涂料。我确实创造了但是有一个问题。油漆深度总是随机放置。
这里是代码,只需要将sceneView与storyboard连接起来。 https://github.com/javaplanet17/test/blob/master/drawingar
我的问题是如何制作程序,使深度始终保持一致,一致,我的意思是油漆和相机之间总是有距离。
如果您运行上面的代码,您将看到我已经打印出所有SCNMatrix4,但我没有一个是深度。
我试图改变hitTransform.m43
,但它只会弄乱x和y。
答案 0 :(得分:3)
如果你想让相机前面的距离保持一定距离,你就不需要进行命中测试了。命中测试可以在相机前找到真实世界的表面 - 除非您的相机指向与设备屏幕完全平行的墙壁,否则您将始终获得一系列不同的距离。
如果您希望相机前方有一个距离,则需要获取相机的位置/方向并应用平移(您的首选距离)。然后将SceneKit内容放在那里,使用结果矩阵设置SceneKit节点的变换。
最简单的方法是始终坚持使用SIMD矢量/矩阵类型,而不是在这些类型和SCN类型之间进行转换。 SceneKit在iOS 11中添加了一堆新的访问器,因此您可以直接使用SIMD类型。
根据你想要的结果,至少有几种方法可以解决这个问题。
// set up z translation for 20 cm in front of whatever
// last column of a 4x4 transform matrix is translation vector
var translation = matrix_identity_float4x4
translation.columns.3.z = -0.2
// get camera transform the ARKit way
let cameraTransform = view.session.currentFrame.camera.transform
// if we wanted, we could go the SceneKit way instead; result is the same
// let cameraTransform = view.pointOfView.simdTransform
// set node transform by multiplying matrices
node.simdTransform = cameraTransform * translation
此选项使用整个变换矩阵,不仅可以使节点在相机前方保持一致的距离,还可以使其指向与相机相同的方向。
// distance vector for 20 cm in front of whatever
let translation = float3(x: 0, y: 0, z: -0.2)
// treat distance vector as in camera space, convert to world space
let worldTranslation = view.pointOfView.simdConvertPosition(translation, to: nil)
// set node position (not whole transform)
node.simdPosition = worldTranslation
此选项仅设置节点的位置,保持其方向不变。例如,如果你在移动相机的同时以这种方式放置一堆立方体,它们将面向相同的方向排列,而选项1则它们都在不同的方向。
上述两个选项仅基于相机的3D变换 - 它们不会考虑屏幕上的2D触摸位置。
如果你想这样做,你还有更多的工作要做 - 基本上你正在做的是打击测试触摸而不是对抗世界,而是针对一个始终与摄像机平行的虚拟平面离一定距离。该平面是相机投影平截头体的横截面,因此其大小取决于放置相机的固定距离。屏幕上的一个点投影到该虚拟平面上的某个点,其在平面上的位置与相机的距离成比例缩放(如下图所示):
因此,要将触摸映射到该虚拟平面,需要考虑几种方法。 (没有为这些代码提供代码,因为它不是我可以在没有测试的情况下编写的代码,而且我现在处于无Xcode的环境中。)
创建一个不可见的SCNPlane
,它是视图pointOfView
节点的子节点,与本地xy平面平行,前面有一些固定的z距离。使用SceneKit hitTest
(不是ARKit命中测试!)将触摸映射到该平面,并使用命中测试结果的worldCoordinates
来定位放入场景中的SceneKit节点。
使用上面的选项1或选项2在相机前面找到一个固定距离的点(或者是一个与相机匹配的整个平移矩阵,在前面翻译一段距离)。使用SceneKit的projectPoint
方法查找该点的标准化深度值 Z ,然后使用您的2D触摸位置和相同的 Z 值调用unprojectPoint
使用相机距离获取触摸位置的3D位置。 (有关额外的代码/指针,请参阅this answer中的类似技巧。)