博物馆AR虚拟合影
一、项目概述
该项目是将博物馆中的文物虚拟化,利用AR增强现实技术与到访的游客进行合影,让文物不再只可远观,带来别样体验。有APP和服务端,APP进行AR合影后将图片上传到服务器,游客通过扫描合影图片右下角的二维码从服务器上下载图片保存。
二、开发环境
win10
jdk 1.8
android studio
三、项目体会
其实做AR的sdk还是非常多的,一开始用的是artoolkit,免费但其实实在有点不好用。。。作为JAVA脑残粉还是喜欢封装好了的各种代码。。搞C++还是太吃力,特别是甲方需求还会一直变。。改的整个流程都变了。。好坑。。然后全部推倒重来,就没用artoolkit了,想用高通ar做,高通ar好像是不开源但有免费的版本,高级的版本是要收费的,但其实只是用了高通的加载现实情景这块,加载文物的obj模型用的是JPCT库,这个库国内文章也很少。。看着官方文档API一步一步自己慢慢摸索。。坑一个一个踩过去。。
三、项目关键代码
主要代码在ImageTargets这个类里,APP里整合了高通AR(vuforia)和JPCT库。
贴一下JPCT库的API:http://www.jpct.net/jpct-ae/doc/
首先要清楚的是,一个3d模型文件是由若干个三角片组成的,常用的obj格式是包括了obj模型文件,mtl材质文件,jpg纹理文件。可以在geomagic中查看编辑obj文件。
1.加载obj模型
传入obj模型文件以及其对应的材质文件,第3个参数是初始的放缩倍数,可以自己调
返回的是一个object3d[] 类型的数组,之后需要把整个数组对象融合成一个模型对象
tmp = Loader.loadOBJ(mActivity.getAssets().open("01.obj"),mActivity.getAssets().open("01.mtl"), 2);
2.加载模型的纹理
这里起码坑了我一个下午加晚上吧。。。官方给的demo是加载obj之后set纹理的。。但其实这样做不好我觉得。。官方给的api里loadOBJ这里有说会自动注入纹理的,先预处理好纹理,然纹理管理器自动注入更方便
//加载自己的纹理,放到纹理管理器中,注意这里纹理的像素都是2的幂次的,用geomagic导出来的就好
com.threed.jpct.Texture t1 = new com.threed.jpct.Texture(getAssets().open("01Image1.png"));
TextureManager.getInstance().addTexture("01Image1.png",t1);
3.模型与现实场景的融合
JPCT库里有个world对象表示整个世界,控制所有物体,sun对象表示太阳,用来控制光源的
比如:
world.addObject(Constant.cyclinder1); //往世界里添加一个对象
world.removeAllObjects(); //移除世界里所有对象
更多方法还是自己查API吧
4.绑定手势【平移、旋转、缩放】
要想通过手势对模型进行操作的话,首先得明白3点:
[1].AR中的坐标系是怎样的?
[2].模型的变换是如何实现的?
[3].从手势的变换中我们要提取什么?
首先是第一点:AR中的坐标系是怎样的?
手机屏幕为x,y轴平面,就是相当于普通的二维平面,但作为一个3d立体的ar模型,z轴猜一猜当然就知道是深度啦!x是屏幕的宽,y是屏幕的高,没记错的话,就跟安卓开发里屏幕像素点的x、y正半轴的方向是一模一样的
首先是第一点:模型的变换是如何实现的?
在这里我把它分为2个部分来谈,第1个是平移和旋转,第2个是缩放。因为缩放相对的是屏幕深度,而平移旋转是相对屏幕这个二维平面。
平移旋转:这个其实可以降阶到二维平面来谈吧,因为并没有涉及到深度问题,模型的坐标我们是用该模型的中心点定义的,二维平面的坐标就是(x0,y0),也可以看成一个1*2的矩阵,要想变换得到另一个坐标(x1,y1),也就是另一个1*2的矩阵,学过线性代数的同学应该都知道平面坐标的变换其实都是通过乘以矩阵的形式来实现的,旋转平移都可以。线性代数和高等数学需要补补的同学可参看如下的平面坐标变换的例子:
https://blog.csdn.net/u012139398/article/details/39618517
缩放:缩放其实比较简单,就是改变了模型的深度,把z轴的值变大变小了而已。
下面是滑动是我通过模式区分旋转还是平移进行操作,以及缩放
//手指在滑动时
if (me.getAction() == MotionEvent.ACTION_MOVE && me.getPointerCount()==1) {
// 计算x,y偏移位置及x,y轴上的旋转角度
float xd = me.getX() - xpos;
float yd = me.getY() - ypos;
xpos = me.getX();
ypos = me.getY();
if(Constant.MODE==ROTATE) {
rotateX = xd / -100f;
rotateY = yd / -100f;
}
else if(Constant.MODE==TRANS && xd<40f && yd<40f) {
translationX = xd / 40f;
translationY = yd / 40f;
}
return true;
}
缩放这里实现了这个类ScaleGestureDetector.OnScaleGestureListener,重写onscale方法就可以啦
@Override
public boolean onScale(ScaleGestureDetector detector) {
Log.i(LOGTAG, "ON SCALE");
float span = detector.getCurrentSpan();
scale=span/curSpan;
curSpan = span;
return false;
}
5.二维码的编制
二维码有专门的库可以比较方便的用,传入需要编制的url地址,以及被编制的图片,就可以生成对应的二维码了,扫一扫就ok哦,当然你的服务器要有这个url链接。
因为这里需要把二维码和合影图片融合在一起,也搞了我不少时间,最后我在ImageTargets的951行封装了这个融合二维码到原图片的方法。
public static Bitmap createQRImage(String url, ImageView sweepIV, int QR_WIDTH, int QR_HEIGHT );
6.服务器端
服务器端就用spring mvc简单搭了个上传图片和下载图片的功能,用时间戳表示每张合影图片的名字,每个合影url对应服务器的一张图片,游客扫一扫的时候就能下载这张图片啦~
测试时好像用1M带宽的阿里云学生机上传速度和下载速度有点慢,每张合影图大小大概在2mb左右
7.obj模型怎么来?
这个有专门的公司就是扫描原始文物然后输入到电脑中转成obj文件的。。。好像价格也挺贵。。编辑obj文件可以用geomagic,感觉还挺好~【作为一名伪全栈工程师学生党,当然要前端、后端、数据库、服务器运维、模型处理、ps、美化都会的啦,苦逼脸】
四、可能出现的问题
1.有些机子的安卓版本不同,现在代码里是 compileSdkVersion 23,出现不能初始化高通ar的话,可能由于安卓版本太低,没自动提示让你开相机权限
2.上传图片时要保证网速ok,我先处理的是本地合影的数据,如果网速太慢编好二维码之后不一定合影图片已经上传到服务器,会404,网速不太慢大概过个3s的样子就传到服务器了,阿里云主机还是贵啊。。带宽不够。
五、项目展示
六、源码!!!
高通ar去水印需要付费,只提供免费版高通ar的sdk的key,普通版的自己去高通ar官网就好啦~有偿去水印呀,key在SampleApplicationSession这个类的384行进行输入
项目地址:https://github.com/yzl123456/AR-photo
你们的每个star和点赞都是对我最大的鼓励!