编者按:AR游戏很常见,但在直播画面中出现AR屏幕,在其中再嵌套直播画面的玩法你试过么?咱们将经过两篇文章由浅入深,带你一块儿看看AR能与音视频直播碰撞出怎样的火花。node
本篇咱们将从ARKit开发原理,到demo代码,带你们一块儿实现一个AR应用。数组
下篇咱们将围绕音视频信息采集,到场景渲染,与你们分享如何实现AR视频会议的demo实例。欢迎访问 RTC 开发者社区,与更多开发者交流经验,参与更多技术活动。bash
今年7月Apple推出了AR工具ARKit,着实闪着了你们的眼睛。从目前的评测能够知道 ARKit已经很是成熟,彻底能够进行商用了。session
在iOS中,加强现实由ARKit和渲染两部分组成。ARKit主要负责AR计算,它将ARCamera捕获的视频帧看成背景,使用视觉惯性测距(VIO)来精确跟踪周围的世界,进行坐标转换,场景搭建及平面的捕获;而后,经过 SceneKit(3D)/SpritKit(2D) 或 Metal 库进行渲染,使虚拟物体与真实世界相接合,达到加强现实的目的。ide
今天咱们就来详细的了解一下 ARKit,看看 Apple 为咱们提供了怎样强大的工具,可让咱们迅速的构建一个AR应用程序。工具
在讲解咱们的 AR 程序以前,咱们先要了解几个ARKit的基本概念。只有这几个基本概念了解清楚以后,咱们才能清楚的知道如何去写一个AR程序。post
AR 的目标是往真实世界中的特定点插入虚拟内容,而且在真实世界中移动时还能对此虚拟内容保持追踪。优化
ARKit 从视频帧中得到某张图片的特征后,就能够从多个帧中追踪这些特征。随着用户在真实世界中的移动,就能够利用相应的特征点来估算 3D 姿态信息。用户移动地越多,就会得到越多的特征,并优化这些估算的 3D 姿态信息。ui
有没有可能检测不出特征点的状况呢?固然有,可能检测不出特征点的状况以下:atom
ARKit 的平面检测用于检测出现实世界的水平面,也就是在 3D 空间中,Y值为0的一个区域。平面检测是一个动态的过程,当摄像机不断移动时,检测到的平面也会不断的变化。此外,随着平面的动态检测,不一样平面也可能会合并为一个新的平面。
只有检测真实世界有水平面以后,才能找到锚定点,并将虚拟物体放到这个锚定点上。
除了平台检测外,还有点击检测。顾名思意,就是当用户点击屏幕时,ARKit 将点击屏幕的2D空间位置转换为ARKit 经过 ARCamera 捕获到的视频帧的 3D 空间位置。并在这个位置检测是否有平面。
世界追踪都追踪什么呢?ARKit 会追踪如下几个信息:
ARKit 使用视觉惯性测距技术,对摄像头采集到的图像序列进行计算机视觉分析,而且与设备的运动传感器信息相结合。ARKit 会识别出每一帧图像中的特征点,而且根据特征点在连续的图像帧之间的位置变化,而后与运动传感器提供的信息进行比较,最终获得高精度的设备位置和偏转信息。
除了上面这几个概念外,咱们还须要知道ARKit提供的一些基本知识。
ARSession 是 ARkit 的核心。它是链接ARCamera与ARACNView之间的桥梁。像捕获视频,与 CoreMotion 的数据整合,场景的理解,平面检测等等都须要 ARSession 来协调各模块进行协同处理。
另外,ARSession 有两种获取 ARFrame 的方法:
push 实时不断的获取相机位置,由ARSession主动告知用户。经过实现ARSession的代理来获取。
(void)session:(ARSession *)session didUpdateFrame:(ARFrame *)frame
复制代码
pull 用户想要时,主动去获取。经过 ARSession的属性currentFrame来获取。
该类的做用是设置一些 ARSession 相关的配置,如是否使用平面检测就是经过这个参数来设置的。
ARSCNView 继承自 SceneKit 中的 SCNView。ARSCNView是一个很是复杂的类,它不只拥有SCNView的功能,并且它还管理着 ARSession。以下图所示:
SceneKit 的主要做用是将虚拟物体展现在3D场景中。每一个虚拟物体均可以用 SCNNode 来表明,SCNNode 在 SCNScene 中展示,而无数SCNScene 组成 3D 世界。
它有几个重要的方法须要特别强调一下:
hitTest 方法
- (NSArray<ARHitTestResult *> *)hitTest:(CGPoint)point types:(ARHitTestResultType)types;
复制代码
point:2D坐标点(手机屏幕某一点);
ARHitTestResultType:捕捉类型是 点 仍是 面;
NSArray<ARHitTestResult *> *:追踪结果数组。数组的结果排序是由近到远。
根据2D坐标点搜索3D模型位置。当咱们在手机屏幕点击某一个点的时候,能够捕捉到这一个点所在的3D模型的位置。为何返回值是一个数组呢?这是由于手机屏幕一个是长方形的二维空间,而相机捕捉到的是一个由这个二维空间映射出去的长方体。咱们点击屏幕一个点,能够理解为在这个长方体的边缘射出一条线,这一条线上可能会有多个3D物体模型。
renderer 方法
(void)renderer:(id<SCNSceneRenderer>)renderer updateAtTime:(NSTimeInterval)time
复制代码
它是 ARSCNViewDelegate 中的回调方法,每次 3D 引擎要渲染新的视频帧时都会调用该方法。
重载 renderer 方法
- (void)renderer:(id<SCNSceneRenderer>)renderer didAddNode:(SCNNode *)node forAnchor:(ARAnchor *)anchor
复制代码
它是 ARSCNViewDelegate 中的回调方法,每次 ARKit 检测到了平面时都会调用此方法。咱们能够经过这个代理方法得知咱们添加一个虚拟物体到AR场景下的锚点(AR现实世界中的坐标)
SCNNode表明一个虚拟物体。经过 SCNNode 能够对虚拟物体进行变换和旋转,还能够作几何变换,光照等操做。
在ARKit中它表明一个场景。SCNScene 包括背景 和 虚似物体。其中背景能够是从 ARCamera捕获的视频帧。而虚拟物体由 rootNode 存储,它就是前面介绍的 SCNNode。
包含真实世界位置和方向的信息。经过它能够轻松地将虚拟物体添加,更新或从会话中删除。
ARCamera 用于捕捉视频流。通常咱们无需去建立一个ARCamera,由于在初始化 AR 时,它就帮咱们将ARCamera建立好了。另外,咱们通常也不直接使用 ARCamera 的 API,默认都是设置好的。
摄像头视频帧的包装类。从 ARCamera 中获取的每一幅视频帧都被封装成 ARFrame。它包含位置追踪信息、环境参数、视频帧。重点是它包含了苹果检测的特征点,经过rawFeaturePoints能够获取,不过只是特征的位置,具体的特征向量并无开放。
使用 SCNMaterial 能够对虚拟物体 SCNNode 进行贴图。
所谓任意门就是在真实环境中虚拟一扇门,当走进这扇门后,能够看到另一个世界。
初始化ARKit
- (void)viewDidLoad {
[super viewDidLoad];
// Set the view's delegate self.sceneView.delegate = self; // Show statistics such as fps and timing information self.sceneView.showsStatistics = YES; // Create a new scene SCNScene *scene = [SCNScene new]; // Set the scene to the view self.sceneView.scene = scene; //Grid to identify plane detected by ARKit _gridMaterial = [SCNMaterial material]; _gridMaterial.diffuse.contents = [UIImage imageNamed:@"art.scnassets/grid.png"]; //when plane scaling large, we wanna grid cover it over and over _gridMaterial.diffuse.wrapS = SCNWrapModeRepeat; _gridMaterial.diffuse.wrapT = SCNWrapModeRepeat; _planes = [NSMutableDictionary dictionary]; //tap gesture UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(placeTransDimenRoom:)]; [self.sceneView addGestureRecognizer:tap]; } - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; // Create a session configuration ARWorldTrackingConfiguration *configuration = [ARWorldTrackingConfiguration new]; configuration.planeDetection = ARPlaneDetectionHorizontal; // Run the view's session
[self.sceneView.session runWithConfiguration:configuration];
}
复制代码
处理ARKit检测到的平面
用于提示能够用于交互的平面,后期模拟物理世界也要用到。
- (void)renderer:(id<SCNSceneRenderer>)renderer didAddNode:(SCNNode *)node forAnchor:(ARAnchor *)anchor{
if ([anchor isKindOfClass:[ARPlaneAnchor class]] && !_stopDetectPlanes){
NSLog(@"detected plane");
[self addPlanesWithAnchor:(ARPlaneAnchor*)anchor forNode:node];
[self postInfomation:@"touch ground to place room"];
}
}
- (void)renderer:(id<SCNSceneRenderer>)renderer didUpdateNode:(SCNNode *)node forAnchor:(ARAnchor *)anchor{
if ([anchor isKindOfClass:[ARPlaneAnchor class]]){
NSLog(@"updated plane");
[self updatePlanesForAnchor:(ARPlaneAnchor*)anchor];
}
}
- (void)renderer:(id<SCNSceneRenderer>)renderer didRemoveNode:(SCNNode *)node forAnchor:(ARAnchor *)anchor{
if ([anchor isKindOfClass:[ARPlaneAnchor class]]){
NSLog(@"removed plane");
[self removePlaneForAnchor:(ARPlaneAnchor*)anchor];
}
}
复制代码
放置transDimenRoom
对于隐藏空间,抽象成两个类来表达:transDimenRoom,transDimenStruct。
后者用于提供一些平板等基础结构,前者将这些结构拼成一个房间,留一个门框出来让用户可以看见里面。
当须要放置任意门时,就用+transDimenRoomAtPosition:方法建立一个transDimenRoom,当用户走进去时,用 -hideWalls: 隐藏四周的墙壁,切换成全景背景。
@interface transDimenRoom : SCNNode
@property (nonatomic, strong) SCNNode *walls;
+(instancetype)transDimenRoomAtPosition:(SCNVector3)position;
//TODO:check if user in room
-(BOOL)checkIfInRoom:(SCNVector3)position;
-(void)hideWalls:(BOOL)hidden;
@end
复制代码
检测到用户走进房间
目前为了简单起见,是判断用户与房间中心的距离,当距离小于1时,就认为用户进入了房间。这里的逻辑之后会收归到transDimenRoom中。
- (void)renderer:(id<SCNSceneRenderer>)renderer updateAtTime:(NSTimeInterval)time{
if (_room.presentationNode) {
SCNVector3 position = self.sceneView.pointOfView.presentationNode.worldPosition;
SCNVector3 roomCenter = _room.walls.worldPosition;
CGFloat distance = GLKVector3Length(GLKVector3Make(position.x - roomCenter.x, 0, position.z - roomCenter.z));
if (distance < 1){
NSLog(@"In room");
[self handleUserInRoom:YES];
return;
}
[self handleUserInRoom:NO];
}
}
复制代码
今天首先向你们介绍了一下 ARKit 的基本知识,而后经过任意门这个实例告诉了你们如何写一个ARKit程序。这个 任意门 能够应用在不少场景中,你们能够经过这个实例进行扩展,充份发挥本身的想像力。
其实本节最最关键的是让你们知道 ARKit中的那些基本概念。ARSession是它的核心,它协调内部模块进行场景的各类计算。而 ARSCNView 只是渲染技术中的一种,咱们完成能够经过 OpenGL/Metal 来替换掉它。
在下篇,咱们将会介绍,如何讲ARkit应用于视频直播。