본문 바로가기
iOS/Graphics and Games

[ARKit] Sample Code: Placing Objects and Handling 3D Interaction

by 탄이. 2020. 6. 21.

https://developer.apple.com/documentation/arkit/placing_objects_and_handling_3d_interaction

 

Placing Objects and Handling 3D Interaction | Apple Developer Documentation

Sample Code Placing Objects and Handling 3D Interaction Place virtual content at tracked, real-world locations, and enable the user to interact with virtual content by using gestures. Download OverviewThe key facet of an AR experience is the ability to int

developer.apple.com

가상 컨텐츠 배치

  • 사용자에게 가상 컨텐츠를 배치할 수 있는 위치에 대한 아이디어를 제공하려면 환경에 주석을 달아 미리 보기를 제공하십시오.

  • 샘플 앱은 사용자가 ARKit이 인지하고 있는 표면의 모양과 정렬을 시각적으로 확인할 수 있는 정사각형을 그린다.

  • 실제 세계에서 사각형을 어디에 둘지 알아보려면 [ARRaycastQuery](https://developer.apple.com/documentation/arkit/arraycastquery)를 사용하여 ARKit에 실제 표면이 어디에 있는지 묻습니다.

  • 먼저 관심 있는 화면에서 2D 지점을 정의하는 레이캐스트 쿼리를 생성하십시오.

  • 초점 사각형이 화면 중앙에 정렬되므로 화면 중앙에 대한 쿼리를 만듭니다.

func getRaycastQuery(for alignment: ARRaycastQuery.TargetAlignment = .any) -> ARRaycastQuery? {
    return raycastQuery(from: screenCenter, allowing: .estimatedPlane, alignment: alignment)
}

그런 다음 세션에 캐스팅을 요청하여 레이캐스트 쿼리를 실행하십시오.

func castRay(for query: ARRaycastQuery) -> [ARRaycastResult] {
    return session.raycast(query)
}
  • ARKit는 results 매개변수에 해당 점이 실제의 표면에 있는 깊이를 포함하는 위치를 반환한다.
  • 사용자가 가상 컨텐츠를 배치할 수 있는 실제 위치에 대한 미리 보기를 사용자에게 제공하려면, 레이캐스트 결과의 [worldTransform](https://developer.apple.com/documentation/arkit/arraycastresult/3132062-worldtransform)을 사용하여 포커스 사각형의 위치를 업데이트하십시오.
func setPosition(with raycastResult: ARRaycastResult, _ camera: ARCamera?) {
    let position = raycastResult.worldTransform.translation
    recentFocusSquarePositions.append(position)
    updateTransform(for: raycastResult, camera: camera)
}
  • 레이캐스트 결과는 표면이 중력에 대해 각도를 이루는 방법 또한 나타냅니다.
  • 사용자의 가상 컨텐츠가 표면에 놓일 수있는 각도를 미리 보려면 결과 방향으로 초점 사각형의 [simdWorldTransform](https://developer.apple.com/documentation/scenekit/scnnode/2881868-simdworldtransform)을 업데이트하십시오.

simWorldTransform
월드 변환은 scene의 좌표 공간에 대한 노드의 좌표 공간 변환이다. 이 변환은 노드의 simdTransform 속성과 상위 노드, 상위 노드 및 씬(scene)의 rootNode 객체까지 결합한 것이다.

func updateOrientation(basedOn raycastResult: ARRaycastResult) {
    self.simdOrientation = raycastResult.worldTransform.orientation
}
  • 앱에서 다양한 유형의 가상 콘텐츠를 제공하는 경우 사용자에게 선택할 수있는 인터페이스를 제공하십시오.
  • 샘플 앱은 사용자가 더하기 버튼을 누를 때 선택 메뉴를 표시합니다.
  • 사용자가 목록에서 항목을 선택하면 해당 3D 모델을 인스턴스화하고 초점 사각형의 현재 위치에서 월드에 고정합니다.
func placeVirtualObject(_ virtualObject: VirtualObject) {
    guard focusSquare.state != .initializing, let query = virtualObject.raycastQuery else {
        self.statusViewController.showMessage("CANNOT PLACE OBJECT\nTry moving left or right.")
        if let controller = self.objectsViewController {
            self.virtualObjectSelectionViewController(controller, didDeselectObject: virtualObject)
        }
        return
    }

    let trackedRaycast = createTrackedRaycastAndSet3DPosition(of: virtualObject, from: query,
                                                              withInitialResult: virtualObject.mostRecentInitialPlacementResult)

    virtualObject.raycast = trackedRaycast
    virtualObjectInteraction.selectedObject = virtualObject
    virtualObject.isHidden = false
}

시간 경과에 따른 가상 컨텐츠의 위치 개선

  • 세션이 실행되면서 ARKit은 각 카메라 이미지를 분석하고 물리적 환경의 레이아웃에 대해 더 많이 배웁니다.
  • ARKit이 실제 표면의 예상 크기와 위치를 업데이트할 때, 앱의 가상 컨텐츠의 위치를 일치하도록 업데이트해야 할 수도 있다.
  • ARKit은 쉽게 사용할 수 있도록 [ARTrackedRaycast](https://developer.apple.com/documentation/arkit/artrackedraycast)를 통해 scene에 대한 이해를 수정할 때 이를 알려준다.
func createTrackedRaycastAndSet3DPosition(of virtualObject: VirtualObject, from query: ARRaycastQuery,
                                          withInitialResult initialResult: ARRaycastResult? = nil) -> ARTrackedRaycast? {
    if let initialResult = initialResult {
        self.setTransform(of: virtualObject, with: initialResult)
    }

    return session.trackedRaycast(query) { (results) in
        self.setVirtualObject3DPosition(results, with: virtualObject)
    }
}
  • ARKit은 추적 광선 추적에 제공 한 쿼리를 연속적으로 반복하며 결과가 이전 결과와 다른 경우에만 제공 한 클로저를 호출합니다.
  • 클로저에 제공 한 코드는 ARKit의 업데이트 된 장면 이해에 대한 응답입니다.
  • 이 경우 업데이트 된 평면에 대해 레이 캐스트 교차점을 확인하고 해당 위치를 앱의 가상 콘텐츠에 적용합니다.
private func setVirtualObject3DPosition(_ results: [ARRaycastResult], with virtualObject: VirtualObject) {

    guard let result = results.first else {
        fatalError("Unexpected case: the update handler is always supposed to return at least one result.")
    }

    self.setTransform(of: virtualObject, with: result)

    // 가상 오브젝트가 아직 scene에 없으면 추가하십시오.
    if virtualObject.parent == nil {
        self.sceneView.scene.rootNode.addChildNode(virtualObject)
        virtualObject.shouldUpdateAnchor = true
    }

    if virtualObject.shouldUpdateAnchor {
        virtualObject.shouldUpdateAnchor = false
        self.updateQueue.async {
            self.sceneView.addOrUpdateAnchor(for: virtualObject)
        }
    }
}

Tracked Ray Casts 관리

  • ARKit은 계속해서 이들을 부르기 때문에 사용자가 더 많은 가상 컨텐츠를 배치함에 따라 추적된 레이 캐스트들이 자원을 점점 더 많이 소비할 수 있다.
  • 가상 풍선이 비행하는 경우 또는 장면에서 가상 개체를 제거할 때와 같이 더 이상 정교한 위치가 필요하지 않을 때 추적된 광선을 정지하십시오.
func removeVirtualObject(at index: Int) {
    guard loadedObjects.indices.contains(index) else { return }

        // 오브젝트의 tracked ray cast 중지
    loadedObjects[index].stopTrackedRaycast()

        // scene 그래프에서 시각적 노드를 제거하십시오.
    loadedObjects[index].removeFromParentNode()
        // 오브젝트가 할당 한 자원을 회수하십시오.
    loadedObjects[index].unload()
    loadedObjects.remove(at: index)
}
  • tracked ray cast을 중지하려면 stopTracking() 함수를 호출하십시오.
func stopTrackedRaycast() {
    raycast?.stopTracking()
    raycast = nil
}

가상 컨텐츠와 사용자 상호 작용 가능하게 하기

  • 사용자가 가상 ​​컨텐츠를 세계로 배치 한 후 이동할 수있게 하려면 pan 제스처 인식기를 구현하십시오.
func createPanGestureRecognizer(_ sceneView: VirtualObjectARView) {
    let panGesture = ThresholdPanGesture(target: self, action: #selector(didPan(_:)))
    panGesture.delegate = self
    sceneView.addGestureRecognizer(panGesture)
}
  • 사용자가 객체를 이동하면 평면에서 객체의 경로를 따라 객체의 위치를 ​​요청합니다.
  • 물체의 위치가 일시적이므로 tracked ray cast 대신에 raycast(_:) 를 사용하십시오.
  • 이 경우 이러한 요청에 대해 시간이 지남에 따라 구체화 된 위치 결과가 필요하지 않으므로 일회성 hit test가 적합합니다.
func translate(_ object: VirtualObject, basedOn screenPos: CGPoint) {
    object.stopTrackedRaycast()

    // 일회성 위치 요청을 사용하여 오브젝트를 업데이트하십시오.
    if let query = sceneView.raycastQuery(from: screenPos, allowing: .estimatedPlane, alignment: object.allowedAlignment) {
        viewController.createRaycastAndUpdate3DPosition(of: object, from: query)
    }
}
  • 레이 캐스팅은 주어진 스크린 포인트에서 표면에 대한 방향 정보를 제공합니다. 드래그하는 동안 현재 객체 회전에서 제스처 회전을 빼서 방향의 빠른 변경을 피할 수 있습니다.

트래킹 인터럽션 처리

  • 추적 조건이 좋지 않은 경우, ARKit은 delegate의 [sessionWasInterrupted(_:)](https://developer.apple.com/documentation/arkit/arsessionobserver/2891620-sessionwasinterrupted)을 호출합니다.
  • 이러한 상황에서는 카메라 피드와 관련하여 앱의 가상 컨텐츠의 위치가 정확하지 않을 수 있으므로 가상 컨텐츠를 숨기십시오.
func hideVirtualContent() {
    virtualObjectLoader.loadedObjects.forEach { $0.isHidden = true }
}
  • 추적 조건이 개선될 때 앱의 가상 컨텐츠를 복원하십시오.
  • 개선된 조건을 알리기 위해 ARKit은 ARTrackingState와 동일한 카메라 추적 상태를 전달하면서 대리인의 세션(_:cameraDidChangeTrackingState:) 기능을 호출한다.

중단되었던 AR 경험 복원

사용자가 복원 대신 다시 시작하도록 하기

댓글