一、ARKit初探索
(一) AR(增强现实技术)介绍
增强现实(Augmented Reality,简称 AR),是一种实时地计算摄影机影像的位置及角度并加上相应图像的技术,这种技术的目标是在屏幕上把虚拟世界套在现实世界并进行互动。
增强现实技术,不仅展现了真实世界的信息,而且将虚拟的信息同时显示出来,两种信息相互补充、叠加。在视觉化的增强现实中,用户利用头盔显示器,把真实世界与电脑图形多重合成在一起,便可以看到真实的世界围绕着它。
(二) ARKit概述
1. 2017.06.06 苹果iOS11发布ARKit框架。
为了在真实空间和虚拟空间之间创建一个对应关系,ARKit使用一种称为视觉惯性测距的技术。该过程将来自iOS设备的运动感应硬件的信息与设备相机可见的场景的计算机视觉分析相结合。ARKit识别场景图像中的显著特征,跟踪视频帧中这些特征位置的差异,并将该信息与运动感测数据进行比较。结果是设备的位置和运动的高精度模型。
ARKit可以将2D或3D元素从设备的相机中添加到实时视图中,使得这些元素似乎在现实世界中。ARKit结合了设备运动跟踪,摄像机场景拍摄,高级场景处理和显示便利,他可让你轻松创建无与伦比的iPhone和iPad增强现实体验。
2. 开发要求 Xcode9及以上、iOS11以及处理器要在A9及以上(6S机型及以上)
目前只有Beta版本,链接地址: https://developer.apple.com/download/
(三)初步探索
1.首先我们来了解一下ARSession类
管理设备相机的共享对象和增强现实体验所需的运动处理。
一个ARSession对象协调ARKit代表你执行的主要过程,以创建增强的现实体验。这些过程包括从设备的运动感测硬件读取数据,控制设备的内置相机,以及对拍摄的相机图像执行图像分析。会话综合了所有这些结果,以建立设备所在真实世界空间与你为AR内容建模的虚拟空间之间的对应关系。
ARKit构建的每个AR体验都需要一个ARSession对象。如果您使用ARSCNView(显示使用3D SceneKit内容增强相机视图的AR体验的视图)或ARSKView(用于显示使用2D SpriteKit内容增强相机视图的AR体验的视图)对象来轻松构建AR体验的可视化部分,则视图对象包含一个ARSession实例。如果您为AR内容构建自己的渲染器,则需要自己实例化和维护一个ARSession对象。
运行会话需要会话配置:AROrientationTrackingConfiguration ARWorldTrackingSessionConfiguration 类的实例或其子类。这些类确定ARKit如何跟踪设备相对于现实世界的位置和运动,从而影响您可以创建的AR体验的种类。
2.接着我将一步步指导大家使用 使用ARSCNView来创建简单的3D AR效果
(1)打开Xcode9 bete版本,我在这里使用的是beta5版本。我们新建一个工程,选择Augmented Reality APP,点击Next
(2)Content Technology中选择SceneKit,Next
(3)ARSCNView
创建完成项目以后我们可以看到 art.scnassets->ship.scn 中苹果自带了一个飞机的模型
我们先直接在iOS11的设备上运行一下看看效果怎么样~~~
出现了~~~出现了~一个3D的小飞机,我们可以靠近他上下左右打量他一下,^V^怎么样感觉还不错吧
接下来我们开始看下代码(项目创建出来没有模型的或者OoO想看我代码的小伙伴们,猛戳~:https://github.com/az52013141711/ARKit_ARSCNView)
首先我们看下Main.storyboard中已经为我们添加了一个ARSCNView
接着我们来到ViewController.swift
@IBOutlet var sceneView:ARSCNView!
override func viewDidLoad() {
super.viewDidLoad()
// Set the view's delegate
//设置代理
sceneView.delegate = self
// Show statistics such as fps and timing information
//显示统计信息,如fps
sceneView.showsStatistics = true
// Create a new scene
//用模型创建场景(scn格式文件是一个基于3D建模的文件,使用3DMax软件可以创建,这里系统有一个默认的3D飞机)
let scene = SCNScene(named:"art.scnassets/ship.scn")!
// Set the scene to the view
//将场景设置到ARSCNView上显示
sceneView.scene = scene
}
override func viewWillAppear(_animated:Bool) {
super.viewWillAppear(animated)
// Create a session configuration
/*创建一个会话配置
这些ARSCNView和ARSKView类包括ARSession管理创建AR体验所需的运动跟踪和图像处理的对象。 但是,要运行会话,您必须提供会话配置。
ARWorldTrackingConfiguration 跟踪设备的方向和位置以及检测设备相机所看到的实际表面的配置
AROrientationTrackingConfiguration 仅跟踪设备方向的配置
ARConfiguration AR会话配置的抽象基类*/
let configuration = ARWorldTrackingConfiguration()
// Run the view's session
//运行视图的会话
sceneView.session.run(configuration)
}
override func viewWillDisappear(_animated:Bool) {
super.viewWillDisappear(animated)
// Pause the view's session
//暂停试图的会话
sceneView.session.pause()
}
就只有这短短的几句代码就可以实现了一个简单的3D飞机~苹果简直太棒了~~
(4)SCNScene介绍。ARSCNView显示需要一个场景(SCNScene)
SCNScene具有连接的几何图形,灯光,照相机和其他属性的节点的层次结构,其一起形成可显示的3D场景。
SCNScene包含在SceneKit中,SceneKit是iOS8之后苹果推出了一个3D模型渲染框架
下图中可以看到ARSCNView、SCNView与SCNScene的关系
SceneKit将内容实现为节点的分层树结构,也称为场景图。一个场景由一个根节点组成,该节点定义了场景世界的坐标空间,以及使用可见内容填充世界的其他节点。SceneKit在视图中显示场景,处理场景图和执行动画,然后高效地渲染GPU上的每个帧。
在使用SceneKit之前,我们熟悉基本的图形概念,如坐标系和三维几何的数学。SceneKit使用右手坐标系,其中(默认情况下)视图的方向沿着负z轴,如下所示。
前面有说ARKit使用视觉惯性测距技术来实现的,这样会在我们当前位置建立一个三维的拥有尺度的坐标系
①创建场景(SCNScene)方法
A、可以从使用外部3D创作工具创建的文件中获取场景。如果您在应用程序的资源管理资源目录中包含场景文件,则Xcode会压缩它们以获得最佳的SceneKit加载性能。要加载场景文件,使用init(named:) 、init(named:inDirectory:options:)、 init(url:options:)方法或实例SCNSceneSource类。上面的代码中我们已经看到了使用文件创建场景。
注意:为了获得最佳效果,将应用程序包中放置的场景文件放在带有.scnassets扩展名的文件夹中,并将从这些场景引用的图像文件作为纹理放置到Asset目录中。然后,Xcode优化场景和纹理资源,以便在每个目标设备上获得最佳性能,并为delivery features(如App Thinning和On-Demand资源)准备。
B、你也可以使用该scene()方法创建一个空场景,并通过创建节点及其内容的层次结构将其添加为场景对象的子项来填充rootNode内容。
②在我们场景中添加一个几何体
//创建空场景
letscene = SCNScene()
//创建一个六面体几何模型,每面都为矩形,可选择圆形边缘和角,单位米
letbox = SCNBox(width:0.1, height:0.1, length:0.1, chamferRadius:0);
//创建节点 并将box加到节点上
letboxNode = SCNNode(geometry: box)
//设置节点位置。x,y,z轴位置
boxNode.position = SCNVector3(0, 0, -0.2)
//把节点添加到根节点上
scene.rootNode.addChildNode(boxNode)
//将场景设置到ARSCNView上显示
sceneView.scene = scene;
这里我们使用SCNBox来建立一个几何体,运行效果如下图
苹果为我们内置几何类型不只SCNBox。还有基本形状,如球体,框和平面,以及用于从2D文本和Bézier曲线生成3D对象的特征。点击查看更多内置几何类型
大家看到会不会觉得~一个白茫茫的大方块未免有些太丑陋了O_O
~~好让我们先来点颜色瞧瞧
③渲染
渲染在电脑绘图中是指用软件从模型生成图像的过程。模型是用严格定义的语言或者数据结构对于三维物体的描述,它包括几何、视点、纹理以及照明信息。
我们需要了解两个类SCNMaterialProperty 和 SCNMaterial
A、SCNMatrialProperty(一种用于颜色或纹理的容器,用于材料的视觉特性之一)
材料具有几种视觉特性,它们一起确定其在照明和阴影下的外观。SceneKit通过将材料属性的信息与灯的位置,强度和颜色相结合,渲染场景中的每个像素。
材料属性的内容可以是一种颜色,它可以通过材料的表面提供均匀的效果,或者纹理,SceneKit使用由材料所附加的几何对象提供的纹理坐标来映射材料的表面。反过来,纹理可以来自任何几个来源:
1.图像对象,或图像文件的路径或URL
2.一个特殊格式的图像或六个图像的数组,用作立方图
3.核心动画层或层层次结构,其本身可能包含动画内容(Core Animation的layer对象)
4.提供静态图像的SpriteKit纹理,或渲染动画2D内容的整个SpriteKit场景。
B、接着给大家简单的介绍另一个重要的类(SCNMaterial)
SCNMaterial是一组阴影属性,用于定义渲染时几何体表面的外观。
创建材质时,你可以定义一组视觉属性及其选项,然后可以在场景中重复使用在多个几何体上。
材料具有八个视觉属性,列在“配置视觉属性”中,每个都定义了SceneKit的照明和阴影处理的不同部分。每个视觉属性都是类SCNMaterialProperty的一个实例,他为SceneKit的渲染方面提供了一个纯色,纹理或其他2D内容。材质的属性然后确定SceneKit用于将视觉特性与场景中的光合并以产生渲染场景中每个像素的最终颜色的公式。有关渲染过程的更多详细信息,请参阅照明模型 lightingModel
你的SCNGeometry(几种内置几何类型都继承与本类)可以使用其firstMaterial或materials将一个或多个材料附加到类的实例。多重几何可以引用相同的材料。在这种情况下,更改材质的属性会更改使用它的每个几何体的外观。
接下来我们先使用一下SCNMaterial中的一些配置视觉属性
//创建一个六面体几何模型,每面都为矩形,可选择圆形边缘和角,单位米
letbox = SCNBox(width:0.1, height:0.1, length:0.1, chamferRadius:0);
//material渲染器
letmaterial = SCNMaterial()
//确定材质的基础颜色 - 几何图形,当白光照亮时,使用这种颜色的灰度显示为阴影的材质。如果您提供图像,SceneKit将图像映射到几何图形的表面,而不是使用均匀基色的阴影。
//material.diffuse.contents = UIColor.orange
material.diffuse.contents=UIImage(named:"brick")
//如果几何具有与几何元素相同数量的材料,则材料索引对应于元素索引。对于具有比元素少的材料的几何,SceneKit通过计算该元素的材料数目的索引来确定每个元素的材料索引。例如,在具有六个元素和三个材质的几何体中,SceneKit将5使用索引处的材质在索引处渲染元素5 % 3 = 2。
box.materials = [material];
注:如果自己空白项目,要使用ARKit请使用应用程序Info.plist, 设置UIRequiredDeviceCapabilities中的arkit键。如果增强现实是应用程序的次要功能,请使用isSupported属性来确定当前设备是否支持您要使用的会话配置。
(四)继续探索~让砖头动起来~
1、尝试用手势让砖块动起来
废话不多说直接上代码
//添加手势
func addGestureRecognizer() {
//添加滑动手势(用于移动和旋转模型)
let PanGestureRecognizer = UIPanGestureRecognizer(target:self, action:#selector(panFunc))
sceneView.addGestureRecognizer(PanGestureRecognizer)
//创建缩放手势(用于缩放模型)
let PinchGestureRecognizer = UIPinchGestureRecognizer(target:self, action:#selector(pinchFunc))
sceneView.addGestureRecognizer(PinchGestureRecognizer)
}
//(用于移动和旋转模型)
@objc func panFunc(recognizer:UIPanGestureRecognizer) {
//1.获取节点在空间中位置
guard let node = self.sceneView.scene.rootNode.childNode(withName:"boxNode", recursively:true) else {
return
}
let nodePosition = node.position
//2.获取相机当前在空间中位置
guard let cameraTransform = self.sceneView.session.currentFrame?.camera.transform else {
return
}
let translation = cameraTransform.columns.3
//3.获取本次滑动的距离
let point = recognizer.translation(in:self.view)
let pointX = Float(point.x / self.view.bounds.size.width) * 0.1
let pointY = Float(point.y / self.view.bounds.size.height) * 0.1
//4.设置新的位置 x(point.x+nodePosition.x)、y(-point.y+nodePosition.y)、z(point.y+nodePosition.z)
let newNodePositionX = pointX + nodePosition.x;
let newNodePositionY = -pointY + nodePosition.y;
let newNodePositionZ = (translation.z-0.1 < pointY+nodePosition.z) ? (translation.z-0.1) : (pointY + nodePosition.z);//模型z坐标保持在距离摄像头0.1
node.position = SCNVector3(newNodePositionX, newNodePositionY, newNodePositionZ)
//旋转使用
//eulerAngles 具体参考 https://developer.apple.com/documentation/scenekit/scnnode/1407980-eulerangles
let angles = Float((node.eulerAngles.x>6) ? (Float.pi/32) : (node.eulerAngles.x+Float.pi/32))
node.eulerAngles = SCNVector3(angles, angles,0)
recognizer.setTranslation(CGPoint.zero, in:self.view)
}
//缩放模型
var nodeBeganScale:Float!
@objc func pinchFunc(recognizer:UIPinchGestureRecognizer) {
//1.获取节点
guard letnode = self.sceneView.scene.rootNode.childNode(withName:"boxNode", recursively:true) else {
return
}
if recognizer.state == UIGestureRecognizerState.ended {
recognizer.scale = 1
} else {
//2.手势开始时保存node的scale
if recognizer.state == UIGestureRecognizerState.began {
self.nodeBeganScale = node.scale.x
}
//3.缩放
//CGAffineTransform转换SCNVector3
//转换具体参考
//CGAffineTransform矩阵运算的原理http://justsee.iteye.com/blog/1969933
//node.scale SCNVector比例向量https://developer.apple.com/documentation/scenekit/scnnode/1408050-scale
//这里我只是使用了x方向的缩放
let nodeScale = Float(recognizer.view!.transform.scaledBy(x: recognizer.scale, y: recognizer.scale).a) * self.nodeBeganScale
node.scale = SCNVector3(nodeScale, nodeScale, nodeScale)
}
}
效果图
本章就到这里了,下节继续为大家分享ARKit相关内容,希望大家多多关注,感谢大家的到来。