이 글은 누구를 위한 것인가
- 쇼핑 앱의 가구/상품 AR 미리보기를 구현하려는 팀
- ARKit/ARCore로 게임이나 교육 AR 콘텐츠를 만들려는 개발자
- LiDAR 기반 고정밀 AR을 활용하려는 팀
들어가며
"소파를 사기 전에 우리 집에 놓아보고 싶다" — AR은 이 니즈를 해결한다. ARKit은 LiDAR 센서로 실내를 정밀하게 스캔하고, RealityKit은 3D 오브젝트를 현실에 자연스럽게 배치한다.
이 글은 bluefoxdev.kr의 ARKit ARCore 모바일 AR 구현 가이드 를 참고하여 작성했습니다.
1. ARKit vs ARCore 비교
[ARKit (iOS 11+)]
하드웨어: iPhone 6s+, iPad Pro
LiDAR: iPhone 12 Pro+, iPad Pro 2020+
평면 감지: 수평 + 수직
이미지 추적: ARImageAnchor
오클루전: LiDAR 기반 정밀 오클루전
멀티플레이어: ARWorldMap 공유
[ARCore (Android 7.0+)]
지원 기기: 구글 AR 지원 기기 목록
평면 감지: 수평 + 수직
이미지 추적: AugmentedImageDatabase
빛 추정: 환경광 자동 감지
클라우드 앵커: 멀티플레이어 AR
[RealityKit vs SceneKit]
RealityKit: 최신, PBR 렌더링, 물리 엔진 내장
SceneKit: 구버전, 더 많은 레퍼런스
권장: RealityKit (iOS 13+)
[성능 최적화]
폴리곤 수: 1만 이하 권장
텍스처: 512×512 ~ 2048×2048
USDZ: iOS AR 표준 포맷
LOD: 거리에 따른 디테일 감소
2. ARKit RealityKit 구현
import ARKit
import RealityKit
import SwiftUI
// AR 뷰 (SwiftUI)
struct ARProductViewer: UIViewRepresentable {
let productModelURL: URL
@Binding var isPlacing: Bool
func makeUIView(context: Context) -> ARView {
let arView = ARView(frame: .zero)
// AR 설정
let config = ARWorldTrackingConfiguration()
config.planeDetection = [.horizontal, .vertical]
config.environmentTexturing = .automatic
// LiDAR 지원 기기: 오클루전 활성화
if ARWorldTrackingConfiguration.supportsFrameSemantics(.sceneDepth) {
config.frameSemantics = .sceneDepth
arView.environment.sceneUnderstanding.options = [.occlusion, .physics]
}
arView.session.run(config)
arView.addGestureRecognizer(
UITapGestureRecognizer(target: context.coordinator, action: #selector(Coordinator.handleTap))
)
context.coordinator.arView = arView
context.coordinator.productModelURL = productModelURL
return arView
}
func updateUIView(_ uiView: ARView, context: Context) {}
func makeCoordinator() -> Coordinator { Coordinator(self) }
class Coordinator: NSObject {
var parent: ARProductViewer
weak var arView: ARView?
var productModelURL: URL?
init(_ parent: ARProductViewer) { self.parent = parent }
@objc func handleTap(_ gesture: UITapGestureRecognizer) {
guard let arView = arView, let modelURL = productModelURL else { return }
let location = gesture.location(in: arView)
// 레이캐스트로 평면 감지
let results = arView.raycast(from: location, allowing: .estimatedPlane, alignment: .horizontal)
guard let firstResult = results.first else { return }
// 3D 모델 배치
Task {
do {
let modelEntity = try await ModelEntity(contentsOf: modelURL)
modelEntity.generateCollisionShapes(recursive: true)
// 물리 상호작용
modelEntity.components[PhysicsBodyComponent.self] = PhysicsBodyComponent(
massProperties: .default,
mode: .dynamic
)
let anchor = AnchorEntity(world: firstResult.worldTransform)
anchor.addChild(modelEntity)
arView.scene.addAnchor(anchor)
// 배치 후 그림자
arView.environment.lighting.intensityExponent = 1.0
} catch {
print("모델 로드 실패: \(error)")
}
}
}
}
}
// 이미지 앵커 (패키지/제품 인식)
class ImageARManager: NSObject, ARSessionDelegate {
func setupImageTracking(for arView: ARView) {
guard let referenceImages = ARReferenceImage.referenceImages(
inGroupNamed: "AR Resources",
bundle: nil
) else { return }
let config = ARImageTrackingConfiguration()
config.trackingImages = referenceImages
config.maximumNumberOfTrackedImages = 3
arView.session.run(config)
arView.session.delegate = self
}
func session(_ session: ARSession, didAdd anchors: [ARAnchor]) {
for anchor in anchors.compactMap({ $0 as? ARImageAnchor }) {
let imageName = anchor.referenceImage.name ?? ""
print("이미지 감지: \(imageName)")
// 감지된 이미지 위에 AR 콘텐츠 표시
}
}
}
마무리
ARKit의 핵심은 ARWorldTrackingConfiguration과 RealityKit의 조합이다. LiDAR 지원 기기에서는 sceneDepth 프레임 시맨틱으로 정밀한 오클루전을 활성화해 3D 오브젝트가 실제 물체 뒤에 자연스럽게 가려지게 한다. 3D 모델은 USDZ 포맷으로 변환하고 폴리곤 수를 최적화해야 AR 프레임 레이트를 유지할 수 있다.