Danny의 iOS 컨닝페이퍼
article thumbnail

시작

오늘은 SceneKit으로 이미지를 인식하여 그 위에 캐릭터를 올려볼 예정입니다.

 

간단히 설명할 예정이라 자세한 내용은 이전 글을 참고하시길 바랍니다.

SceneKit의 사용법 (1) - 정육면체와 달을 만들어 보자

SceneKit의 사용법 (2) - 주사위 만들기

SceneKit의 사용법 (3) - 카메라 줄자, 거리 측정하기

 

 

준비물

1. 이미지를 인식할 수 있는 스티커 or 카드

사용할 스티커 징징이 & 고스트

 

 

2. 스티커 모양에 맞는 3D 모델을 다운로드

 

3D 모델 다운로드하는 사이트들

https://www.turbosquid.com/ko/

https://free3d.com/3d-models/

https://sketchfab.com/feed

 

위에서 원하는 모델을 다운로드합니다.

모델의 파일 확장자는 dae, obj  둘 중 하나를 다운로드하여 주세요.

 

 

만약 dae 파일을 다운로드 시

 

다운로드한 dae파일을 art.scnassets에 넣어줍니다.

 

dae 파일을 Xcode 내에서 Editor -> Convert를 통해 scn파일로 변환을 해주세요

SceneKit에서 사용가능한 모델을 만들 수 있습니다.

 

 

만약 obj 파일을 다운로드 시

 

obj 파일을 다운로드하였다면 usdz파일로 변환시켜줘야 합니다.

 

여기서 USDZ는 애플과 픽사가 같이 만든 AR content를 표시하는 3D file format으로

ARKit의 모델들과 호환이 가능하다고 하네요.

 

변환을 하기 위하여 아래 공식 홈페이지에서

 

AR Creation Tools - Augmented Reality - Apple Developer

Reality Composer is a powerful tool that makes it easy for you to create interactive augmented reality experiences. Reality Converter quickly converts your existing 3D models to USDZ.

developer.apple.com

 

Reality Converter 다운로드를 해주세요.

 

Reality Converter 사용법

 

Reality Converter 실행 후 드래그로 obj파일 넣은 후

내보내기(command + E)를 통하여 usdz파일로 변환을 시켜 줍니다.

 

생성된 usdz파일을 art.scnassets 폴더에 넣어줍시다.

 

usdz파일을 Xcode 내에서 Editor -> Convert를 통해 scn파일로 변환해 주세요

SceneKit에서 사용가능한 모델을 만들 수 있습니다.

 

 

인식할 이미지를 추가하기

자 이제 이미지 준비하기 단계는 끝났습니다.

한번 준비한 이미지를 넣어봅시다.

 

Assets.xcassets에서 AR Resource 폴더를 추가합니다.

폴더명은 원하는 이름으로 지어주세요. (저는 "Cards"라고 지었습니다.)

 

인식시키고 싶은 이미지를 생성한 폴더에 넣어줍니다.

사진을 추가하기 전 색상 조절(노출, 대비 설정)을 통해 인식률을 높여주세요.

 

느낌표 에러가 생겼네요

에러가 발생하는 이유는 인식할 사진의 크기를 정해주지 않아서 발생합니다.

실제 스티커의 크기로 설정해 줍시다. 여기서 기본 단위는 미터(m)입니다

(width만 설정해 주면 자동으로 Height는 잡아줍니다.)

 

 

Configuration 설정

 

이미지를 인식해야 하므로 ARImageTrackingConfiguration을 사용합니다.

 

Asset에 넣어둔 이미지를

ARReferenceImage.referenceImages(inGroupNamed:bundle:)로 불러와 줍니다.

 

referenceImages(inGroupNamed:)에는

