apple/VisionOS, ARKit

[ARKit] #1 ARKit 시작하기

lgvv 2023. 8. 12. 01:18

#1 ARKit 시작하기

 

VisionOS를 공부하면서 ARKit부터 차근히 쌓아야 할 것 같아서 정리합니다.

 

 

# 프로젝트 환경

 - Xcode 15.0 beta 3

 - iOS 17.0 +

 - UIKit

 - SnapKit, RxSwift

 

# 프로젝트 생성


SwiftUI와 UIKit중 UIKit으로 구성

 

UIKit 자료가 더욱 풍부하며 UIKit으로 학습해도 추후에 SwiftUI로 뷰만 래핑해서 사용하거나 혹은 SDK형태로 기능 구현을 유지한 채 SwiftUI로 UI만 다시 작성할 수 있기에 학습 측면에서 UIKit이 더 낫다는 판단.

 

Augmented Reality App

 

프로젝트 구성

 

 

# 프로젝트 파일 설명

import UIKit
import SceneKit
import ARKit

class ViewController: UIViewController, ARSCNViewDelegate {

    @IBOutlet var sceneView: ARSCNView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // 딜리게이트 연결
        sceneView.delegate = self
        
        // 통계 정보를 보여줄지 말지
        sceneView.showsStatistics = true
        
        // 새로운 Scene 생성
        let scene = SCNScene(named: "art.scnassets/ship.scn")!
        
        // ARSCNView의 Scene에 만든 Scene 연결
        sceneView.scene = scene
    }
    
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        
        // 세션 Configuration 생성
        let configuration = ARWorldTrackingConfiguration()

        // session 시작
        sceneView.session.run(configuration)
    }
    
    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        
        // 세션 중단
        sceneView.session.pause()
    }

    // MARK: - ARSCNViewDelegate
    
/*
    // 렌더될 때 뷰 세션의 위치를 설정
    func renderer(_ renderer: SCNSceneRenderer, nodeFor anchor: ARAnchor) -> SCNNode? {
        let node = SCNNode()
     
        return node
    }
*/
    
    func session(_ session: ARSession, didFailWithError error: Error) {
        // Present an error message to the user
        
    }
    
    func sessionWasInterrupted(_ session: ARSession) {
        // Inform the user that the session has been interrupted, for example, by presenting an overlay
        
    }
    
    func sessionInterruptionEnded(_ session: ARSession) {
        // Reset tracking and/or remove existing anchors if consistent tracking is required
        
    }
}

 

 

결과물~!

 

 

ARSession

 

모션 트래킹, camera passthrough, 이미지 분석 등 모든 AR 경험과 관련된 주요 작업을 관리하는 객체.

#  OverView

ARSession 객체는 AR 경험을 만들기 위해 ARKit이 사용자를 대신하여 수행하는 주요 프로세스를 조정함. 이러한 프로세스에는 장치의 동작 감지, 하드웨어에서 데이터 읽기, 장치의 내장 카메라 제어 및 캡처된 카메라 이미지에 대한 이미지 분석 수행을 포함. 세션은 이러한 모든 결과를 종합하여 장치가 거주하는 실제 공간과 AR 콘텐츠를 모델링하는 가상 공간 간의 대응 관계를 설정한다.

 

#  Create a Session

모든 AR 경험을 만들기 위해서는 세션이 필요. 커스텀 렌더러 구현하려면 직접 세션을 인스턴스화

let session = ARSession()
session.delegate = self

 

표준 렌더러 (ARView, ARSCNView, ARSKView)중 하나를 사용하는 경우 렌더러가 세션 객체를 생성합니다. 앱의 세션과 상호 작용하려면 앱의 렌더러에서 액세스합니다.

 

let session = myView.session

 

#  Run a Session

 

세션을 실행하려면 Configuration이 필요. ARConfiguration의 subclass는 현실 세계를 기준으로 디바이스의 위치와 동작을 추적하는 방법을 결정하므로 생성하는 AR 경험을 결정함. 예를 들어 디바이스 후면 카메라 통해 주변 세계에 대한 사용자의 시야를 확대할 수 있음.

 

