在 3D 世界 ARKit 中创建 UIBezierPath 形状
Create UIBezierPath shape in 3D world ARKit
我正在制作一个应用程序,用户可以通过使用 ARKit 在 3D space 上定位一些点来创建一些平面形状,但我使用这些点创建 UIBezierPath 的部分似乎有问题.
在我的应用中,用户首先在 AR 中通过按下按钮将虚拟透明墙放置在他的设备所在的同一位置:
guard let currentFrame = sceneView.session.currentFrame else {
return
}
let imagePlane = SCNPlane(width: sceneView.bounds.width, height: sceneView.bounds.height)
imagePlane.firstMaterial?.diffuse.contents = UIColor.black
imagePlane.firstMaterial?.lightingModel = .constant
var windowNode = SCNNode()
windowNode.geometry = imagePlane
sceneView.scene.rootNode.addChildNode(windowNode)
windowNode.simdTransform = currentFrame.camera.transform
windowNode.opacity = 0.1
然后,用户在墙上放置一些点(一些球体节点),通过按下按钮来确定他想要创建的平面对象的形状。如果用户指向创建的第一个球体节点,我关闭形状,创建它的节点并将其放置在与墙相同的位置:
let hitTestResult = sceneView.hitTest(self.view.center, options: nil)
if let firstHit = hitTestResult.first {
if firstHit.node == windowNode {
let x = Double(firstHit.worldCoordinates.x)
let y = Double(firstHit.worldCoordinates.y)
let pointCoordinates = CGPoint(x: x , y: y)
let sphere = SCNSphere(radius: 0.02)
sphere.firstMaterial?.diffuse.contents = UIColor.white
sphere.firstMaterial?.lightingModel = .constant
let sphereNode = SCNNode(geometry: sphere)
sceneView.scene.rootNode.addChildNode(sphereNode)
sphereNode.worldPosition = firstHit.worldCoordinates
if points.isEmpty {
windowPath.move(to: pointCoordinates)
} else {
windowPath.addLine(to: pointCoordinates)
}
points.append(sphereNode)
if undoButton.alpha == 0 {
undoButton.alpha = 1
}
} else if firstHit.node == points.first {
windowPath.close()
let windowShape = SCNShape(path: windowPath, extrusionDepth: 0)
windowShape.firstMaterial?.diffuse.contents = UIColor.white
windowShape.firstMaterial?.lightingModel = .constant
let tintedWindow = SCNNode(geometry: windowShape)
let worldPosition = windowNode.worldPosition
tintedWindow.worldPosition = worldPosition
sceneView.scene.rootNode.addChildNode(tintedWindow)
//removing all the sphere nodes from points and reinitializing the UIBezierPath windowPath
removeAllPoints()
}
}
当我创建第一面不可见的墙和第一个形状时,该代码有效,但是当我创建第二面墙时,当我完成绘制我的形状时,该形状似乎变形并且不在正确的位置就像真的不在正确的地方一样。所以我认为我的 UIBezierPath 点的坐标丢失了一些东西,但是什么?
编辑
好的,经过多次测试,这似乎取决于启动 AR 会话时设备的方向。当设备在启动时面向用户将创建的第一面墙时,将创建形状并按预期放置。但是,例如,如果用户在他的设备指向一个方向的情况下启动应用程序,然后将自己旋转 90 度,放置第一面墙并创建他的形状,该形状将变形并且不在正确的位置。
看来是3D坐标的问题,我还是没弄明白
好的,我刚刚找到问题了!我只是使用了错误的矢量和坐标...我从来都不是 math/geometry 人哈哈
所以不要使用:
let x = Double(firstHit.worldCoordinates.x)
let y = Double(firstHit.worldCoordinates.y)
我现在使用:
let x = Double(firstHit.localCoordinates.x)
let y = Double(firstHit.localCoordinates.y)
而不是使用:
let worldPosition = windowNode.worldPosition
我现在使用:
let worldPosition = windowNode.transform
这就是为什么我的形状节点的位置取决于 AR 会话的初始化,我正在使用世界坐标,现在对我来说似乎很明显。
我正在制作一个应用程序,用户可以通过使用 ARKit 在 3D space 上定位一些点来创建一些平面形状,但我使用这些点创建 UIBezierPath 的部分似乎有问题.
在我的应用中,用户首先在 AR 中通过按下按钮将虚拟透明墙放置在他的设备所在的同一位置:
guard let currentFrame = sceneView.session.currentFrame else {
return
}
let imagePlane = SCNPlane(width: sceneView.bounds.width, height: sceneView.bounds.height)
imagePlane.firstMaterial?.diffuse.contents = UIColor.black
imagePlane.firstMaterial?.lightingModel = .constant
var windowNode = SCNNode()
windowNode.geometry = imagePlane
sceneView.scene.rootNode.addChildNode(windowNode)
windowNode.simdTransform = currentFrame.camera.transform
windowNode.opacity = 0.1
然后,用户在墙上放置一些点(一些球体节点),通过按下按钮来确定他想要创建的平面对象的形状。如果用户指向创建的第一个球体节点,我关闭形状,创建它的节点并将其放置在与墙相同的位置:
let hitTestResult = sceneView.hitTest(self.view.center, options: nil)
if let firstHit = hitTestResult.first {
if firstHit.node == windowNode {
let x = Double(firstHit.worldCoordinates.x)
let y = Double(firstHit.worldCoordinates.y)
let pointCoordinates = CGPoint(x: x , y: y)
let sphere = SCNSphere(radius: 0.02)
sphere.firstMaterial?.diffuse.contents = UIColor.white
sphere.firstMaterial?.lightingModel = .constant
let sphereNode = SCNNode(geometry: sphere)
sceneView.scene.rootNode.addChildNode(sphereNode)
sphereNode.worldPosition = firstHit.worldCoordinates
if points.isEmpty {
windowPath.move(to: pointCoordinates)
} else {
windowPath.addLine(to: pointCoordinates)
}
points.append(sphereNode)
if undoButton.alpha == 0 {
undoButton.alpha = 1
}
} else if firstHit.node == points.first {
windowPath.close()
let windowShape = SCNShape(path: windowPath, extrusionDepth: 0)
windowShape.firstMaterial?.diffuse.contents = UIColor.white
windowShape.firstMaterial?.lightingModel = .constant
let tintedWindow = SCNNode(geometry: windowShape)
let worldPosition = windowNode.worldPosition
tintedWindow.worldPosition = worldPosition
sceneView.scene.rootNode.addChildNode(tintedWindow)
//removing all the sphere nodes from points and reinitializing the UIBezierPath windowPath
removeAllPoints()
}
}
当我创建第一面不可见的墙和第一个形状时,该代码有效,但是当我创建第二面墙时,当我完成绘制我的形状时,该形状似乎变形并且不在正确的位置就像真的不在正确的地方一样。所以我认为我的 UIBezierPath 点的坐标丢失了一些东西,但是什么?
编辑
好的,经过多次测试,这似乎取决于启动 AR 会话时设备的方向。当设备在启动时面向用户将创建的第一面墙时,将创建形状并按预期放置。但是,例如,如果用户在他的设备指向一个方向的情况下启动应用程序,然后将自己旋转 90 度,放置第一面墙并创建他的形状,该形状将变形并且不在正确的位置。
看来是3D坐标的问题,我还是没弄明白
好的,我刚刚找到问题了!我只是使用了错误的矢量和坐标...我从来都不是 math/geometry 人哈哈
所以不要使用:
let x = Double(firstHit.worldCoordinates.x)
let y = Double(firstHit.worldCoordinates.y)
我现在使用:
let x = Double(firstHit.localCoordinates.x)
let y = Double(firstHit.localCoordinates.y)
而不是使用:
let worldPosition = windowNode.worldPosition
我现在使用:
let worldPosition = windowNode.transform
这就是为什么我的形状节点的位置取决于 AR 会话的初始化,我正在使用世界坐标,现在对我来说似乎很明显。