一、ARkit尺子项目学到了什么?
先看效果,如下图:
- 1.SCNVector3本质就是一个三维坐标
- 2.画线的步骤就是:拿到2个坐标 --> 选择“线”这个“几何” --> 渲染--> 最后生成节点。这个过程待会会详细的代码解释。
- 3.坐标之间的转换,世界坐标、摄像头。这个和SLAM里面的很类似。
- 4.SCNText,一个很特殊的“几何”,如图中的8.52cm就是用SCNText表示的,当然最后都会放到节点里面去。
- 5.对节点的使用更加的清晰,每个点、每根线、以及描述的text都会被添加到节点,然后ARSCNView的scene.rootNode.addChildNode(node)
二、下面开始具体的讲解整个实现的逻辑和流程。
先解释下:图中的 白色"+"是始终位于屏幕的正中间的,整个项目都是以这个点为瞄准点。
开始第一步:
extension ARSCNView {
//拿到三维坐标
func worldVector(for position:CGPoint) ->SCNVector3?{
let results = self.hitTest(position, types: [.featurePoint])
guard let result = results.first else {
return nil
}
// 获取点的坐标,类型是matrix_float4x4 ,调这个方法就可以拿到相机的镜头
return SCNVector3.positionTransform(result.worldTransform)
}
}
// 拿到镜头的坐标
static func positionTransform(_ transform: matrix_float4x4) -> SCNVector3{
return SCNVector3Make(transform.columns.3.x, transform.columns.3.y, transform.columns.3.z)
}
- 1.点击手机屏幕,然后获取到一个CGPoint,
- 2.通过 self.hitTest(position, types: [.featurePoint]).first 就拿到一个 为matrix_float4x4类型的坐标。
- 3.通过positionTransform方法得到镜头的坐标。
实话里面到底是如何实现的,我也很想知道。我通过之前对SLAM的学习,大概知道,里面的矩阵变换的过程。对于iOS开发者,知道需要转换即可,具体的深究留到自己对AR的掌握到了一定程度的时候会比较好。
第二步:画线
//画线的方法
func drawLine( vector: SCNVector3, color:UIColor) -> SCNNode {
let indices: [UInt32] = [0,1] // 指数 //0指:一维,表示点 //1指:二维,表示线
//数据来源
let source = SCNGeometrySource(vertices: [self,vector])
//画什么样的几何---选择线
let element = SCNGeometryElement(indices: indices, primitiveType: .line)
let geometry = SCNGeometry(sources: [source], elements: [element])
geometry.firstMaterial?.diffuse.contents = color
let node = SCNNode(geometry: geometry)
return node
}
- 1.数据源:两个坐标
- 2.选择几何模型--线
- 3.生成节点
第三步:什么时候绘制了?
实在ARSCNViewDelegate中的渲染方法里面进行绘制。具体代码:
{
func renderer(_ renderer: SCNSceneRenderer, updateAtTime time: TimeInterval) {
DispatchQueue.main.async {
self.scanWorld()
}
}
//扫描外部真实世界,开始测量
func scanWorld() {
//我们以中间点为开始点,也就是那个十字标图片指的点为开始的点。
guard let worldPosition = sceneView.worldVector(for: view.center) else {
return
}
vectorStart = worldPosition
currentLine = Line.init(sceneView: self.sceneView, startVector: vectorStart, unit: self.unit)
//设置结束的节点
vectorEnd = worldPosition
currentLine?.update(to: vectorEnd)
infoLabel.text = currentLine?.distance(to: vectorEnd) ?? "是同一个点"
}
}
进行扫描真实的世界,在scanWorld()方法里面调用绘制的方法进行绘制。
三、总结一下思路
- 选择开始的点
- 然后开始绘制并计算长度
- 再次点击屏幕获取终点--结束。
实现的难点:想到坐标的转换、然后绘制的时机、对节点的使用的掌握。