前端AR探索与实现【前篇】
一、前言
首先想请问下大家生活中体验过AR、VR么?比如当年比较流行的扫卡片成像AR,又或是你去逛商场时的VR体验店,那
二、什么是AR、VR
AR —— 增强现实
它通过电脑技术,将虚拟的信息应用到真实世界,真实的环境和虚拟的物体实时地叠加到了同一个画面或空间同时存在
AR:淘宝、apple商店的物品展示、支付宝扫福、pokemon、英雄联盟S7鸟巢的远古巨龙
VR —— 虚拟现实
实际上是一种可创建和体验虚拟世界(Virtual World)的计算机系统,我们说的比较多的都是沉浸式虚拟现实
VR:头号玩家、刀剑神域、VR游戏(minecraft)
以上就是我们现在市面上对于AR以及VR技术的一个概念定义,那我们标题中前端AR又是什么,它就是AR本身?亦或是AR的子集?
答案可显而知,它是属于AR技术的一个子集,也称为webAR。
三、前端AR(webAR)
众所周知,我们市面上AR功能普遍都是实现在客户端或终端游戏上(如unity3D),相对来说客户端或游戏端的小伙伴可能会比我们更熟悉,而我们这节课将从偏前端的webAR技术出发,讲述我们在前端AR上的探索、难题攻克及实现
前端AR,即webAR
webAR就是在Web端集成AR的功能。webAR使用WebRTC、WebGL和现代传感器API的组合技术,通过Web浏览器提供对基于Web的增强现实的访问与实现。
四、什么是WebGL和WebRTC
WebGL
WebGL是一种3D绘图协议,该绘图技术标准允许网页在渲染图形时可以使用硬件加速来提高渲染效率。
WebRTC
WebRTC则是一种实时通讯技术,它提供的实时视频流处理技术对AR技术意义重大,因为AR中的许多识别功能需要实时的摄像头数据以及传感器数据才能保证识别结果的精确。
WebGL和WebRTC得益于前端元素canvas标签的引入,canvas标签在前端H5主要是提供画布用来进行绘图的功能,canvas使网页终于拥有了方便使用的实时图形渲染接口,目前的webVR与webAR画面都是渲染在这一标签中的。
您的手机系统亦或是浏览器是否支持canvas、是否支持这俩技术都决定着你的手机能否支持AR/VR,那
五、如何判断web端是否支持
这里的话主要涉及到两个步骤判断,其中我们第一步就需要用到ThreeJS库相关的一个插件,这里有人会问threejs是什么东西,它是一个基于WebGL的JavaScript 3D库,我们将其当成一个javascript库就可以了,对于这个库究竟是用来干什么的我们稍后再来介绍。
1、Three.js的话它有一个插件Detector.js,这个js插件提供了检测浏览器是否支持canvas和webgl的函数,具体的话可自行百度其API,所以只要你在项目里安装此插件,就能很快很方便地判断出您的浏览器是否支持canvas与webgl协议;
2、利用浏览器的 getUserMedia 方法用来检测是否支持摄像机视频流,也就是webrtc是否支持,当然浏览器如果连getUserMedia这个方法都没有,那肯定可以直接pass了;
对于webgl、webrtc、getUserMedia目前整体的一个兼容性我们可以自行上 can I use 网站进行查询,据我们之前开发H5 AR小游戏实际应用测试下来,目前在ios 12.1以上版本及Android 9以上版本几乎都是支持的
六、技术选型
在了解完AR/VR的一些相关知识以及如何判断浏览器或设备是否支持AR技术后,请务必跟随我的脚步,一起体验下我们当时开发AR小游戏的一个思路历程,那了解完基础概念后,首先肯定是需要调研并进行一个技术选型。
在网上经过多番百度、google及阅览他人博客后,找到了相对比较成熟,又或者说就只有这几种技术方案,分别是高通Vuforia、EasyAR、太虚AR、AR.js、mindAR,其中
1、高通Vuforia或者EasyAR在unity3D引擎比较成熟,绝大多数应用于APP,EasyAR有提供webAR技术(但需要花钱购买token和云图库,利用getUserMedia视频流实时传图像到其云台识别):成本相对比较大,且云识别终归来说会有延时
2、太虚AR:只支持小程序AR,没有webAR的jssdk支持
3、AR.js:(4.4k星星),用于 Web 增强现实的轻量级库,具有图像跟踪、基于位置的 AR 和标记跟踪等功能
4、mindAR:(1.6k星星)相比AR.js多了面部跟踪
所以综上来说AR.js和mindAR都符合我们的需求,但刚好我们当时开发AR小游戏需要涉及到面部追踪功能,同时mindAR的API也更为的简单,源码更为的简洁方便我们进行二次开发,所以我们最终是将mindAR作为我们的一个技术方案。
讲完技术方案的选型后,我们再来认识下开发过程中对我们比较重要的两个图形库。
七、Three.js 和 Afarme
1、Threejs
OpenGL是市面上最常用的跨平台图形库,WebGL则是基于OpenGL设计的面向web的图形标准,提供了一系列JavaScript API,Three.js是通过对WebGL接口的封装与简化而形成的一个易用的图形库
它提供了创建场景、载入3D模型、对AR模型进行位移、旋转、缩放、获取模型外层容器、获取模型材质、获取模型3D坐标系的空间坐标等功能,当然更多详细的可以到threejs官网查看其API文档,毕竟是很庞大的一个库,细说肯定是说不完的啦,甚至后续大家有时间可以将其当成一门技术来学习。
2、Aframe(《萌葱闪现篇》再讲)
基于Threejs框架的一个web框架,采用三维游戏设计模式实体-组件-系统(entity-component-system)(ECS)架构,提供了 a-scene、a-entity、a-box等组件标签供我们直接使用,由于其是threejs的抽象,每个实体都有其自己的THREE.Object3D对象,我们可通过document.querySelector('a-scene').object3D或entityEl.getObject3D('mesh')来获取,同时它也提供了网页版视觉3D检查器供我们调试。
好了,讲到这里的话大家其实新手指引差不多已经走完了,而且threejs和Aframe这俩个装备也给大家装备上了,那接下来就一起走进2023年年会AR小游戏冲向星辰与萌葱闪现,一起探索思路以及实现它们!
八、冲向星辰
首先我将对需求玩法进行一个大致的介绍:
1、类似雷电3,平面 里程式 闯关游戏
2、识别面部将聪哥定位为脸部中心点上,通过面部追踪操纵葱哥,躲避石头,抵达终点
3、太阳加速,无敌3秒
这里请大家先暂停往下看,给自己10分钟左右的时间,想一想思路,换做是你来开发的话,你要怎么去实现?
Q:
那一上来肯定是要想一下石头、太阳、葱哥这些物体你要怎么添加?
A:
1、往AR场景里去添加实体(控制属性过程繁琐、需要按3D坐标系来计算,不懂点图形学有点难上手、色差严重)
2、H5上添加固定or绝对定位的元素(基于2D交互情况下)
所以我们最终是使用了后者,也就是第二种方式进行我们游戏人物、物体以及障碍物的一个添加
Q:
添加完物体后下一步要干什么?
A:
对没错,就是如何将物体进行定位,取得它在屏幕上的坐标,特别是如何定位面部追踪的葱哥的位置,石头及太阳这种随机生成的我觉得难不倒大家。那葱哥的定位这里就涉及比较多的技术知识点了,比如我们需要用到相机投影、归一化以及标准设备坐标系等相关知识,当然在这之前我们也先简单地了解下脸部识别的一个流程。
如上图,这里每一步我都大致稍稍讲一下,首先肯定是要
1、初始化我们的AR插件了,初始化后其暴露诸如相机、场景、渲染器对象等供我们使用其相关的一些API方法。
2、插件识别到我们的脸部后,会调用threejs进行一个轮廓的绘制,也是识别到的脸部的范围,你可以当圈出了一块空间模型,这时如果你想查看其识别到区域,你就可以调用Threejs里的灯光投影,对这块模型进行投影,那就会看到如下图这种效果(挡了一块面膜在你脸上)。
3、既然轮廓出来了,我们需要拿的就是上图中间标红的点的坐标,但是threejs为了节能,在视频流工作后这个点的坐标xyz并不会去实时计算,也就是你去获取的话都会是0,0,0,所以需要调用其computeBoundingBox的方法主动让其进行计算,那么这时我们就能得到这个红点的3D世界坐标了。
咦对了,你是不是发现了盲点,为啥叫3D世界坐标而不是屏幕坐标呢,那肯定是没那么容易就get到我们想要的东西啦,不然就不用探索了哈哈,那这里就要开始涉及我们上面刚刚提到的几个知识点了,首先认识一下什么是相机投影。
● 相机投影
即将我们设备摄像头捕捉到的图像轮廓投影到我们的手机屏幕上来,举个简单的例子,我们的冲向星辰不是要用到面部识别与追踪嘛,当前置摄像头扫出我们脸以及背后的背景环境出现在屏幕上时,我们这时把摄像头呈现给我们的镜像也当成是一个对着我们的3D环境,手机屏幕相当于只是一面透明的玻璃面而已,那这时我们透过屏幕在对面也有了个镜像相反的头像,这时就需要在这个头像后面进行投影,将其轮廓映射到我们这个玻璃面,也就是手机屏幕上来,总结成一句话就是
- 3D转2D
● 标准设备坐标与归一化
具体的方法我们可以通过Threejs的Vector3对象将我们拿到的3D世界坐标给实例化为一个三维向量/点,然后调用这个实例的project方法(vector3.project),将我们插件初始化时暴露的camera相机对象作为投影目标参数传进去,就能计算出这个 3D 世界坐标在我们屏幕上的 2D 位置坐标,但这个坐标的 x 和 y 是介于 0 和 1 之间的,仍旧不是我们真正需要的那个最终坐标(我们手机屏幕宽高肯定不止1像素),我们称这时的坐标为标准设备坐标,而这一步也被称之为归一化。
看到这里,你可能会说弄了大半天还是没计算出来,但换个角度看,你已经确确实实将坐标由3D转2D了呀,至少没了个z,这不就是成功了一大步。
九、设备坐标转屏幕坐标
不要灰心,设备坐标与屏幕坐标同为2D坐标系,它们之间终归必然存在着某种联系或换算关系,最直观能想到的就是它们是不是经过了比例换算,果不其然,通过不断的度娘,确确实实找到了标准设备坐标与屏幕坐标的转换公式,你看这不就解决了吗,屏幕坐标不就得以计算出来了,废话不多说直接贴公式:
x屏幕坐标:Math.round(vector.x * halfW + halfW)
y屏幕坐标:Math.round(-vector.y * halfY + halfY)
这里的vector.x和vector.y为点的标准设备坐标x和y,halfW和halfY则是手机屏幕的半宽和半高。
十、冲向星辰开发流程总结
讲了这么多,可能头皮已经有点发麻,这里给大家总的梳理一遍思路流程,方便大家消化。
1、初始化mindAR插件,由其初始化实例解构出渲染器对象、主场景对象、相机对象供后续使用;
2、进行脸部识别,必要时添加灯光进行投影判断轮廓是否OK;
3、让mindAR开始视频流识别工作了,利用computeBoundingBox计算脸部实时坐标
4、利用threejs的Vector3定义一个3维向量,即3D坐标系中一个点,将我们脸部中心点坐标赋值给它(3D世界坐标);
5、将3D世界坐标投影归一化成我们手机屏幕上的标准设备坐标
6、利用转换公式将标准设备坐标转换为屏幕坐标
到这里,坐标就已经拿到了,那后面的交互逻辑比如移动逻辑or碰撞逻辑自己应该知道怎么操作了吧,这方面我这里就不讲了。
十一、兼容问题
当然,在开发过程中还是存在着许许多多的兼容问题,考虑到篇幅的原因,这里就不展开讲了,这边把我们的一个适配兼容方案直接列出来给大家。
1、mindAR脸部识别存在镜像问题,也就是左右相反
解决方案:利用transform:scaleX(-1)进行镜像翻转,然后脸部移动时坐标进行反向计算。
2、灵敏度问题
解决方案:换算坐标时给x坐标增加固定值,相当于移动时x坐标变化增大了。
3、升降式镜头手机存在极少机型获取不到脸部坐标
解决方案:判断上报机型的userAgent,并进行触控模式的降级处理。
十二、结尾
好了,关于年会AR小游戏 - 冲向星辰的一个开发思路就讲解到这里了,总的来说,相比起萌葱闪现,冲向星辰还是相对比较容易实现,只要你能够理清思路,不过前期坐标的一个换算调研还是走了不少弯路,毕竟市面上webAR的参考资料亦或技术博客还是少之又少,特别是图标换算几乎都是图形学和矩阵换算那边涉及的比较多,所以最开始一直探索不到转换的方法,好在最终还是幸运地get到方法并将其解决,受篇幅限制原因,萌葱闪现开发思路探索我将放在下一篇wiki进行讲解,大家敬请期待。
若上述讲解有不对的地方欢迎大家指正,也欢迎对webAR技术感兴趣大佬一起探索AR世界,攻克未知难题,感谢您的阅览~