AR 基础入门及在 58同城 iOS App 中的实践

orangeswan 发布于8月前 阅读299次
0 条评论

本文将从 AR 基础知识说起,重点介绍苹果的 ARKit 框架并大致介绍 58 同城 iOS 客户端中 AR 功能的实现及遇到的问题和解决,希望对大家开发 AR 功能有所帮助。

一、 AR 基础知识

    1 、 AR 简介

增强现实技术( AugmentedReality ,简称 AR ),是一种实时地计算摄影机影像的位置及角度并加上相应图像、视频、 3D 模型的技术,这种技术的目标是在屏幕上把虚拟世界套在现实世界并进行互动。

一个最简单地 AR 场景实现所需要的技术及步骤如下:

1) 多媒体捕捉现实图像:如摄像头;

2) 三维建模:如 3D 立体模型;

3) 传感器追踪:主要追踪现实世界动态物体的六轴变化,这六轴分别是 X 、 Y 、 Z 轴位移及旋转。其中位移三轴决定物体的方位和大小,旋转三轴决定物体显示的区域;

4) 坐标识别及转换: 3D 模型显示在现实图像中不是单纯的 frame 坐标点,而是一个三维的矩阵坐标;

5) 除此之外, AR 还可以与虚拟物体进行一些交互。 AR 基础入门及在 58同城 iOS App 中的实践

    2 、 3D 模型制作

AR 模型规范如下:

1)目录,文件夹,文件名所有相关 为英文名或字母配数字(中文路径无法识别);

2)所有模型制作标准:三角面数控制在1万到1万3左右,顶点数在3万左右;

3)配好表格体现模型关节与材质,贴图对应关系,表明材质类型等等;

4)模型关节命名也不要带中文;

5)贴图格式为PNG,不要用JPG;

6)单个贴图尺寸控制在512*512及以下;

7)模型Dae文件大小在4M以下;

模型制作工具有:

1 ) 3dmax ,目前主流的移动端 AR 模型制作工具;

2 ) Xcode 3D 模型编辑器;

3 ) Blender 、 moi 、 SketchUp 、 Wings 3D 、 Autodesk123D Make 草图大师等其他工具;

iOS 端模型加载方式 :

1 ) OpenGL

2 ) mental

3 ) unity3D

4) 苹果 ARKit 框架

二、 ARKit 工作原理及流程介绍

    1 、 ARKit 概述及特点介绍

1 ) ARKit 是 2017 年 6 月 6 日,苹果发布 iOS11 系统所新增框架 , 它能够帮助我们以最简单快捷的方式实现 AR 技术功能。

2)ARKit 框架提供了两种 AR 技术,一种是基于 3D 场景 (SceneKit) 实现的增强现实,一种是基于 2D 场景 (SpriktKit) 实现的增强现实

3 )目前 ARKit 框架本身只包含相机追踪,不能直接加载物体模型,要想显示 AR 效果,必须要依赖于苹果的游戏引擎框架( 3D 引擎 SceneKit , 2D 引擎 SpriktKit ),主要原因是游戏引擎才可以加载物体模型。

4) 误区解读: ARKit 虽然是 iOS11 新出的框架,但并不是所有的 iOS11 系统都可以使用,而是必须要是处理器 A9 及以上才能够使用,苹果从 iPhone6s 开始使用 A9 处理器,也就是 iPhone6 及以前的机型无法使用 ARKit

5) 开发环境介绍

Xcode 版本: Xcode9 及以上

iOS 系统 :iOS11 及以上

iOS 设备:处理器 A9 及以上( 6S 机型及以上)

MacOS 系统: 10.12.4 及以上(安装 Xcode9 对 Mac 系统版本有要求)

    2 、 ARKit 与 SceneKit 的关系

1 ) AR 技术叫做虚拟增强现实,也就是在相机捕捉到的现实世界的图像中显示一个虚拟的 3D 模型。这一过程可以分为两个步骤:

    • 相机捕捉现实世界图像

由 ARKit 来实现

    • 在图像中显示虚拟 3D 模型