세션을 실행하려면 구성이 필요합니다. 의 하위 클래스는 ARWorldTrackingConfiguration이 실제 세계를 기준으로 장치의 위치와 동작을 추적하는 방법을 결정하므로 생성하는 AR 경험의 종류를 결정합니다. 예를 들어 장치의 후면 카메라를 통해 주변 세계에 대한 사용자의 시야를 확대할 수 있습니다.

 

# ARWorldTrackingConfiguration

 

ARWorldTrackingConfiguration 클래스는 디바이스의 여섯개의 각도를 기준으로 6개의 자유도: 3개의 회전 축(roll, pitch, and yaw) 그리고 3개의 변환 축(x, y, z 축 안에서 움직임)

이러한 종류의 추천은 몰입형 AR 경험을 생성할 수 있음. 사용자가 장치를 기울여서 물체 위나 아래를 보거나 장치를 움직여 물체의 측면과 뒤를 볼 때에도 가상 물체는 실제 세계에 비해 같은 장소에 있는 것처럼 보일 수 있습니다.

 

 

World-tracking 세션들은 카메라에 보이는 실제 장면의 요소를 인식하거나 우리의 앱과 상호작용할 수 있는 몇가지 방법을 제공함.

 

 

 - plainDetection을 통해서 실제 수평 또는 수직면을  찾는다. 그리고 ARPlainAhchor 객체로 세션에 해당 surface을 추가한다.

var planeDetection: ARWorldTrackingConfiguration.PlaneDetection { get set }

기본값은 평면 감지를 비활성화하며, 만약 수직 혹은 수평 평면을 감지하고, 비디오 이미지 분석에서 평평한 표면을 감지할 때 세션은 ARPlaneAnchor 객체를 추가하고 그리고 ARSessionDeleate, ARSCNViewDelete 또는 ARSKViewDelegate 알림

 

 - detectionImages를 통해서 2D 움직임일 인지한다. 그리고 ARImgaeAnchor를 통해서 2D이미지를 씬에 추가한다.

var detectionImages: Set<ARReferenceImage>! { get set }

 

 

사용자의 ARKit에서 검색하는 이미지들의 Set 집합.

 

ARKit이 매칭된 이미지를 감지할 때 해당 프레임워크는 ARImageAnchor 객체를 해당 세션에 추가함. ARKitReferenceImage 클래스는 현실에서 2D이미지의 위치와 방향을 정확하게 알아야 하는데, 그것을 캡슐화하여 정보를 제공함. ARSession에서 이미지 감지를 활성화하면 이미지의 레퍼런스를 해당 속성에 전달함. ARKitReferenceImage클래스는 일반적으로

1. 에셋 카탈로그에서 + 버튼을 통해 AR Resouce Group을 생성하고

2. 이미지 파일을 리소스 그룹으로 드래그 앤 드랍하여 참조할 AR 이미지 항목을 만든 다음에

3. Xcode 인스펙터에서 ARKit에서 인식하기 원하는 이미지의 사이즈 등을 제공함. (name 프로퍼티를 통해 런타임 시 디버깅 가능)

 - 이미지 카탈로그 방식의 예시를 보려면

 : https://developer.apple.com/documentation/arkit/arkit_in_ios/content_anchors/detecting_images_in_an_ar_experience

 

만약 0이 아닌 수로 maximumNumberOfTrackedImages을 설정한다면 ARKit은 이미지 트래킹을 활성화 함. 세션의 시간이 흐름에 따라 최대 4개의 이미지 앵커에 대해서 변환을 지속적으로 업디이트하여 이미지 추적을 활성화 함.

maximumNumberOfTrackedImages 값은 위치 및 방향 업데이트를 면밀히 모니터링할 앵커의 갯수를 지정하는 것임. 만약 세션이 최대 트래킹 이미지 수에 도달하면 기존 이미지 중 하나라 디바이스 뷰에서 벗어나야 다른 이미지를 추적하려고 함.

