传感器获取旋转矩阵R,机身坐标系和世界坐标系的映射remapR,获得旋转角度 orientationValues;


Android写了个简单的增强实境demo,程序包括三个图层:

1)摄像头拍摄的实境

2)OpenGL绘制的3D物体

3)一些文本提示信息

一、图层的叠加

这个实现的方法应该很多,我选择的是在FrameLayout上叠加SurfaceView、GLSurfaceView、TextView,分别对应上述的三个图层。

FrameLayout mainLayout = (FrameLayout) findViewById(R.id.MainLayout);

mainLayout.addView(views, new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));

问题1:

GLSurfaceView glView = new GLSurfaceView(this);

glView.setEGLConfigChooser(8, 8, 8, 8, 16, 0);

// 我参考的书上没有上面这句,在2.2模拟器上调试可以正常运行,但在2.0.1的Milestone真机上程序会死掉

glView.setRenderer(glRenderer);

问题2:

GLSurfaceView和SurfaceView叠加的问题,按照正常的逻辑,应该先添加相机预览使用的SurfaceView,然后添加 OpenGL绘图使用的GLSurfaceView,并 GLSurfaceView.getHolder().setFormat(PixelFormat.TRANSLUCENT)。如果你这样做了,你会发现实际程序运行的时候是反的,相机预览完全遮盖了OpenGL绘图。我尝试过别的View跟SurfaceView叠加,没有类似的问题,显示顺序就是 addView顺序,唯有两个SurfaceView叠加会出这种问题。在这里 可以看到类似问题的讨论,不过我没遇到他们所说的切到后台再切回来后乱序的问题。在另一个网站上看到如下解释“Multiple surface views works, but you don't want to have them overlap because the Z-order of them is undefined”。

二、传感器获取Orientation

先注册两个传感器TYPE_MAGNETIC_FIELD和TYPE_ACCELEROMETER,在onSensorChanged事件里获取传感器值accelerometerValues和magneticFieldValues,再把两个传感器的值扔进SensorManager.getRotationMatrix(R, I, accelerometerValues, magneticFieldValues),得到R和I矩阵,I矩阵是个地磁偏角,用不到,R是rotation矩阵,关键是这个。

三、机身坐标系和世界坐标系的映射

机身坐标系的定义是,将屏幕按照默认显示方向放置,然后X轴从左向右,Y轴从下至上,Z轴从里到外,右手坐标系。注意,当你切换显示方向,比如从portrait切到landscape,这个坐标系是不跟着变的,文档里用的词是screeen而不是display。

世界坐标系的定义是,Y轴指向地磁北极,Z轴逆重力方向指向天空,X轴按照右手系可知是指向东方的。下面的函数用来指定把机身坐标系的X和Y轴映射到世界坐标系的哪个轴。我选择的工作姿态是把手机横持在身前,屏幕朝向自己,所以使用如下的设定,这样当摄像头指向地磁北极,屏幕与重力方向平行,屏幕法线与重力方向垂直时,获得的orientaion三个值都是0。

SensorManager.remapCoordinateSystem(R, SensorManager.AXIS_Z, SensorManager.AXIS_MINUS_X, remapR);

然后就可以得到orientaion了:

SensorManager.getOrientation(remapR, orientationValues);

orientationValues是个3个float的数组,依次表示Azimuth、Pitch、Roll,就是在世界坐标系里,机器绕Z轴、X轴、Y轴旋转的角度。

Android提供了一个TYPE_ORIENTATION传感器,也可以获得这三个量,我理解这只是个虚拟的传感器,还是通过类似上文的过程从地磁和重力传感器计算出来的,文档里不推荐使用这个传感器。但是这里有个问题,我得到的值的取值范围跟文档里TYPE_ORIENTATION描述的不一样,正值方向也跟文档里getOrientation描述的不一样,不知何故。

四、OpenGL绘图

此前完全没有接触过OpenGL,所以从0开始。Android的OpenGL只是对OpenGL ES API的简单封装,Google的文档里没有提供函数的说明,可以去OpenGL ES的官网 查询。我使用的参考书是《Android应用开发揭秘》,其OpenGL基础部分基本上是抄的周炜的NeHe中文教程 ,只是做了一下对Android移植。这本书里的例子一个很别扭的地方是,他描述一个三角形的坐标时使用了IntBuffer,一个单位值是 0x10000,但是OpenGL的函数使用的都是浮点数,跟这个0x10000完全不是一个量级的,很难理解。所以我该用了FloatBuffer,相应的glVertexPointer函数使用GL10.GL_FLOAT,这样整个坐标量度就能统一了。

五、世界坐标系和OpenGL坐标系的对应

OpenGL的坐标系是屏幕中心为源点,X轴从左向右,Y轴从下向上,Z轴从里到外,注意与前文所述机身坐标系不同的是,OpenGL坐标系是根据显示方向变化的,即其参照物不是screen,是display。增强实境要求的效果是,OpenGL绘制的物体要跟摄像头运动方向相反,因为加速度传感器的限制,用户只能站在固定位置,所以摄像头不会平移,只是绕世界坐标系的坐标轴旋转,即当摄像头从正前向左旋转时,OpenGL在屏幕上绘制的物体要从中央向右旋转,以造成虚拟物体停留在现实世界某个位置的感觉。我尝试了多种实现世界坐标系和OpenGL坐标系映射的方法,最后发现最简单的还是使用 gluLookAt,其有三组参数,

第一组是观察点位置,即脑袋位置;

第二组是场景的中心,即你看向的那个点,我实验了一下,貌似这个只是描述方向,在这个方向上距离有多远不影响显示效果;

第三组是观察时的上方向,即脑袋顶指向的方向。调用此函数后,显示出来的图像就是你在虚拟世界里把脑袋摆到那个位置看到的景象。尝试了几组参数,主要是调整了一下正负符号,选定参数如下:

GLU.gluLookAt(gl,


//第一组是观察点位置,即脑袋位置;

0, 0, 0,


//第二组是场景的中心,即你看向的那个点,

(float)Math.sin(orientationValues[0]), -(float)Math.sin(orientationValues[1]), -(float)Math.cos(orientationValues[0]),


//第三组是观察时的上方向,即脑袋顶 指向的方向(一般向上)。

(float)Math.sin(orientationValues[2]), (float)Math.cos(orientationValues[2]), 0


);

至此,可以说有么点样子了,镜头旋转的时候,屏幕上的虚拟物体会反向旋转,但是因为没有距离对应,所以还没有停留在固定点的效果。

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值