特色的岑溪网站开发/百度网盟推广官方网站
背景
AR场景中需要对3D模型进行一个真实水平面的位置摆放,与摄像头拍摄的真实世界水平面类似,模型可任意的放置于这个水平面上。
于是存在几个问题
- 水平面怎么计算并生成?
- 计算出的水平面如何与模型所在AR的3D世界坐标对应起来?
- 如何保证把模型放置在了平面上,随着手机的任意移动和转向模型始终“面向观众”?
- 用户使用时的场景千奇百怪,就算用户已经摆放好模型到一个平面上,当他移动手机到一个新的环境重新去摆放,怎么保证还可以继续正确的放置到之前已经计算好的平面上?
水平面的计算
在3D世界中,平面可通过法向量和平面上的一个点来确定,我们通过借助手机的重力sensor实时的拿到手机的反重力向量,从而确定了平面的法向量,此时我们只需假定一个任意点则可生成任意个水平面。
[_motionManager startDeviceMotionUpdatesToQueue:self.queue withHandler:^(CMDeviceMotion * _Nullable motion, NSError * _Nullable error) { /*** gravity*/CMAcceleration gravity = self.motionManager.deviceMotion.gravity;Ant3DIMUData* imuDataGravity = [Ant3DIMUData instanceWith:Ant3DIMU_MAGNETIC x:gravity.x y:gravity.y z:gravity.z time:[self getTimes]];[[NSNotificationCenter defaultCenter] postNotificationName:kAnt3DIMUDataNotification object:imuDataGravity];}];
水平面与模型所在的3D世界坐标对应
当我们已经计算出平面时,如何与3D世界的坐标系统一?我们知道当3D场景建立后,3D场景的世界坐标系便已经确定,对应到真实世界可以是任意方向和位置的, 我们所有的计算都需要基于这个世界坐标系去生成,所以我们只需要在世界坐标系建立的那一刻通过sensor计算出的旋转矩阵作用到相机节点便可以拿到3D世界中这个水平面的法向量,再通过规定任意一个点便确立了这个平面。
模型的水平面放置和“面向观众”
每当点击屏幕,模型都会摆放到点击点对应的这个平面上,这个过程可以抽象成通过相机视口发出了一条射线撞击这个平面,这不就是一个直线与平面的交点的问题吗?
如果直线不与平面平行,将存在交点。如下图所示,已知直线L过点m(m1,m2,m3),且方向向量为VL(v1,v2,v3),平面P过点n(n1,n2,n3),且法线方向向量为VP(vp1,vp2,vp3),求得直线与平面的交点O的坐标(x,y,z):
将直线方程写成参数方程形式,即有:
将平面方程写成点法式方程形式,即有:
则直线与平面的交点一定满足式(1)和(2),联立两式,求得
如果(3)式中分母(vp1∗v1+vp2∗v2+vp3∗v3)为0,则表示直线与平面平行,即直线与平面没有交点。求解出t后,然后将t代入式(1)即可求得交点O的坐标(x,y,z)。
ps: 直线为相机节点的forword向量与它的原点确定
最终作用模型的矩阵Matrix我们已经确定了Translate(T), 决定每次模型放置后是否都”面向观众”的Rotation(R)计算过程,我们需要知道:
- 最终模型的朝向这个目标向量:”面向观众”的向量(相机节点在已生成的平面的投影点subPoint与之前计算的交点interPoint组成的向量) subPoint - interPoint
- 当前模型的forward向量
- 拿到2到1的旋转矩阵即可
至此我们已经可以把模型摆放到水平面并面向观众了,只需要处理一些异常无交点、交点距离过远(如手机朝天点击,或者模型放置的特别远小)等情况便可。
后续
当然AR仅仅放置平面是没有什么增强现实体验的,这里离不开跟踪算法,可对摄像头位姿变化进行实时的估计和定位(一帧内即可完成初始化),利用得到6自由度空间位置信息,用户根据自己的喜好,在可视范围内放置虚拟模型,虚拟模型与现实世界在现实空间无缝融合,实现增强显示的效果。
所以针对“用户使用时的场景千奇百怪,就算用户已经摆放好模型到一个平面上,当他移动手机到一个新的环境重新去摆放,怎么保证还可以继续正确的放置到之前已经计算好的平面上?”这个问题,通过slam可以不断的更新相机节点的空间位置,从而每次放置都需要考虑相机节点的Translate(T)和Rotation(R),重新计算与已存在在3D世界的水平面的撞击点和朝向便得以解决。
下面是几个平面的演示效果: