시작
오늘은 SceneKit
으로 카메라를 통한 줄자를 만들어 보겠습니다.
간단히 설명할 예정이라 자세한 내용은 이전 글을 참고하시길 바랍니다.
SceneKit의 사용법 (1) - 정육면체와 달을 만들어 보자
화면의 위치 얻기
touchesBegan
메서드를 통하여 터치한 화면(2D 공간)에서 실제 카메라에 비치는(3D 공간) 좌표를 구해봅시다.
raycastQuery
를 통하여 터치 시 그 공간 좌표정보를 요청한 뒤,
raycast
로 만든 쿼리값(좌표정보 요청)을 넣어준 뒤 결괏값으로 변환시켜서 좌표 정보를 얻을 수 있습니다.
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
resetDot()
guard let touch = touches.first else { return }
// 터치되는 위치 얻기
let touchLocation = touch.location(in: sceneView)
// 2D공간(화면)을 3D로 좌표를 계산하여 변환시켜 준다.
guard let query = sceneView.raycastQuery(from: touchLocation,
allowing: .existingPlaneGeometry,
alignment: .any) else { return }
// 터치시 3D공간의 위치 정보 결과들 (return = [ARRaycastResult])
let hitResults = sceneView.session.raycast(query)
// 터치시 3D공간의 위치결과
guard let hitResult = hitResults.first else { return }
// 점을 추가하기
addDot(at: hitResult)
}
여기서 resetDot()
, addDot(at:)
는 코드 정리를 위해 따로 묶어놓았습니다. 바로 밑에서 설명드리겠습니다.
점 만들기
위에 공간 좌표를 얻었으면 이제 터치한 지점에 점을 생성시켜 봅시다.
터치한 위치(hitResult
)의 리턴값이 ARRaycastResult
이므로 이 위치를 받아서 점을 생성시킵니다.
ARKit
의 형상 생성법을 모르신다면 SceneKit의 사용법 (1) - 정육면체와 달을 만들어 보자를 참고하세요
func addDot(at location: ARRaycastResult) {
let dotGeometry = SCNSphere(radius: 0.002)
let material = SCNMaterial()
material.diffuse.contents = UIColor.red
dotGeometry.materials = [material]
let dotNode = SCNNode()
dotNode.geometry = dotGeometry
dotNode.position = SCNVector3(location.worldTransform.columns.3.x,
location.worldTransform.columns.3.y,
location.worldTransform.columns.3.z)
sceneView.scene.rootNode.addChildNode(dotNode)
// 나중에 초기화 시킬 때 사용하려고 전역변수에 담았습니다.
dotNodes.append(dotNode)
// 점을 두번 찍으면 거리를 계산하기.
if dotNodes.count >= 2 {
calculate()
}
}
또한 계산 및 리셋을 위해 전역변수를 만들어 줍시다.
var dotNodes = [SCNNode]()
계산하기
가장 중요한 두 점 사이를 계산해주는 함수를 만들어야 합니다.
그전에 우리는 수학 공부를 간단히 해봅시다.......
자 다들 피타고라스의 정리에 대해서 들어보셨을 겁니다.
2차원으로 된 두 점 사이의 거리는 아마 다들 구하실 수 있을 거예요.
그런데 우리는 3차원의 두 점 사이의 거리를 구해야 됩니다. 점의 생성 좌표가 3차원이기 때문이죠.
그림을 보면서 이해해 봅시다.
피타고라스의 정리를 생각하며 각각의 위치를 통해 가상의 정육면체를 생성해 줍니다.
이렇게 보면 📐 ABQ
가 직각삼각형이니 우리는 변 AB
의 길이, 즉 두 점 사이의 거리를 구할 수 있습니다!
먼저 📐 APQ
를 통하여 변 AQ
를 구해봅시다.
변 AQ
= √ ( (변 PQ)² + (변 AP)² )
변 AQ
= √ ( (x₂ - x₁)² + (y₂ - y₁)² )
📐 ABQ
의 밑변인 변 AQ
를 구했으니 높이인 변 BQ
를 갖고 바로 빗변인 변 AB
를 구할 수 있습니다.
변 AB
= √ ( (변 AQ)² + (변 BQ)² )
변 AB
= √ ( (x₂ - x₁)² + (y₂ - y₁)² + (z₂ - z₁)² )
이렇게 이제 우리는 3D공간의 두 점 사이의 거리를 구할 수 있게 됐습니다.
바로 코드에 적용해 봅시다.
일단 전역변수로 첫 번째 위치와 두 번째 위치를 갖고 온 뒤
제곱근(루트, √)sqrt
와 제곱 함수(pow
) 를 사용하여 두 점 사이를 구해봅시다.
func calculate() {
let start = dotNodes[0]
let end = dotNodes[1]
// 그림 참고. 월드 좌표계의 두 점 사이의 거리 구하기
// sqrt 제곱근(루트, √) 만드는 함수 , pow 제곱하기
let distance = sqrtf(powf(end.position.x - start.position.x, 2) + // 변 PQ
powf(end.position.y - start.position.y, 2) + // 변 AP
powf(end.position.z - start.position.z, 2)) // 변 BQ
// cm로 변형
let distanceUnitCm = String(format: "%.2f", distance * 100)
updateText(text: distanceUnitCm + "Cm", atPosition: start.position)
}
입체 글자 생성하기
ARKit의 형상 생성법을 모르신다면 SceneKit의 사용법 (1) - 정육면체와 달을 만들어 보자를 참고하세요
SCNText
를 생성할 때는 Material
을 따로 만들지 않아도 됩니다.
func updateText(text: String, atPosition position: SCNVector3) {
// 업데이트 될때 마다 텍스트 지우기
textNode.removeFromParentNode()
// 입체감 있는 텍스트를 생성
let textGeometry = SCNText(string: text, extrusionDepth: 1.0)
// material을 따로 만들지 않아도 된다.
textGeometry.firstMaterial?.diffuse.contents = UIColor.red
textNode = SCNNode(geometry: textGeometry)
textNode.position = SCNVector3(position.x + 0.05, position.y , position.z - 0.25)
// 기본 단위가 미터라서 스케일을 줄여줌.
textNode.scale = SCNVector3(0.005, 0.005, 0.005)
sceneView.scene.rootNode.addChildNode(textNode)
}
여기도 마찬가지로 리셋을 위해서 전역변수를 만들어 줬습니다.
var textNode = SCNNode()
리셋시키기
마지막입니다. 지금까진 터치 시 점을 생성만 계속하게 되죠.
그러므로 조건을 걸어서 점이 2개 이상이면 리셋을 시켜줄 겁니다.
func resetDot() {
if dotNodes.count >= 2 {
// node에서 점을 지우기
dotNodes.forEach { $0.removeFromParentNode() }
// 계산을 위해 다시 초기화
dotNodes = [SCNNode]()
}
}
완성
청소를 안 해서 그런지 맥북이 반짝 거리네요...
제 맥북의 길이는 30.4cm인데 테스터 결과 30.49cm!!!
거리 정확도가 꽤나 높다는 것을 알 수 있었습니다.
전체 코드를 보고 싶다면 Github 를 참고하세요.
ScnenKit의 다른 예제를 보고 싶다면 아래 링크를 참고하세요
참고
'Xcode > Framework' 카테고리의 다른 글
[iOS/Swift] SceneKit의 사용법 (5) - 이미지를 인식하고 그 위에 동영상을 재생시켜 보자 (0) | 2023.01.11 |
---|---|
[iOS/Swift] SceneKit의 사용법 (4) - 이미지를 인식하여 3D형상 만들기 (0) | 2023.01.11 |
[iOS/Swift] SceneKit의 사용법 (2) - 주사위 만들기 (0) | 2023.01.08 |
[iOS/Swift] SceneKit의 사용법 (1) - 정육면체와 달을 만들어 보자 (0) | 2023.01.07 |
[iOS/Swift] ARKit의 종류 (0) | 2023.01.07 |