ARKit - 1. 그리기

Dave·2022년 8월 3일
0
post-thumbnail

ARKit을 이용해 화면에 그림을 그릴 수 있는 기능을 만들어보자

위치와 방향 구하기

import UIKit
import ARKit

class ViewController: UIViewController {

    @IBOutlet weak var SceneView: ARSCNView!
    let configuration = ARWorldTrackingConfiguration()
    
    // 장치의 위치와 방향을 추적하는데 사용
    
    override func viewDidLoad() {
        super.viewDidLoad()
        self.SceneView.debugOptions = [ARSCNDebugOptions.showWorldOrigin,
                                       ARSCNDebugOptions.showFeaturePoints]
                                       
// 이 세계의 중심을 표현하고 점으로 사물들을 특징해줌

        self.SceneView.showsStatistics = true
        // 프레임 수를 표시해줌
        
        self.SceneView.session.run(configuration)
    }
}

ARSCNViewDelegate의 renderer 메서드를 호출하고,
60프레임의 1초단위로 계속해서 호출되는 것을 확인 할 수 있다.

왜 중요할까?
카메라 위치와 두 SCNVector 유형을 추가하여 카메라의 정상 위치를 가리키는 방향 벡터를 모두 잡고, 정의에 따라 카메라 앞에 노드를 추가한다.

    func renderer(_ renderer: SCNSceneRenderer, willRenderScene scene: SCNScene, atTime time: TimeInterval) {
        print("rendering")
        
        guard let pointOfView = SceneView.pointOfView else { return }
        
        let transform = pointOfView.transform
        let orientation = SCNVector3(transform.m31, transform.m32, transform.m33)
        let location = SCNVector3(transform.m41, transform.m42, transform.m43)
        

    }
}

핸드폰의 현재 위치 벡터와 카메라 방향을 가리키는 방향 벡터를 얻었고, 이제 우리는 카메라 뷰 앞에 노드를 추가할 수 있도록 이것들을 벡터에 결합해야 함

    func renderer(_ renderer: SCNSceneRenderer, willRenderScene scene: SCNScene, atTime time: TimeInterval) {
        print("rendering")
        
        guard let pointOfView = SceneView.pointOfView else { return }
        // 1) 초당 60프레임으로 발생하는 render 장면이 나올때마다 그 장면의 관점을 얻는다.
             
        let transform = pointOfView.transform
        let orientation = SCNVector3(transform.m31, transform.m32, transform.m33)
        let location = SCNVector3(transform.m41, transform.m42, transform.m43)
        // 2) 관점은 변환된 매트릭스를 제공한다. 그 변환 행렬은 카메라 열과 행의 방향과 위치를 인코딩한다. 
        
        let currentPositionOfCamera = orientation + location
        // 3) 위치와 방향을 추출하고 그것을 결합하여 카메라의 현재 위치를 얻는다.
        
        print(orientation.x, orientation.y, orientation.z)
    }
}

func +(left: SCNVector3, right: SCNVector3) -> SCNVector3 {
    return SCNVector3Make(left.x + right.z , left.y + right.y, left.z + right.z)
    // SCNVector3끼리의 연산을 위해 정의한 함수 
}

그리기 기능 만들어보기

func renderer(_ renderer: SCNSceneRenderer, willRenderScene scene: SCNScene, atTime time: TimeInterval) {
    
    guard let pointOfView = scencView.pointOfView else { return }
    let transform = pointOfView.transform
    let orientation = SCNVector3(-transform.m31, -transform.m32, -transform.m33)
    let location = SCNVector3(transform.m41, transform.m42, transform.m43)
    let currentPositionOfCamera = orientation + location
    
    DispatchQueue.main.async {
        if self.draw.isHighlighted {
            let sphereNode = SCNNode(geometry: SCNSphere(radius: 0.02))
            sphereNode.position = currentPositionOfCamera
            self.scencView.scene.rootNode.addChildNode(sphereNode)
            sphereNode.geometry?.firstMaterial?.diffuse.contents = UIColor.red
            print("draw button is being pressed")
        }
    }
}

버튼을 하나 추가해준 뒤 renderer 함수에 버튼이 활성화 됐을시 구체를 카메라 위치에 생성하는 조건문을 추가해준다.

포인터만들기

그림을 그리는 위치를 알려줄 포인터를 만든다.

func renderer(_ renderer: SCNSceneRenderer, willRenderScene scene: SCNScene, atTime time: TimeInterval) {
        
        guard let pointOfView = scencView.pointOfView else { return }
        let transform = pointOfView.transform
        let orientation = SCNVector3(-transform.m31, -transform.m32, -transform.m33)
        let location = SCNVector3(transform.m41, transform.m42, transform.m43)
        let currentPositionOfCamera = orientation + location
        
        DispatchQueue.main.async {
            if self.draw.isHighlighted {
                let sphereNode = SCNNode(geometry: SCNSphere(radius: 0.02))
                sphereNode.position = currentPositionOfCamera
                self.scencView.scene.rootNode.addChildNode(sphereNode)
                sphereNode.geometry?.firstMaterial?.diffuse.contents = UIColor.red
                print("draw button is being pressed")
            }
            else {
            
            // if-else문을 이용하여 버튼이 눌려지지 않았을때 pointer를 만들어준다.
            
                let pointer = SCNNode(geometry: SCNBox(width: 0.01, height: 0.01, length: 0.01, chamferRadius: 0.01/2))
                pointer.position = currentPositionOfCamera
                
                self.scencView.scene.rootNode.enumerateChildNodes { (node, _) in
                    if node.geometry is SCNBox {
                        node.removeFromParentNode()
                        // 최신뷰만 남기고 삭제하기
                    }
                }
                
                self.scencView.scene.rootNode.addChildNode(pointer)
                pointer.geometry?.firstMaterial?.diffuse.contents = UIColor.red
            }
        }
    }
}

profile
iOS Developer

0개의 댓글