我正在尝试一个AR应用程序。我尝试做以下几点:
iOS设备通过ARView
显示真实的场景,ARView
创建网格。
每当网格更新时,我都想找到最靠近相机的顶点,并将虚拟对象附加到它(在删除可能先前附加的对象之后)。
我不确定我现在的代码是否正确,但它似乎做了我上面描述的事情。如果移动设备,显示的网格将更新。当找到一个新的最近顶点时,它会在MainView
中切换一个状态变量refreshToggle
,我希望更新后的网格与虚拟对象一起显示。但虚拟对象没有显示,我不明白为什么。
这是我的代码。我很抱歉它这么长,但我不知道该省略什么。任何帮助都欢迎!
struct MainView : View {
@State private var refreshToggle = false // Toggled, when a new closest anchor is found
var body: some View {
ARViewContainer(refreshToggle: $refreshToggle).edgesIgnoringSafeArea(.all)
}
}
struct ARViewContainer: UIViewRepresentable {
@Binding var refreshToggle: Bool
func makeUIView(context: Context) -> ARView {
let arView = ARView(frame: .zero)
arView.environment.sceneUnderstanding.options = []
arView.environment.sceneUnderstanding.options.insert(.occlusion) // Turn on occlusion from the scene reconstruction's mesh.
arView.environment.sceneUnderstanding.options.insert(.physics) // Turn on physics for the scene reconstruction's mesh.
arView.debugOptions.insert(.showSceneUnderstanding) // Display a debug visualization of the mesh.
arView.renderOptions = [.disablePersonOcclusion, .disableDepthOfField, .disableMotionBlur] // Disable not required render options
arView.session.delegate = context.coordinator
return arView
}
func updateUIView(_ uiView: ARView, context: Context) {}
func makeCoordinator() -> Coordinator {
Coordinator($refreshToggle)
}
class Coordinator: NSObject, ARSessionDelegate {
@Binding var refreshToggle: Bool
var model: ModelEntity
init(_ refreshToggle: Binding<Bool>) {
self._refreshToggle = refreshToggle
// Create a cube model
let mesh = MeshResource.generateBox(size: 0.1, cornerRadius: 0.005)
let material = SimpleMaterial(color: .gray, roughness: 0.15, isMetallic: true)
model = ModelEntity(mesh: mesh, materials: [material])
}
func session(_ session: ARSession, didUpdate anchors: [ARAnchor]) {
var closestAnchor: ARAnchor? = nil
for anchor in anchors {
if let meshAnchor = anchor as? ARMeshAnchor {
let meshGeometry = meshAnchor.geometry
let vertices = meshGeometry.vertices
// Search for the vertex closest to the camera and place there a virtual marker object
let nrVertices = vertices.count
var closestVertex = SIMD3<Float>(x: 0, y: .infinity, z: 0)
for i in 0 ..< nrVertices {
let nextVertex = meshGeometry.vertex(at: UInt32(i))
if nextVertex.y < closestVertex.y {
closestVertex = nextVertex
if closestAnchor?.identifier != meshAnchor.identifier {
// A new closest anchor has been found. Remove a virtual marker object
if let closestAnchor = closestAnchor {
let anchor = AnchorEntity(anchor: closestAnchor)
anchor.children.remove(model)
}
}
closestAnchor = meshAnchor
}
}
// If a closest vertex was found, attach a virtual object to it
if let closestAnchor = closestAnchor {
let anchor = AnchorEntity(anchor: closestAnchor)
anchor.children.append(model)
refreshToggle = !refreshToggle // Let ARViewContainer redisplay the real scene with the mesh and a virtual object attached to the closest anchor
}
} // if an ARMeshAnchor was found
} // for all anchors
} // session didUpdate anchors
} // coordinator
}
extension ARMeshGeometry { // See https://developer.apple.com/documentation/arkit/armeshgeometry/3516924-vertices
func vertex(at index: UInt32) -> SIMD3<Float> {
assert(vertices.format == MTLVertexFormat.float3, "Expected three floats (twelve bytes) per vertex.")
let vertexPointer = vertices.buffer.contents().advanced(by: vertices.offset + (vertices.stride * Int(index)))
let vertex = vertexPointer.assumingMemoryBound(to: SIMD3<Float>.self).pointee
return vertex
}
}
字符串
2条答案
按热度按时间zbsbpyhn1#
编辑:下面代码的解释
我很高兴解决方案为您工作!
对于那些可能不熟悉这些代码的人来说,这是使用Apple的ARKit,RealityKit和SwiftUI生成增强现实视图。ARKit是Apple用于创建增强现实体验的框架,RealityKit是基于Swift的框架,用于创建增强现实的3D内容,SwiftUI是Apple用于声明式用户界面构建的框架。
这里的主要任务是在所有检测到的ARAchor中定位“最近的”ARAchor(增强现实空间中的3D点)。最近的ARAhonor是具有最大z坐标的顶点(3D空间中的点)的ARAhonor(因为在ARKit的基础SceneKit中,z轴从相机指向场景)。
一旦找到最接近的ARAhonor,它就被用来创建AnchorEntity,这是一个RealityKit实体,它将虚拟内容与现实世界的对象或位置连接起来。ARAchor用于在增强现实空间中放置虚拟ModelEntity(在本例中为立方体)。
AnchoringComponent(.world(transform:transform))用于相对于AnchorEntity定位和定向ModelEntity。该变换矩阵中的特定值将影响AR场景中模型的位置和方向。
因此,当您在具有AR功能的设备上运行此代码并四处移动时,您应该会看到一个小立方体出现在您的环境中,位于ARKit能够检测到的“最近”(最前方)表面上。
第一部分
SwiftUI不会像常规SwiftUI视图那样刷新UIViewRepresentable类型。换句话说,当一个状态被切换时,SwiftUI会重新呈现它的主体,但它不会对UIViewRepresentable类型做同样的事情。这就是为什么您的ARView无法刷新的原因。
您需要直接在ARView示例中处理更改,并在必要时手动触发更新。
1.每次找到一个新的锚点时,似乎都在创建一个新的AnchorEntity。跟踪单个AnchorEntity示例并在需要时更新其锚点可能更好。
1.将模型附加到锚点时,应将锚点添加到ARView的scene.anchors,而不是设置refreshToggle。
1.当找到新的最近锚点时,还应该从ARView的场景中删除旧锚点。
...
字符串
现在,只要找到新的最近锚点,我们就直接更新ARView。它不会尝试刷新整个
ARViewContainer
,而是只更新AR场景的 * 相关 * 部分,这样效率更高,也避免了SwiftUI不刷新UIViewRepresentable
类型的潜在问题。kpbpu0082#
问题解决了,虽然我不明白(没有计算机图形学的经验)。
找到最近的网格锚点后,必须创建
AnchorEntity
。在这个实体中,必须设置
anchoring
属性,并使用适当的AnchoringComponent.Target
初始化AnchoringComponent
。只有这样,才在场景中渲染虚拟对象。下面的代码对我来说很有用,并且基于一些有价值的信息,KFDoom(+1)的答案,blog of Ethan Saadia和tutorial of Ralf Ebert。
下面是更新后的代码,以防有人想玩它。
.world(transform: transform)
中的转换取自不同的版本,结果证明是有用的。字符串