XLOG

ARKit + RealityKit 기초 및 예제 본문

Swift/Etc

ARKit + RealityKit 기초 및 예제

X_PROFIT 2024. 9. 11. 23:10

이전 회사에서 혼자 AR 을 담당해서 개발을 했었는데, 바쁘다는 핑계로 제대로 정리를 한적이 없었다....
그래서 시작하게 된 AR Posting

1. ARKit, RealityKit 은 무엇일까?

  • ARKit

AR을 사용하려면 ARKit 을 쓰면된다고 생각하지만 100% 맞다고 할 순 없다. 실제 우리가 App 을 만들때 UI 가 있어야 하지만, ARKit 은 우리가 사용할 수 있는 UI 요소가 존재하지 않는다. ARKit 은 AR 환경을 구축하기 위해 내가 가지고 있는 하드웨어(iPhone, iPad)에 장착되어 있는 센서들을 통해 데이터를 받아와 AR 환경을 만드는데 도움을 주는 프레임워크이다.

  • RealityKit


RealityKit 공식 문서 페이지에서 말해주듯이 3D 시뮬레이션 및 렌더링 기능을 제공하는 프레임워크이다. 즉 ARSession 을 통해 데이터를 받아와서 우리가 보고 싶은 UI 환경을 만들어 준다고 보면 된다.

이때 사용하는 것이 ARView이다, 작년에 VisionOS 발표와 함께 공간 컴퓨팅이 가능한 ARView인 RealityView 가 나왔고 최근 iOS18 에도 적용이 된다고는 하지만 이 포스팅에서 사용하는 것은 ARView 를 사용할 예정이다.

ARView 는 하나의 Scene 을 가지고 있고 Scene 은 AnchorEntity 그리고 AnchorEntity는 자식으로 Entity, ModelEntity 를 가지고 잇다.
여기서 실제 Entity, ModelEntity 구 우리가 만들거나 Load 한 3D Asset 이다. 이 둘을 편의상 Entity 로 얘기할 계획이다. 여러 Entity 를 갖는 AnchorEntity 는 실제세계의 좌표에 Entity 들 그룹의 영역을 만드는 듯한 느낌이다. 일분 View 를 생각하면, Group, VStack 등과 비슷하다. Stack 내부의 Contents 들끼리 상대위치를 조정을 했듯이 AnchorEntity 내부에 Entity 들을 AnchorEntity 기준, 혹은 본인 부모 Entity 기준으로 위치를 잡아줬다.

2. Entity, AnchorEntity, ModelEntity

사진에서 보이듯이 AnchorEntity 는 유일하게 Anchoring component 가 존재한다. Anchoring 의 영어뜻을 살펴보면 단단히 고정시키다, 닻을 내리다 의 뜻으로 이 요소로 session 을 통해 만들어진 AR 공간에 위치를 고정시켜준다. 여기서 그냥 Entity Transform componet, SynchronizationComponent 이 두 요소만 존재하는데, 주로 다른 Entity들의 네트워킹, 즉 연결 포인트로 사용하게 된다. 일종에 관절과도 같다고 생각하면 된다. ModelEntity 는 Entity에 물리적인 요소들이 추가가 되어있다. 실제로 3D Asset 에 물리요소가 빠져있는 것이 있었는데 물리요소가 빠지면 터치 제스쳐를 통해 객체를 캐치하여 PanGesture 를 통해 이동을 시키고 싶지만 물리 요소가 빠진 Entity 는 감지를 할 수 없다.

3. ARView

그렇다면 가장 중요한 ARView 를 사용해보자

import SwiftUI
import ARKit
import RealityKit

struct ContentView: View {
    
    @State private var arView: ARView = ARView(frame: .zero)
    
    var body: some View {
        ARViewRepresentable(arView: $arView)
            .ignoresSafeArea()
    }
}

struct ARViewRepresentable: UIViewRepresentable {
    @Binding var arView: ARView
    @Binding var planeEntities: [PlaneEntity]
    
    func makeCoordinator() -> Coordinator {
        return Coordinator(parent: $arView, with: $planeEntities)
    }
    
    func makeUIView(context: Context) -> ARView {
        arView.automaticallyConfigureSession = false
        let configuration = ARWorldTrackingConfiguration()
        #if !targetEnvironment(simulator)
        // 감지할 평면은 수평, 수직이 있음
        configuration.planeDetection = [.horizontal]
        configuration.sceneReconstruction = .mesh
        arView.debugOptions = [.showWorldOrigin, .showSceneUnderstanding]
        #endif
        
        arView.session.delegate = context.coordinator
        configuration.environmentTexturing = .automatic
        arView.session.run(configuration)
        return arView
    }
    
    func updateUIView(_ uiView: ARView, context: Context) {
    }
    
    final class Coordinator: NSObject, ARSessionDelegate {
        