만약 0으로 값을 설정하면 비활성화 되며, ARKit은 관찰된 이미지에 대해 앵커를 생성하지만 해당 위치는 몇초에 한번 꼴로 드물에 업데이트 함. 

 - 이미지 추적 및 변경 예시를 보려면

 : https://developer.apple.com/documentation/arkit/arworldtrackingconfiguration/2941063-detectionimages

 

 

 

주의:

 성능을 위해 이미지 개수를 제한함.

Set으로 구현되어 있어서 이미지 수가 증가함에 따라 이미지 감지에 있어 정확도 및 성능이 저하될 수 있음. 최상의 결과를 얻으려면 약 100개 이하로 제한할 것.

 

100개보다 많은 이미지를 감지하기 위해서는 특정 시간에 100개의 이미지를 처음에 할당하고 다음 100개를 업데이트 하는 순간에 다시 할당하는 등 run() 메소드를 호출하여 변경해서 사용하기 바람.

 

 - detecionObjects를 통해서 3D 오브젝트를 인지하고 추적함. 씬에 3D 객체를  추가함.

ray casting을 사용하여 디바이스 화면의 터치 지점에 해당하는 실제 기능의 3D 위치를 찾습니다.

 

 

# initialWorldMap

- 세션 configuration으로 resume을 시도하는 이전 AR Session의 상태

var initialWorldMap: ARWorldMap? { get set }

 

ARWorldMap 클래스는 디바이스가 물리적 공간에서 사용자의 위치와 앵커 객체에 대한 근접성을 매핑하는 동안 AR세션의 상태.

class ARWorldMap : NSObject

 

해당 클래스에는 사용자가 디바이스를 물리적 공간에 대해 ARKit의 인지를 포함. ARKit은 유저의 물리적 공간에 대한 세부정보를 디바이스의 위치와 방향을 결정짓기 위해서 사용한다. ARAnchor 객체는 현실 세계에 가상 컨텐츠를 보여주기 위해 사용한다.

 

 

- World Map의 Seialize와 Deserialize (직렬화 및 역직렬화)

  - 우리가 앱에서 나갈때 현재 world map(getCurrentWorldMap(completionHandler:)) 메소드를 통해서 가져올 수 있다. 에 대해서 저장할 필요가 있다. 직렬화 및 역직렬화는 ARWorldMap이 NSSecureCoding을 상속받고 있기 때무넹 가능하며, NSKeyedArchiver를 통해서 직렬화할 수 있음.

 

 - 직렬화

func writeWorldMap(_ worldMap: ARWorldMap, to url: URL) throws {
    let data = try NSKeyedArchiver.archivedData(withRootObject: worldMap, requiringSecureCoding: true)
    try data.write(to: url)
}

 

 - 역직렬화

앱을 다음에 켰을 때 이전에 저장한 값을 가져오려면 아래와 같은 메소드 사용하면 된다.

func loadWorldMap(from url: URL) throws -> ARWorldMap {
    let mapData = try Data(contentsOf: url)
    guard let worldMap = try NSKeyedUnarchiver.unarchivedObject(ofClass: ARWorldMap.self, from: mapData)
        else { throw ARError(.invalidWorldMap) }
    return worldMap
}

 

앱이 동일한 물리적 환경에서 실행되는 경우에는 다시 복구한 world map의 앵커를 사용하여 저장된 세션의 동일한 위치에 동일한 가상 컨텐츠를 배치할 수 있음.

 

월드 데이터 저장 및 로드 예시:

https://developer.apple.com/documentation/arkit/arkit_in_ios/data_management/saving_and_loading_world_data

 

- # 저장된 월드 맵을 공유

   동일한 월드 맵을 추적하는데 두 개의 장치를 사용하여 두 사용자가 동일한 가상 컨텐츠를 보고 상호작용 가능한 네트워크 환경을 구축할 수 있음!

 

