https://developer.apple.com/documentation/arkit/placing_objects_and_handling_3d_interaction
가상 컨텐츠 배치
-
사용자에게 가상 컨텐츠를 배치할 수 있는 위치에 대한 아이디어를 제공하려면 환경에 주석을 달아 미리 보기를 제공하십시오.
-
샘플 앱은 사용자가 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:) 기능을 호출한다.
댓글