增强现实引擎实现细节

来源:互联网 发布:北京大数据培训多少钱 编辑:程序博客网 时间:2024/06/01 20:25

增强现实引擎实现细节


@Autrhor: Ling Bao@Data: August 3, 2017

1 基于标记/自然特征的AR引擎

参考资料:ARToolkit / Mastering OpenCV with Practical Computer Vision / Mastering OpenCV 3 - Second Edition

1.1 ARToolkit分析

(1) ARToolkit特性

A. 鲁棒跟踪,包括基于标记的跟踪与基于特征的跟踪;
B. 强用力的摄像头/相机标定支持(摄像头/相机标定需要确定中心点坐标(光心)、摄像机实际焦距(fx=αf,fy=βf)与镜头畸变参数(取决于畸变模型);主要提供两种标定方法:两步标定法与一步标定法;当已知摄像头/相机内部参数时,ARToolkit程序就可以在运行时自动矫正摄像头畸变);
C. 多目标支持与单目相机支持;
D. 移动设备优化;
E. Unity3D与OpenSceneGraph支持。

(2) 原理阐述
ARToolkit通过计算机来判断是否找到标记及在标识识别成功后计算虚拟物体所需叠加的位置。因此包含主要核心部分:摄像机标定标记检测标记识别3D注册

(3) ARToolkit跟踪原理图

ARToolkit跟踪原理图

A. 摄像机捕获真实世界的视频,并将其传送给计算机;
B. 软件监控视频流中的每一帧图像,并在其中搜索所有的正方形(方形标记);
C. 如果找到并且匹配和识别方形标记(方形和图案所嵌入的图像)后,ARToolKit 将通过数学运算计算出相对摄像头的图形标记位置和图案方向(投影变换矩阵/位姿);
D. 利用摄像头位姿来调整模型的位置和方向;
E. 将模型渲染到标记卡所在(帧画面)的位置;
F. 最后的输出将显示在显示屏上,所以用户可以通过现实世界的视频流看到渲染的图像模型,并且效果看起来与摄像机视图相同。

(4) 基于标记的AR系统流程图

Created with Raphaël 2.1.0系统初始化获取视频帧值计算分割阈值图像分割模板匹配匹配成功计算变换矩阵三维模型渲染yesno

A. 系统初始化:初始化视频捕捉、载入模板及其对应虚拟对象、相机内参(光心|焦距|畸变参数);
B. 标记检测(搜索整个图像,寻找含有正确标识模板的标记):计算分割阈值、图像分割、模板匹配、计算投影变换矩阵;
C. 3D虚拟对象模型渲染:匹配成功则利用投影变换矩阵对3D虚拟对象模型进行渲染并显示。

(5) 应用实例详析simpleTest.c

A. 参考资料:ARToolKit教程:开发第一个程序;
B. 应用程序执行步骤如下所示:

模块 流程 初始化 a. 初始化视频捕获,读取标识文件和相机参数 b. 获取一帧输入视频图像 主循环 c. 标识检测与识别 d. 计算相机投影转换矩阵 e. 标识上叠加虚拟对象 关闭 f. 关闭视频捕获

初始化:开始视频捕捉;读取标识文件以及摄像机特性参数;相关函数包括 arVideoOpen(),arVideoInqSize(),arParamLoad(),arInitCparam(),arParamChange Size();
工作循环:获取一帧输入视频;标识检测与识别;获取标识后计算摄像头-标识的位置转移矩阵,记录标识参数(位置及方向);计算标识-模型的坐标映射关系,匹配虚拟物体,最终将虚拟物体叠加在真实场景中输出;argMainLoop(),glutMainLoop(),arDetectMarker();
关闭视频与资源释放:相关函数包括arVideoCapStop(),arVideoClose(),argCleanup()等。

C. 程序调用关系如下所示:

ARToolkit步骤 函数 应用程序初始化 init 获取一帧视频 arVideoGetImage 标识检测 arDetectMarker 计算投影转换矩阵 arGetTransMat 叠加虚拟对象 draw 关闭视频捕获 clearup


2 几种系统的坐标系分析

参考资料: Unity3D坐标系 / Unity3d数学基础之坐标系 / LearnOpenGL-CN / OpenGL坐标系与几何变换 / 投影变换–透视投影和正交投影 / OpenGL学习脚印: 理解坐标系及坐标变换(上) / OpenGL学习脚印: 理解坐标系及坐标变换(下)

2.1 Unity3D坐标系(左手坐标系)

(1) 世界坐标系(左手坐标系)

世界坐标是按照笛卡尔坐标系定义出来的绝对坐标系,下面的各种坐标系都建立在世界坐标的基础上;在Unity3D中我们可以使用transform.position来获取场景中一个物体的世界坐标,通常情况下编辑器中的Inspector窗口是以世界坐标来描述一个3D物体的位置的,除非当一个3D物体存在父物体的时候,它会以相对坐标来描述其位置。