💡 비전OS에서 같이 게임할 수 있는거 만들면 재미있겠군!_!

 

ARWorldMap을 다른 장치를 내보내는 방법

1. 하나의 디바이스에서 NSKeyedArchiver를 통해 world map을 data type의 객체로 변홚나다. 네트워크를 통해 데이터를 send하기 위해서 파일에 write할 필요가 없다.

2. 선택한 네트워킹 기술을 사용하여 인코딩 되어 나온 데이터를 다른 장치로 전송한다.(내가 실제 서버가 구현되어 있다면 그걸로 쏴서 통신해도 되고 아니면, Multipeer Connectivity 세션에서 호출하여 데이터를 전당하고 다른 장체에서 데이터를 수신하는 메서드를 구현한다.(

3.  수신을 받는 디바이스에서는 NsKeyUnarchiver를 통해서 데이터에서 ARWorldMap로 인스턴스화하는데 사용한다.

 

멀티 유저 AR 경험의 예시를 보려면:

https://developer.apple.com/documentation/arkit/arkit_in_ios/creating_a_multiuser_ar_experience

 

 

 - 역직렬화 하여 world map 실행

존재하는 ARWorldMap으로부터 새로운 세션을 시작하기 위해서는 initialWorldMap 변수와 run 메소드를 통해서 world-tracking 속성을 설정하고 사용한다. 새로운 세션을 시작하고 같은 공간을 인지, 그리고 저장된 world map을 통헤서 로드한다.

 

# Examining a World Map

 

var anchors: [ARAnchor] // world map에 기록된 앵커 리스트

월드 맵이 캡쳐된 시점을 기준으로 세션에 있는 ARAnchor 객체의 스냅샷. 환경과 관련하여 정적인 것으로 간주되는 모든 앵커를 자동으로 포함. 월드 맵을 만든 후 월드 맵을 저장하거나 공유하기 전에 이 배열에서 앵커를 추가하거나 삭제할 수 있음.

 

ARAnchor 클래스는 카메라를 기준으로 실제 또는 가상 객체의 정적 위치 및 방향을 추적하려면 앵커 객체를 만들고 add(anchor: ) 메서드를 사용해 AR 세션에 추가.

 

 - Tip

 : 세션에 앵커를 추가하면 ARKit이 해당 앵커 주변 영역에서 월드 맵 추적 정확도를 최적화하여 가상 객체가 실제 세계에 비해 제자리에 유지되는 것처럼 보입니다. 가상 물체가 움직이면 이전 위치에서 해당 앵커를 제거하고 새 위치에 하나를 추가합니다.

 

 

 

 

(참고)

https://developer.apple.com/documentation/arkit/arworldtrackingconfiguration/2923548-planedetection

 

planeDetection | Apple Developer Documentation

A value that specifies whether and how the session automatically attempts to detect flat surfaces in the camera-captured image.

developer.apple.com

https://developer.apple.com/documentation/arkit/arworldtrackingconfiguration/2941063-detectionimages

 

detectionImages | Apple Developer Documentation

A set of images that ARKit searches for in the user's environment.

developer.apple.com

https://developer.apple.com/documentation/arkit/arworldtrackingconfiguration/2968180-initialworldmap

 

initialWorldMap | Apple Developer Documentation

The state from a previous AR session to attempt to resume with this session configuration.

developer.apple.com

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

 

ARWorldMap | Apple Developer Documentation

The state in a world-tracking AR session during which a device maps the user's position in physical space and proximity to anchor objects.

developer.apple.com

 

'apple > VisionOS, ARKit' 카테고리의 다른 글

[ARKit] #6 ARKit in iOS  (1) 2023.08.15
[SceneKit] #5 SCNSceneRendererDelegate  (0) 2023.08.15
[SceneKit] #4 SCNAction  (1) 2023.08.13
[SceneKit] #3 Animating SceneKit Content  (0) 2023.08.13
[SceneKit] #2 Geometry 다뤄보기  (0) 2023.08.13