由 SceneKit 来实现

2 ) ARSCNView 所有跟场景和虚拟物体相关的属性及方法都是自己父类 SCNView 的:

    • <ARKit> 框架中中显示 3D 虚拟增强现实的视图 ARSCNView 继承于 <SceneKit> 框架中的 SCNView, 而 SCNView 又继承于 <UIKit> 框架中的 UIView。

UIView 的作用是将视图显示在 iOS 设备的 window 中, SCNView 的作用是显示一个 3D 场景, ARScnView 的作用也是显示一个 3D 场景,只不过这个 3D 场景是由摄像头捕捉到的现实世界图像构成的

    • ARSCNView 只是一个视图容器,它的作用是管理一个 ARSession, 即 AR 会话。

    • 在一个完整的虚拟增强现实体验中, <ARKit> 框架只负责将真实世界画面转变为一个 3D 场景,这一个转变的过程主要分为两个环节:由 ARCamera 负责捕捉摄像头画面,由 ARSession 负责搭建 3D 场景。

    • 在一个完整的虚拟增强现实体验中,将虚拟物体显示在 3D 场景中是由 <SceneKit> 框架来完成中:每一个虚拟的物体都是一个节点 SCNNode, 每一个节点构成了一个场景 SCNScene, 无数个场景构成了 3D 世界

    • 综上所述, ARKit 捕捉 3D 现实世界使用的是自身的功能,这个功能是在 iOS11 新增的。而 ARKit 在 3D 现实场景中添加虚拟物体使用的是父类 SCNView 的功能,这个功能早在 iOS8 时就已经添加( SceneKit 是 iOS8 新增)

3 、 ARKit 工作原理

1 ) ARSCNView&ARSession

AR 基础入门及在 58同城 iOS App 中的实践

    • ARKit 提供两种虚拟增强现实视图,他们分别是 3D 效果的 ARSCNView 和 2D 效果的 ARSKView ,无论是使用哪一个视图都是用了相机图像作为背景视图( iOS 自定义相机中的预览图层),而这一个相机的图像就是由 <ARKit> 框架中的相机类 ARCamera 来捕捉的。

    • ARSCNView 与 ARCamera 两者之间并没有直接的关系,它们之间是通过 AR 会话,也就是 ARKit 框架中非常重量级的一个类 ARSession 来搭建沟通桥梁的

    • 要想运行一个 ARSession 会话,你必须要指定一个称之为 会话追踪配置 的对象 : ARSessionConfiguration , ARSessionConfiguration 的主要目的就是负责追踪相机在 3D 世界中的位置以及一些特征场景的捕捉(例如平面捕捉),这个类本身比较简单却作用巨大。

    2)ARWorldTrackingSessionConfiguration&ARFrame

    • ARSession 搭建沟通桥梁的参与者主要有两个 ARWorldTrackingSessionConfiguration 与 ARFrame

    • ARWorldTrackingSessionConfiguration (会话追踪配置)的作用是跟踪设备的方向和位置 , 以及检测设备摄像头看到的现实世界的表面。它的内部实现了一系列非常庞大的算法计算以及调用了你的 iPhone 必要的传感器来检测手机的移动及旋转甚至是翻滚

    • 当 ARWorldTrackingSessionConfiguration 计算出相机在 3D 世界中的位置时,它本身并不持有这个位置数据,而是将其计算出的位置数据交给 ARSession 去管理,而相机的位置数据对应的类就是 ARFrame

    • ARCamera 只负责捕捉图像,不参与数据的处理。它属于 3D 场景中的一个环节,每一个 3D Scene 都会有一个 Camera ,它觉得了我们看物体的视野

它们三者之间的关系看起来如下图:

AR 基础入门及在 58同城 iOS App 中的实践

Ps : ARCamera 在 3D 世界的位置看起来是这样的

AR 基础入门及在 58同城 iOS App 中的实践

4、ARKit工作完整流程