바로 위에 생성한 AR Resource 폴더 이름인 "Cards"를 넣어 줍시다.

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    
    // ARImageTrackingConfiguration으로 특정 이미지를 찾는다.
    let configuration = ARImageTrackingConfiguration()
    
    // Asset에서 이미지를 인식할 이미지 폴더를 선택합니다..
    // inGroupNamed: 생성한 AR Resource Group의 폴더의 이름
    // bundle: 번들의 위치를 나타낸다.(main에서 실행되므로 Bundle.main)
    guard let imageToTrack = ARReferenceImage.referenceImages(inGroupNamed: "Cards", bundle: Bundle.main) else { return }
    
    // ARKit에서 어떤 이미지를 추적할 것인가?
    configuration.trackingImages = imageToTrack
    
    // 몇 개의 이미지를 추적할 것인가?
    configuration.maximumNumberOfTrackedImages = 2
        
    // Run the view's session
    sceneView.session.run(configuration)
}

 

참고

ARImageTrackingConfigurationARWorldTrackingConfiguration 둘 다 이미지를 인식할 수 있습니다.

// 한 두개의 이미지 또는 움직이는 상황에도 오직 이미지만 인식하여 인식률이 높다.
let configuration = ARImageTrackingConfiguration()
configuration.trackingImages = referenceImages

// 여러 이미지들을 추척해야 할 때 안정적으로 사용 가능합니다.
let configuration = ARWorldTrackingConfiguration()
configuration.detectionImages = referenceImages

 

각각의 장단점이 존재하는데 자세한 내용은 공식문서를 참고해 주세요.

 

Apple Developer Documentation

 

developer.apple.com

 

 

이미지 인식시키기

이미지를 추가하였으니 이제 인식을 시켜보겠습니다.

 

 

순서

1. 이미지를 인식하기

2. 인식된 이미지 위에 평면을 올리기

3. 평면 위에 3D모델 올리기

 

 

먼저 renderer(_:nodeFor:) 메서드 간단 설명

 

renderer메서드는 anchor의 위치에 따라 새로운 노드를 추가할 수 있습니다.

    anchor는 화면에 감지된 이미지를 말합니다. 그리고 결과 값으로 3D객체(node)를 리턴하죠.

 

이제 인식한 이미지 위에 반투명한 평면을 올려보겠습니다.

extension ViewController: ARSCNViewDelegate {
    func renderer(_ renderer: SCNSceneRenderer, nodeFor anchor: ARAnchor) -> SCNNode? {
        // 빈 노드를 생성시킵니다.
        let node = SCNNode()
        
        // 이미지를 추적해야 하므로 감지된 anchor를 ARImageAnchor로 형변환을 시켜줍니다.
        // imageAnchor.referenceImage.name로 접근하여 지금 인식되고 있는 사진의 이름도 알 수 있습니다.
        guard let imageAnchor = anchor as? ARImageAnchor else { return node }
        
        // 카드를 인식해야 하므로 감지된 카드의 크기를 입력해 준다.(하드코딩 할 필요 X)
        // 카드위에 3D객체 형상(plane)을 렌더링을 시킨다.
        let plane = SCNPlane(width: imageAnchor.referenceImage.physicalSize.width,
                             height: imageAnchor.referenceImage.physicalSize.height)
        
        // plane을 투명하게 만들기
        plane.firstMaterial?.diffuse.contents = UIColor(white: 1.0, alpha: 0.5)
        
        // 뼈대 설정 : 위에 생성한 plane 
        let planeNode = SCNNode(geometry: plane)
        
        // plane은 처음 생성시 수직평면으로 생성이 되므로 우리는 스티커와 동일하게 90도로 눞여 줍니다.
        // eulerAngles은 라디안 각도를 표현하기 위함. : -90°
        planeNode.eulerAngles.x = -(Float.pi / 2)
        
        node.addChildNode(planeNode)
        
        // 감지된 사진의 이름을 갖고 모델을 만들어 준다. (감지된 이름 : AR Resource폴더의 이미지 이름)
        if let imageName = imageAnchor.referenceImage.name {
            makeModel(on: planeNode, name: imageName)
        }
  
        return node
    }

           

makeModel(on planeNode: SCNNode, name: String)라는 함수를 생성해 모델을 만들어 보겠습니다.

 

여기서 사용할 이미지는 2개입니다.

간편하게 사용하기 위해 Enum으로 구성해 줬습니다.

enum Card {
    case Ghost
    case Squidward
    