        @Binding var parent: ARView
        
        init(parent: Binding<ARView>) {
            self._parent = parent
        }
        
        func session(_ session: ARSession, didUpdate frame: ARFrame) {
        }
        
        func session(_ session: ARSession, didAdd anchors: [ARAnchor]) {
        }
        
        func session(_ session: ARSession, didUpdate anchors: [ARAnchor]) {
        }
    }
}

참고로 RealityKit 의 ARView 의 경우 ARKit 을 이용하여 ARSessionConfiguration을 생성하여 동작을 시키지 않더라도 default 값으로 ARWorldTrackingConfiguration 이 default로 싱행이 된다.
DebugOption을 활용하면 session 을 통해 전달되는 데이터를 시각화하여 확인할 수 있다.

또한 session Delegate 를 채택하게 되면 실시간으로 받아오는 데이터들 타이밍에 맞게 원하는 동작을 할 수도 있다.

 

 

처음 화면에서 보이는 3축은 showWorldOrigin 옵션으로 해당 위치가 이 AR 세상의 기준 위치가 된다. 또한 showSceneUndestanding 옵션으로 각 표면에 대한 정보를 읽어들이는데 이는 Lidar 센서가 있는 모델에서만 사용이 가능한 옵션이다. 위에서 다루진 않았지만 해당 옵션이 동작이 되는지 안되는지에 따른 분기처리 또한 가능하다.

이처럼 세션 옵션에 따라 다양한 데이터를 활용할 수 있다. 이 밖에도 bodyTracking, faceTracking 등 다양한 configuration 이 존재한다.

4. ARAsset 추가하기

추가하는 방법은 총 2가지이다. usdz 파일을 다운받아 직접 파일을 로드하거나, 간단하게 코드로 작성하여 만들어서 추가하는 방법이다.

struct ARViewRepresentable: UIViewRepresentable {
	func makeUIView(context: Context) -> ARView {
            '''
            '''
            let tapGesture = UITapGestureRecognizer(target: context.coordinator, action: #selector(context.coordinator.tapGesture))
            arView.addGestureRecognizer(tapGesture)
            return arView
    }
    
    final class Coordinator: NSObject, ARSessionDelegate {
    	@objc func tapGesture() {
            do {
            	// Scene 에 추가할 Anchor
                let anchor = AnchorEntity()
                // anchor 에 추가할 직접 코드로 만든 Entity
                let customEntity = makeCustomEntity()
                // https://developer.apple.com/kr/augmented-reality/quick-look/
                // 에서 다운받은 3D Asset 을 load 한 Entity
                let loadEntity = try loadEntity()
                anchor.addChild(customEntity)
                anchor.addChild(loadEntity)
                
                // anchorEntity 기준 좌측 10cm, 아래로 50cm, 뒤르 50cm
                customEntity.position = [-0.1, -0.5, -0.5]
                // anchorEntity 기준 우측 10cm, 아래로 50cm, 뒤르 50cm
                loadEntity.position = [0.1, -0.5, -0.5]
                parent.scene.addAnchor(anchor)
            } catch {
                print("load error")
            }
        }
        
        // Code 로 Entity 생성
        private func makeCustomEntity() -> ModelEntity {
            let mesh = MeshResource.generateBox(width: 0.1, height: 0.1, depth: 0.1)
            let material = SimpleMaterial(color: .red, isMetallic: true)
            let entity = ModelEntity(mesh: mesh, materials: [material])
            return entity
        }
        
        // usdz file load
        private func loadEntity() throws -> Entity {
            return try Entity.load(named: "toy_drummer_idle")
        }
    }
    
}



직접 Entity 를 Generate 하는 것은 위에 코드에서 보이지만, Mesh 로 모양을 잡고, 표면의 역할을 하는 material 을 입히는 방법이다. 여기선 SimpleMaterial 로 색만 입혔지만, 다양한 종류의 Material 이 있다.

5. Session, ARAsset 응용하기

위는 session 을 통해 ARAnchor 가 생성될때 생성된 Anchor 를 확인하기 위해 해당 Anchor를 사용하여 AnchorEntity 를 생성한 후, Anchor Size 와 같은 PlaneEntity 생성 및 추가하고 ARView Scene 에 추가를 한다. 추가된 Entity 는 배열로 가지고 있다 추가된 Anchor 에 update 가 발생하면 해당 Entity 를 찾아 사이즈와 컬러를 바꿔주는 것이다.

1년도 안되었지만 한동안 전혀 사용하지 않았더니...기억이 나지 않는데....미리미리 정리했으면 좋았을 텐데...ㅜㅜ

다음엔 AR Asset 에 레이캐스팅을 통한 감지한 평면에 위치시키기, Animation 적용 등 조금 더 심화 버전을 정리해봐야겠다.