ARKit 框架工作流程如下 :

    • ARSCNView 加载场景 SCNScene

    • SCNScene 启动相机 ARCamera 开始捕捉场景

    • 捕捉场景后 ARSCNView 开始将场景数据交给 Session

    • Session 通过管理 ARSessionConfiguration 实现场景的追踪并且返回一个 ARFrame

    • 给 ARSCNView 的 scene 添加一个子节点( 3D 物体模型)

三、 AR圈子实现细节

说起58同城AR的实现细节就不得不先看看SceneKit框架了,因为只有当真正做AR功能时才发现原来ARKit只是个架子,所有的功能都需要SceneKit来实现,所以我们先来看看SceneKit。

    1 、 SceneKit API 介绍

    • SCNView

SCNView 主要作用是显示 SceneKit 的 3D 内容 , 是 UIView 的子类 , 它可以添加到我们的视图中去

    • SCNScence

SCNScene 为游戏中的场景 , 简单的说 , 就是放的游戏元素 ( 地图 , 灯光 , 人物的游戏元素 ) 的地方。

    • SCNNode

SCNNode 被称为节点 , 一个大型的游戏场景结构就是由无数个小的节点组成 , 它有自己的位置和自身坐标系统 , 我们可以把几何模型,灯光,摄像机的游戏中的真实元素,吸附到 SCNNode 节点上。

    • SCNCamera

SCNCamera 被称为照相机或者摄像机 , 游戏就相当于一个生活中的环境 , 我们可以通过照相机捕捉到你想要观察的画面。

    • SCNLight

SCNLight 被称为灯光 , 没有光线的话 , 我们是看不到物体的 , 在游戏中也是一样的 , 我们可以给游戏中添加不同的灯光 , 来模拟逼真的环境。

    • SCNAudioSource

SCNAudioSource 主要负责给游戏中添加声音。

    • SCNAction

SCNAction 主要负责改变节点的属性 , 比如我们要让一个地球围绕太阳旋转 , 一个气球从一个地方移动另外一个地方。

    • SCNTransaction

SCNTransaction 主要负责提交改变节点属性的事件 , 后面具体讲到再说明白。

    • SCNGeometry

SCNGeometry 就是呈现三维模型的类 , 我们模型具体长什么样子 , 是个正方体还是长方体 , 都是它说了算。

    • SCNMaterial

SCNMaterial 定义模型的外观,好比一个球体 , 它渲染出来是红色,还是绿色 , 会不会发光等

2 、 58 同城 AR 圈子具体实现

1 ) AR 环境搭建

在页面加载时进行 AR 环境搭建:

    • 初始化视图 ARSCNView 并设置代理,方便使用平面检测功能;

    • 添加灯光让 3D 模型效果更好的呈现出来;

    • 添加视野节点以便动态计算节点位置并添加 3D 模型约束;

    • 初始化 ARSession 并设置代理,以便摄像机移动时进行实时检测;

在页面出现时初始化世界追踪会话配置并开启 AR 场景;

在页面消失时暂停 AR 场景;

在 ARSCNView 上添加手势以便可以获取到用户点击的模型对象;

2 ) AR 模型的制作

使用 SceneKit 框架,支持加载的模型格式为: .scn 、 .dae 和 .obj 。其中 obj 只有体没有动画, .scn 需要使用苹果 3D 编辑器进行编辑, .dae 需要 3DMax 安装 OpenCollada 插件导出后才能使用,项目中使用 dae 模型进行帮帮和宝箱模型的展示。

使用 Xcode 自带的命令进行模型的压缩,具体为命令为:

