使用ARKit,通过摄像机捕捉到真实世界并在其中加入计算机程序创造的虚拟物品,检测平面,并在平面上覆盖上一张网图,最终效果图如下
- 创建ARKit项目,Xcode会自动在Main.storyboard中为项目创建一个ARSCNView
- 项目简单设置
- (void)viewDidLoad {
[super viewDidLoad];
[self setupScene];
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self setupSession];
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
// 暂停 session
[self.sceneView.session pause];
}
- (void)setupScene{
//设置ARSCNViewDelegate代理
self.sceneView.delegate = self;
// 显示 fps 等信息
self.sceneView.showsStatistics = YES;
SCNScene *scene = [SCNScene new];
self.sceneView.scene = scene;
//添加光线
self.sceneView.autoenablesDefaultLighting = YES;
//显示 debugging information 3D坐标系和探测点
self.sceneView.debugOptions = ARSCNDebugOptionShowWorldOrigin|ARSCNDebugOptionShowFeaturePoints;
}
- (void)setupSession {
// 配置 session configuration
ARWorldTrackingConfiguration *configuration = [ARWorldTrackingConfiguration new];
//检测水平方向
configuration.planeDetection = ARPlaneDetectionHorizontal;
[self.sceneView.session runWithConfiguration:configuration];
}
复制代码
- ARSCNViewDelegate协议方法 项目中使用到的几个协议方法的介绍
/**
当一个新的node映射上去时调用
*/
- (void)renderer:(id )renderer
didAddNode:(SCNNode *)node
forAnchor:(ARAnchor *)anchor {
}
/**
当一个node属性发生变化时调用
*/
- (void)renderer:(id)renderer didUpdateNode:(SCNNode *)node forAnchor:(ARAnchor *)anchor
/**
当一个node移除时调用
*/
- (void)renderer:(id)renderer didRemoveNode:(SCNNode *)node forAnchor:(ARAnchor *)anchor{
复制代码
- 渲染一个平面 先创建一个Plane类,继承至SCNNode,需要两个方法,一个初始化方法和一个更新方法
在初始化方法中传入位置信息ARPlaneAnchor,平面的宽高坐标都从ARPlaneAnchor获取,平面发生变化时,需要更新位置属性
- (instancetype)initWithAnchor:(ARPlaneAnchor *)anchor;
- (void)update:(ARPlaneAnchor *)anchor;
复制代码
声明一个平面属性
@property (nonatomic, strong) SCNPlane *planeGeometry;
复制代码
初始化方法中创建平面信息
- (instancetype)initWithAnchor:(ARPlaneAnchor *)anchor{
self = [super init];
self.planeGeometry = [SCNPlane planeWithWidth:anchor.extent.x height:anchor.extent.z];
//平面展示网格
SCNMaterial *material = [SCNMaterial new];
UIImage *img = [UIImage imageNamed:@"tron_grid"];
material.diffuse.contents = img;
self.planeGeometry.materials = @[material];
SCNNode *planeNode = [SCNNode nodeWithGeometry:self.planeGeometry];
//将平面移到传入的位置
planeNode.position = SCNVector3Make(anchor.extent.x, 0, anchor.extent.z);
//平面在SceneKit中默认是vertical的,所以要旋转90度
planeNode.transform = SCNMatrix4MakeRotation(-M_PI/2.0, 1.0, 0.0, 0.0);
//设置平面的相关属性
[self setTextureScale];
[self addChildNode:planeNode];
return self;
}
//更新平面属性
- (void)update:(ARPlaneAnchor *)anchor{
self.planeGeometry.width = anchor.extent.x;
self.planeGeometry.height = anchor.extent.z;
self.position = SCNVector3Make(anchor.center.x, 0, anchor.center.z);
[self setTextureScale];
}
复制代码
- 使用平面Plane类
声明一个字典保存平面
@property (nonatomic, strong) NSMutableDictionary *planes;
复制代码
在ARSCNViewDelegate协议的回调方法中,通过Anchor创建平面
- (void)renderer:(id)renderer didAddNode:(SCNNode *)node forAnchor:(ARAnchor *)anchor{
if (![anchor isKindOfClass:[ARPlaneAnchor class]]) {
return;
}
Plane *plane = [[Plane alloc] initWithAnchor:(ARPlaneAnchor *)anchor];
[self.planes setObject:plane forKey:anchor.identifier];
[node addChildNode:plane];
}
复制代码
当移动摄像头时,需要重新渲染Plane,所以需要在ARSCNViewDelegate的另一个回调方法中更新状态
- (void)renderer:(id)renderer didUpdateNode:(SCNNode *)node forAnchor:(ARAnchor *)anchor{
Plane *plane = [self.planes objectForKey:anchor.identifier];
if (plane == nil) {
return;
}
[plane update:(ARPlaneAnchor *)anchor];
}
复制代码
我还在学习摸索ARKit中,这只是ARKit的一点基本使用,要想做出酷炫的AR应用,这点知识是远远不够的。项目github地址