(2) 屏幕坐标系
屏幕坐标是以像素来定义的,它的范围是以左下角为(0,0),右上角为(Screen.width,Screen.height)定义的这样一个矩形,屏幕坐标是一个3D坐标,Z轴用相机的世界单位来衡量;屏幕坐标和相机之间满足两个条件:
creen.width=Camera.pixelWidth,Screen.height=Camera.pixelHeight
例如:相机正对着场景中的原点(0,0,0),相机的Z轴分量为10,按照屏幕坐标的定义,假设屏幕大小为800×640,则原点转化为屏幕坐标后应该是(400,320,10);在Unity3D中我们可以使用WorldToScreenPoint来将一个世界坐标转换为屏幕坐标;鼠标位置Input.mousePosition获取的是屏幕坐标;
例如:场景分辨率1024×768,如果屏幕被缩小至100×100,那么点击右上角是100×100,而不是分辨率的1024×768
(3) 视口坐标系
视口坐标是标准化后的屏幕坐标;视口坐标是一个3D坐标,Z轴用相机的世界单位来衡量;视口坐标用0到1间的数字来表示,左下角为(0,0),右上角为(1,1);Unity3D使用WorldToViewportPoint将世界坐标转换为视口坐标;
例如:将相机正对着场景中的原点(0,0,0),相机的Z轴分量为10,按照屏幕坐标的定义,假设屏幕大小为800×640,则原点转化为视口坐标后应该是(0.5,0.5,10)
(4) GUI坐标系
GUI坐标是通过OnGUI方法绘制UI时使用的坐标;该坐标系和屏幕坐标类似,同样用像素定义;左上角为(0,0),右下角为(Screen.width,Screen.height);GUI坐标是一个2D坐标(绝对坐标)。
(5) 其它知识
每个对象都有自己的局部中心局部坐标系,局部坐标系是与所选对象具有特定关系的坐标系;
枢轴点(或变换中心)是发生旋转或发生比例变形的部位;
2.2 OpenGL坐标系(右手坐标系统)

(1) OpenGL顶点变换过程图

OpenGL顶点变换过程图

(2) 整体流程及转换过程图
整体流程及转换过程图
(3) 世界坐标系(世界空间/右手坐标系统)
(4) 对象坐标系(局部空间/物体空间)
对象被应用于任何变换之前的初始位置和方向所在的坐标系,也就是当前绘图坐标系;该坐标系不固定且仅对该对象适用;默认情况下,该坐标系与世界坐标系重合;当用函数glTranslatef(),glScalef(),glRotatef()对当前绘图坐标系进行平移、伸缩、旋转变换后,世界坐标系和当前绘图坐标系不再重合;改变以后,再用glVertex3f()等绘图函数绘图时,都是在当前绘图坐标系进行绘图,所有函数参数都是相对当前绘图坐标系来讲。备注:模型变换(对象坐标系->世界坐标系),视变换(世界坐标系->眼坐标系)
(5) 眼坐标系(相机坐标系/观察空间/视觉空间)
GL_MODELVIEW矩阵是模型变换矩阵和视变换矩阵的组合Mview×Mmodel;使用GL_MODELVIEW矩阵就可以使对象从对象坐标系转换到眼坐标系;OpenGL不存在单独的模型变换Mmodel和视点变换Mview;ModelView变换把场景与我们的观察位置对应起来;默认情况下,眼坐标系与世界坐标系是重合;使用函数gluLookAt()可以指定眼睛(相机)的位置和眼睛的方向;
使用glTranslatef()glScalef(),glRotatef()函数可以对对象坐标系进行变动;使用voidgluLookAt()函数可以对眼坐标系进行变动;两者可以达到相同的变换效果,相当于对象不动移动相机,和相机不动移动对象。
(6) 裁剪坐标系(裁剪空间)
眼坐标到裁剪坐标由投影完成,眼坐标通过乘以GL_PROJECTION矩阵变成了裁剪坐标;GL_PROJECTION矩阵定义了视景体,即确定哪些物体位于视野之内,位于视景体外的对象会被剪裁掉;除了视景体,投影变换还定义了顶点投影到屏幕上的方式,包括:透视投影和正交投影。
(7) 归一化设备坐标系
由裁剪坐标系下通过除以W分量得到,该操作为透视除法;归一化设备坐标很像屏幕坐标,但还没有经过平移和缩放到屏幕像素;(x,y,z)范围均为[1,1]
(8) 屏幕坐标系(屏幕空间)
将屏幕上的设备坐标称为屏幕坐标;设备坐标又称为物理坐标,指输出设备上的坐标;设备坐标用对象距离窗口左上角的水平距离和垂直距离来指定对象的位置,以像素为单位表示;设备坐标X轴向右为正,Y轴向下为正,坐标原点位于窗口的左上角;从归一化设备坐标到屏幕坐标基本上是一个线性映射关系,通过对归一化设备坐标进行视口变换得到;可以用函数glViewport()定义渲染区域的矩形。