/Applications/Xcode.app/Contents/Developer/usr/bin/copySceneKitAssets  模型文件 -o 优化后模型存储的目录 `

3 ) AR 模型加载

卡片模型的加载

    • 初始化卡片视图单例,使用 SCNPlane 平面节点渲染卡片视图,根据卡片距离摄像机的距离比例放大或缩小卡片并使用父节点和视野节点的朝向约束让卡片正常展示;

    • 根据摄像机的移动动态的改变卡片的大小以便正常展示,更新根据时间和空间双维度减少性能的消耗;

3D 模型的加载

本地内置或者从网络下载模型文件,使用 SCNSceneSource 类型进行加载并设置好位置、缩放比例和朝向等即可展示;

3D 模型跟随屏幕

为了更好的让用户看到 3D 模型,根据摄像机的移动实时的改变模型的位置,让模型始终在视野前几米的最佳位置。

4 )真实地理位置转换为 AR 中的 3D 坐标

将经纬度转换为 AR 中真实的 3D 坐标的具体步骤为:

    • 将经纬度转换到直角坐标系中;

    • 设置 AR 世界追踪状态为 3D 场景和真实世界匹配;

    • 随机海拔高度确定 3D 坐标;

5 ) 3D 模型动画

使用 SCNAction 、 SCNAnimationEvent 、 SCNTransaction 以及 CAAnimationGroup 进行普通动画和骨骼动画的展示;

6 ) 3D 模型手势交互

根据手势获取 SCNHitTestResult 从而拿到点击到的模型,并进行对应的操作。

7 )此次 AR 圈子功能的主场景为附近的人和发布信息以卡片的形式展示,点击附近的人进入帮帮模型相关流程,点击卡片进入盒子模型相关流程,其中还可以聊天和发布信息,具体内容大家可以在58APP中查看。

四、遇到的问题及解决

1 、地理位置 转换为 AR 场景中的真实 3D 坐标

解决方案:先把经纬度通过百度 SDK 转换为直角坐标,再基于用户初始位置坐标取一个差值,然后在 AR 场景中设置对应真实的地理朝向;

代码如下:

//卡片直角坐标
BMKMapPoint point = BMKMapPointForCoordinate(CLLocationCoordinate2DMake(lat, lon));
//用户定位直角坐标
BMKMapPoint basePoint = BMKMapPointForCoordinate(CLLocationCoordinate2DMake(self.arCircleInfoModel.lat, self.arCircleInfoModel.lon));
//坐标间的距离
CLLocationDistance baiduDistance = BMKMetersBetweenMapPoints(point,basePoint);
//返回卡片3D坐标
return SCNVector3Make(point.x-basePoint.x, distance, point.y-basePoint.y);
//3D场景和真实世界匹配
_arSessionConfiguration.worldAlignment = ARWorldAlignmentGravityAndHeading;

2 、判断 3D 模型是否在摄像头视野中

解决方案:解决思路为把 3D 模型投影到摄像机的视图上,具体方案为使用苹果 API

代码如下:

NSArray *nodesArr = [self.arSCNView nodesInsideFrustumWithPointOfView:self.arSCNView.pointOfView];

3 、问题:模型渲染问题

解决方案:使用视图转换为图片进行渲染

代码如下:

cardPlane.firstMaterial.diffuse.contents = [self cardViewWithData:dic andDistance:distance];

4 、问题:让所有模型都朝向摄像机

解决方案:通过子父节点嵌套,并加入朝向约束实现模型一直朝向摄像机

代码如下:

//添加视野节点
        [_arSCNView.scene.rootNode addChildNode:_arSCNView.pointOfView];
        //初始化朝向约束
        self.lookAtConstraint = [SCNLookAtConstraint lookAtConstraintWithTarget:_arSCNView.pointOfView];
        self.lookAtConstraint.gimbalLockEnabled = TRUE;

5 、问题:灯光问题

解决方案:自定义点光源或者开启 AR 场景模型光源

代码如下:

//自动刷新灯光
        _arSCNView.automaticallyUpdatesLighting = YES;
        //添加灯光
        _arSCNView.autoenablesDefaultLighting = YES;

6 、问题:模型适度更新不影响性能

解决方案:时间空间双维度,都符合才刷新模型渲染图

7 、问题:卡片大小问题

解决方案:根据卡片距离摄像机位置动态实时调整卡片 3D 模型大小

8 、问题:卡片模型重叠问题

解决方案:顺序随机高度,并配合 server 端打散策略

9 、问题:模型动画加载问题

解决方案:先加载初始模型,然后取出动画帧按需加载

10 、问题:模型加载问题

解决方案:需要把模型及皮肤文件放到主工程( Cube )中,使用加载资源模型 API 进行加载

代码如下:

SCNSceneSource *sceneSource = [SCNSceneSource sceneSourceWithURL:[[NSBundle mainBundle] URLForResource:sceneName withExtension:@"dae"] options:nil];
    SCNNode *subNode = [[sceneSource sceneWithOptions:nil error:nil].rootNode clone];

11 、问题:获取模型点击

解决方案:通过在 AR 视图中加入点击事件获取模型点击

代码如下:

NSArray *resultArr = [self.arSCNView hitTest:[tap locationOfTouch:0 inView:self.arSCNView] options:nil];
    //获取点击模型
    SCNHitTestResult *result = [resultArr firstObject];
    WBSCNNode *clickNode = (WBSCNNode *)result.node;

12 、问题:模型重叠时的渲染动画闪烁和撕裂问题

解决方案:通过指定渲染顺序及清理深度缓存来解决

代码如下:

//指定每个模型的渲染顺序
boxNode.renderingOrder =  index+i;
//清理深度缓存
box.firstMaterial.writesToDepthBuffer = false;

AR 基础入门及在 58同城 iOS App 中的实践

部门招聘

高级Java开发工程师

工作职责:

1、负责58同城APP,58同镇等相关后端研发工作;

2、负责基础平台的架构设计,核心代码开发;

3、调研并掌握业内通用技术方案,引入项目迭代,提升研发效率;

职位要求:

1、3年以上Java互联网项目开发经验;

2、Java基础扎实,编码规范,程序具备较高的健壮性,熟悉常用设计模式;

3、对MVC框架、RPC框架、基础服务组件等有深入的研究;

4、掌握Linux环境下的网络编程、多线程编程,数据结构和算法能力良好;

5、对高并发高可用系统设计有深入的实践经验;

6、具有高度的责任心、勇于承担责任,能承受较强的工作压力;

7、积极主动,敢于接受挑战,有较强的团队合作精神;

高级前端研发工程师

工作职责:

1、负责58同城App前端产品研发;

2、负责58同城前端无线产品某一技术方向,人才培养;

3、前端研发所需类库、框架、脚手架搭建;

4、交互模式调研及创新(React,ReactNative);

职位要求:

1、计算机及相关专业本科以上学历;

2、3年以上前端开发经验,负责过复杂应用的前端设计和开发 ;

3、精通web前端技术(js/css/html),熟悉主流框架类库的设计实现、w3c标准,熟悉ES6/7优先;

4、熟悉前端模块化开发方式(commonjs/webpack …);

5、熟悉移动端开发、自适应布局和开发调试工具,熟悉hybrid app开发;

6、掌握一门后端语言(node/java/php...),对前后端合作模式有深入理解;

7、有良好的产品意识和团队合作意识,能够和产品、UI交互部门协作完成产品面向用户端的呈现;

8、有技术理想,致力于用技术去推动和改变前端研发;

9、熟悉Vue/React/ReactNative优先,有BAT等公司经验优先;

高级Android开发工程师

岗位描述:

1、负责58同城App的研发工作;

2、肩负平台化任务(插件框架,Walle,Hybrid,WubaRN) ;

3、维护和开发服务库,公共库的工作;

4、调研Android前端技术;

5、提升开发效率和应用性能;

职位要求:

1、2年以上的Android开发工作经验;

2、精通Java语言,精通Android Studio开发,了解Gradle编译;

3、精通常用算法、数据结构和架构设计;

4、了解Android性能限制及优化方案;

5、了解常用的开源工具:Volley,RxJava,Fresco等等;

6、了解git, maven等等工具;

7、有插件开发经验,Hybrid开发经验,ReactNative开发经验优先;

8、积极主动、喜欢挑战,有强烈的创业精神,能承受高强度的工作压力;

以上如有小伙伴感兴趣,请发送简历到:

liunz@58ganji.com

  • bigdog
  • heavypanda
需要 登录 后回复方可回复, 如果你还没有账号你可以 注册 一个帐号。