HIT해

[Swift/SceneKit] 3D 모델 캡쳐하기 본문

Swift/Swift 개발 노트

[Swift/SceneKit] 3D 모델 캡쳐하기

힛해 2024. 9. 23. 05:03
728x90

https://developer.apple.com/documentation/scenekit

 

SceneKit | Apple Developer Documentation

Create 3D games and add 3D content to apps using high-level scene descriptions, and easily add animations, physics simulation, particle effects, and realistic physically based rendering.

developer.apple.com

 

돌의 3D 모습을 정면에서 딴 사진이 필요했다.

 

그리고 Render라는 메서드를 이용하면 화면을 캡쳐할 수 있다는 것을 알았다.

 

func captureSceneImage(scene: SCNScene, size: CGSize, cameraPosition: SCNVector3) -> UIImage? {
  
    let renderer = SCNRenderer(device: nil, options: nil)
    renderer.scene = scene

    let scale = UIScreen.main.scale
    let scaledSize = CGSize(width: size.width * scale, height: size.height * scale)

    let snapshot = renderer.snapshot(atTime: 0, with: scaledSize, antialiasingMode: .multisampling4X)

    return snapshot
}

 

이렇게 Scene을 받아와서 snapshot으로 UIImage를 return 해주면 된다.

 

한번 실행해보자.

3D 뷰 Scene 전체 모델들이 담기게 캡쳐가 된다.

 

나는 여기서 돌만을 추출해내고 싶었다.

 

그래서 반환된 이미지를 반만 잘라서 출력을 해보았더니.

 

돌만 나오지 않고 주변에 있는 3D 모델들도 같이 보이게됐다..

 

이렇게 이미지로 추출하려는 이유는 사용자가 커스텀한 돌의  프로필 사진을 만들어주기 위해서이었는데 다른 모델들이 나오면 안되기 떄문에 단순히 추출된 이미지를 자르는 방식으로는 해결되지 않았다.

 

그때 든 생각이.

 

"Render가 해당 Scene을 캡쳐하는거라면 새로운 Scene을 만들고 돌 모델 노드만 담고 캡쳐하면 되지 않을까?"

 

func captureSceneImage(scene: SCNScene, size: CGSize, selectedFaceShape: String, selectedFace: String) -> UIImage? {
 
    let renderer = SCNRenderer(device: nil, options: nil)

    // 2. 새로운 SCNScene 생성
    let newScene = SCNScene()

    if let parentNode = scene.rootNode.childNode(withName: "\(selectedFaceShape) reference", recursively: true),
       let childNode = parentNode.childNode(withName: "\(selectedFace)", recursively: true)
  {

        // 노드 복제
        let newChildNode = childNode.clone()

        // 새로운 장면에 추가
        newScene.rootNode.addChildNode(newChildNode)
        newScene.rootNode.addChildNode(newlightNode)
        newScene.rootNode.addChildNode(newlight2Node)
    }

    renderer.scene = newScene

    let scale = UIScreen.main.scale
    let scaledSize = CGSize(width: size.width * scale, height: size.height * scale)

    let snapshot = renderer.snapshot(atTime: 0, with: scaledSize, antialiasingMode: .multisampling2X)

    renderer.scene = nil  // SCNRenderer에서 참조 해제
    newScene.rootNode.childNodes.forEach { $0.removeFromParentNode() }  // 루트 노드에서 모든 자식 노드 제거

    return snapshot
}

 

 

결과는 성공적이었다.