    // 파일 이름
    var name: String {
        switch self {
        case .Ghost: return "Ghost"
        case .Squidward: return "Squidward"
        }
    }
    
    // 파일이 있는 위치
    var assetLocation: String {
        switch self {
        case .Ghost:
            return "art.scnassets/Ghost.scn"
        case .Squidward:
            return "art.scnassets/SquidwardTentacles.scn"
        }
    }
}

 

3D모델 생성 관련 자세한 내용은 정육면체와 달을 만들어 보자, 주사위 만들기 를 참고해 주세요.

func makeModel(on planeNode: SCNNode, name: String) {
    switch name {
    case Card.Ghost.name:
        guard let ghostScene = SCNScene(named: Card.Ghost.assetLocation) else { return }
        guard let ghostNode = ghostScene.rootNode.childNodes.first else { return }
        
        // 생성된 3D 모델의 각도를 조정
        ghostNode.eulerAngles.x = Float.pi/2
        ghostNode.eulerAngles.z = -(Float.pi/2)
        
        // plane위에다 올려야 하므로 planeNode.addChildNode에 추가
        planeNode.addChildNode(ghostNode)
        
    case Card.Squidward.name:
        guard let squidwardScene = SCNScene(named: Card.Squidward.assetLocation) else { return }
        guard let squidwardNode = squidwardScene.rootNode.childNodes.first else { return }
        
        // 생성된 3D 모델의 각도와 위치를 조정
        squidwardNode.eulerAngles.x = Float.pi/2
        squidwardNode.position.z = -(squidwardNode.boundingBox.min.y * 6)/1000

        // plane위에다 올려야 하므로 planeNode.addChildNode에 추가
        planeNode.addChildNode(squidwardNode)
        
    default: break
    }
}

 

 

완성

신기하고 재밌는 경험이었습니다. ㅎ

 

이미지 인식률이 낮아서 애를 먹었.. 사진만 100장 이상 찍었던...

 

아무튼 팁으로는 처음 이미지를 만들 때

최대한 이미지를 꽉 채워서 사진을 찍도록 합시다.

 

완성된 코드를 보고 싶다면 Github 를 참고하세요.

 

 

ScnenKit의 다른 예제를 보고 싶다면 아래 링크를 참고하세요

 

[iOS/Swift] SceneKit의 사용법 (1) - 정육면체와 달을 만들어 보자

시작 AR 주사위 던지는 앱을 만들기 앞서 이해를 돕기 위해 들어가기 앞서 간단한 Cube(정육면체)와 Shpere(구)를 만들어 봅시다. 사용법 iOS -> Augmented Reality App 선택 Content Technology : SceneKit을 선택해

ios-daniel-yang.tistory.com

 

[iOS/Swift] SceneKit의 사용법 (2) - 주사위 만들기

시작 오늘은 SceneKit으로 주사위 생성 및 굴리기를 해봅시다. 순서는 이전에 만든 Cube와 달 만들기와 거의 동일하니 못 보셨다면 여기를 참고하세요. 바로 시작하겠습니다. 주사위로 사용할 모델

ios-daniel-yang.tistory.com

 

[iOS/Swift] SceneKit의 사용법 (3) - 카메라 줄자, 거리 측정하기

시작 오늘은 SceneKit으로 카메라를 통한 줄자를 만들어 보겠습니다. 간단히 설명할 예정이라 자세한 내용은 이전 글을 참고하시길 바랍니다. SceneKit의 사용법 (1) - 정육면체와 달을 만들어 보자 Sc

ios-daniel-yang.tistory.com

 

참고

 

iOS & Swift - The Complete iOS App Development Bootcamp

From Beginner to iOS App Developer with Just One Course! Fully Updated with a Comprehensive Module Dedicated to SwiftUI!

www.udemy.com

 

 

반응형
profile

Danny의 iOS 컨닝페이퍼

@Danny's iOS

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!