Android OpenGL射线拾取&手势旋转(一)
2012-02-28 13:18
295 查看
Android OpenGL射线拾取&手势旋转
参考了《OPhone 3D开发之射线拾取》一文。一是通过射线拾取监听正方体各面的点击事件,二是使用绕任意轴旋转实现正方体一直按手势方向进行旋转(即无论正方体如何翻转,旋转方向一直跟随手势方向)。
图1 样例效果图
一、我的OpenGL概念
1)OpenGL坐标系
OpenGL为右手笛卡尔坐标系统。这么描述吧:高抬右手,大拇指向右、食指向上、中指弯90°。大拇指方向为X正轴方向(从左到右),食指方向为Y正轴方向(从下到上),中指呢就是Z正轴方向(从里到外)。
绕轴旋转,遵循右手法则。例如右手握住Y轴,大拇指指向Y正轴方向,四指弯曲方向即为绕轴旋转方向。
(以上手头文档发现都没描述==,记忆里是这样的,有误还请指正!)
OpenGL中从三维场景到屏幕图形要经历如下所示的变换过程:模型坐标->世界坐标->观察坐标->投影坐标->设备坐标。
其中四种坐标经常要在程序中用到:物体坐标(也叫模型坐标、局部坐标),世界坐标,眼坐标(也叫观察坐标)和设备坐标。
世界坐标即刚开始描述的坐标系统,是用来描述OpenGL中的场景。
OpenGL中的坐标变换都是通过矩阵运算完成的,与图形学课本描述一致。由于样例工做了一些自己的矩阵变换,有些概念最好过目一下。小弟也就过目了下^^,了解矩阵为啥都是4X4的,以及简单的缩放矩阵、平移矩阵什么的就成。在这基础上写绕任意单位轴旋转任意角度的矩阵时,就不至于迷茫了。
2)OpenGL库函数
貌似有核心库(gl)、实用库(glu)、辅助库(aux)、实用工具库(glut)、窗口库(glx、agl、wgl)和扩展函数库等==。也就不详细说了(事实上是,我没用过啊T^T)。
样例工程很简单的,知道下Java层的GL10和GLU就成了-_-!
3)OpenGL的学习
空间几何:和我一样已经生疏了的同学,还请搜“空间解析几何与向量代数”。
线性代数:这门课的书,都卖了没啊?仍上学的不算==。
图形学:这个不懂啊,总之学OpenGL要看这个T^T。
OpenGL:OpenGL编程指南^^,就从这开始吧。瞄过,还不错的样子。
小弟路走岔了,不是从事OpenGL的,上述也就是自己瞎磨叽的。不过应该是这样的吧,应该错不了==。
二、射线拾取概念
1)什么是拾取
根据2D屏幕坐标来选取3D空间中图元的操作,就是拾取。
2)拾取射线
搬个拾取原理图过来^^。
图2.2 拾取原理图
z = 0处为视锥体近剪裁面,z = 1处为远剪裁面。拾取射线,就是由触摸位置在近剪裁面上的位置P0,以及在远剪裁面上的位置P1所组成的,其中,P0为射线原点,射线由P0发射指向P1。概括点就是,2D平面的一个点映射至3D空间为一射线。
好吧,这严重涉及了我们工程的目标之一:点击立方体某一面触发某事件。样例工程做的是渲染选中的三角以及提示当前选中了哪面。
三、主要实现内容
1)正方体类:Cube.java
绘制正方体所需的相关信息,以及射线与其的碰撞检测方法(及射线与正方形各个小三角形的碰撞检测)、返回外接圆信息的方法(用于快速排除外切圆区域外的触摸事件)。
2)GLSurfaceView:MyGLSurfaceView.java
专门用来实现OpenGL渲染的SurfaceView,在触屏事件里计算出绕轴的单位向量。
参考了《OPhone 3D开发之射线拾取》一文。一是通过射线拾取监听正方体各面的点击事件,二是使用绕任意轴旋转实现正方体一直按手势方向进行旋转(即无论正方体如何翻转,旋转方向一直跟随手势方向)。
图1 样例效果图
一、我的OpenGL概念
1)OpenGL坐标系
OpenGL为右手笛卡尔坐标系统。这么描述吧:高抬右手,大拇指向右、食指向上、中指弯90°。大拇指方向为X正轴方向(从左到右),食指方向为Y正轴方向(从下到上),中指呢就是Z正轴方向(从里到外)。
绕轴旋转,遵循右手法则。例如右手握住Y轴,大拇指指向Y正轴方向,四指弯曲方向即为绕轴旋转方向。
(以上手头文档发现都没描述==,记忆里是这样的,有误还请指正!)
OpenGL中从三维场景到屏幕图形要经历如下所示的变换过程:模型坐标->世界坐标->观察坐标->投影坐标->设备坐标。
其中四种坐标经常要在程序中用到:物体坐标(也叫模型坐标、局部坐标),世界坐标,眼坐标(也叫观察坐标)和设备坐标。
世界坐标即刚开始描述的坐标系统,是用来描述OpenGL中的场景。
OpenGL中的坐标变换都是通过矩阵运算完成的,与图形学课本描述一致。由于样例工做了一些自己的矩阵变换,有些概念最好过目一下。小弟也就过目了下^^,了解矩阵为啥都是4X4的,以及简单的缩放矩阵、平移矩阵什么的就成。在这基础上写绕任意单位轴旋转任意角度的矩阵时,就不至于迷茫了。
2)OpenGL库函数
貌似有核心库(gl)、实用库(glu)、辅助库(aux)、实用工具库(glut)、窗口库(glx、agl、wgl)和扩展函数库等==。也就不详细说了(事实上是,我没用过啊T^T)。
样例工程很简单的,知道下Java层的GL10和GLU就成了-_-!
3)OpenGL的学习
空间几何:和我一样已经生疏了的同学,还请搜“空间解析几何与向量代数”。
线性代数:这门课的书,都卖了没啊?仍上学的不算==。
图形学:这个不懂啊,总之学OpenGL要看这个T^T。
OpenGL:OpenGL编程指南^^,就从这开始吧。瞄过,还不错的样子。
小弟路走岔了,不是从事OpenGL的,上述也就是自己瞎磨叽的。不过应该是这样的吧,应该错不了==。
二、射线拾取概念
1)什么是拾取
根据2D屏幕坐标来选取3D空间中图元的操作,就是拾取。
2)拾取射线
搬个拾取原理图过来^^。
图2.2 拾取原理图
z = 0处为视锥体近剪裁面,z = 1处为远剪裁面。拾取射线,就是由触摸位置在近剪裁面上的位置P0,以及在远剪裁面上的位置P1所组成的,其中,P0为射线原点,射线由P0发射指向P1。概括点就是,2D平面的一个点映射至3D空间为一射线。
好吧,这严重涉及了我们工程的目标之一:点击立方体某一面触发某事件。样例工程做的是渲染选中的三角以及提示当前选中了哪面。
三、主要实现内容
1)正方体类:Cube.java
绘制正方体所需的相关信息,以及射线与其的碰撞检测方法(及射线与正方形各个小三角形的碰撞检测)、返回外接圆信息的方法(用于快速排除外切圆区域外的触摸事件)。
public class Cube { public static final int VERTEX_BUFFER = 0; public static final int TEXTURE_BUFFER = 1; private float one = 1.0f; // 立方体顶点坐标 private float[] vertices = new float[] { -one, -one, one, one, -one, one, one, one, one, -one, one, one, -one, -one, -one, -one, one, -one, one, one, -one, one, -one, -one, -one, one, -one, -one, one, one, one, one, one, one, one, -one, -one, -one, -one, one, -one, -one, one, -one, one, -one, -one, one, one, -one, -one, one, one, -one, one, one, one, one, -one, one, -one, -one, -one, -one, -one, one, -one, one, one, -one, one, -one }; // 立方体纹理坐标 private float[] texCoords = new float[] { one, 0, 0, 0, 0, one, one, one, 0, 0, 0, one, one, one, one, 0, one, one, one, 0, 0, 0, 0, one, 0, one, one, one, one, 0, 0, 0, 0, 0, 0, one, one, one, one, 0, one, 0, 0, 0, 0, one, one, one }; // 三角形描述顺序 private byte[] indices = new byte[] { 0, 1, 3, 2, 4, 5, 7, 6, 8, 9, 11, 10, 12, 13, 15, 14, 16, 17, 19, 18, 20, 21, 23, 22 }; // 触碰的立方体某一面的标记(0-5) public int surface = -1; // 获得坐标的缓存对象 public FloatBuffer getCoordinate(int coord_id) { switch (coord_id) { case VERTEX_BUFFER: return getDirectBuffer(vertices); case TEXTURE_BUFFER: return getDirectBuffer(texCoords); default: throw new IllegalArgumentException(); } } // 获得三角形描述顺序 public ByteBuffer getIndices() { return ByteBuffer.wrap(indices); } public FloatBuffer getDirectBuffer(float[] buffer) { ByteBuffer bb = ByteBuffer.allocateDirect(buffer.length * 4); bb.order(ByteOrder.nativeOrder()); FloatBuffer directBuffer = bb.asFloatBuffer(); directBuffer.put(buffer); directBuffer.position(0); return directBuffer; } // 返回立方体外切圆的中心点 public Vector3f getSphereCenter() { return new Vector3f(0, 0, 0); } // 返回立方体外切圆的半径(√3) public float getSphereRadius() { return 1.732051f; } private static Vector4f location = new Vector4f(); /** * 射线与模型的精确碰撞检测 * * @param ray * - 转换到模型空间中的射线 * @param trianglePosOut * - 返回的拾取后的三角形顶点位置 * @return 如果相交,返回true */ public boolean intersect(Ray ray, Vector3f[] trianglePosOut) { boolean bFound = false; // 存储着射线原点与三角形相交点的距离 // 我们最后仅仅保留距离最近的那一个 float closeDis = 0.0f; Vector3f v0, v1, v2; // 立方体6个面 for (int i = 0; i < 6; i++) { // 每个面两个三角形 for (int j = 0; j < 2; j++) { if (0 == j) { v0 = getVector3f(indices[i * 4 + j]); v1 = getVector3f(indices[i * 4 + j + 1]); v2 = getVector3f(indices[i * 4 + j + 2]); } else { // 第二个三角形时,换下顺序,不然会渲染到立方体内部 v0 = getVector3f(indices[i * 4 + j]); v1 = getVector3f(indices[i * 4 + j + 2]); v2 = getVector3f(indices[i * 4 + j + 1]); } // 进行射线和三角行的碰撞检测 if (ray.intersectTriangle(v0, v1, v2, location)) { // 如果发生了相交 if (!bFound) { // 如果是初次检测到,需要存储射线原点与三角形交点的距离值 bFound = true; closeDis = location.w; trianglePosOut[0].set(v0); trianglePosOut[1].set(v1); trianglePosOut[2].set(v2); surface = i; } else { // 如果之前已经检测到相交事件,则需要把新相交点与之前的相交数据相比较 // 最终保留离射线原点更近的 if (closeDis > location.w) { closeDis = location.w; trianglePosOut[0].set(v0); trianglePosOut[1].set(v1); trianglePosOut[2].set(v2); surface = i; } } } } } return bFound; } private Vector3f getVector3f(int start) { return new Vector3f(vertices[3 * start], vertices[3 * start + 1], vertices[3 * start + 2]); } }
2)GLSurfaceView:MyGLSurfaceView.java
专门用来实现OpenGL渲染的SurfaceView,在触屏事件里计算出绕轴的单位向量。
public class MyGLSurfaceView extends GLSurfaceView { // private final float TOUCH_SCALE_FACTOR = 180.0f / 480; /** * 具体实现的渲染器 */ private RayPickRenderer mRenderer; /** * 记录上次触屏位置的坐标 */ private float mPreviousX, mPreviousY; public MyGLSurfaceView(Context context, OnSurfacePickedListener onSurfacePickedListener) { super(context); // 设置渲染器 mRenderer = new RayPickRenderer(context); // 透视上一个View setZOrderOnTop(true); setEGLConfigChooser(8, 8, 8, 8, 16, 0); // 透视上一个Activity getHolder().setFormat(PixelFormat.TRANSLUCENT); setRenderer(mRenderer); // 设置渲染模式为主动渲染 setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY); mRenderer.setOnSurfacePickedListener(onSurfacePickedListener); } public void onPause() { super.onPause(); } public void onResume() { super.onResume(); } /** * 响应触屏事件 */ @Override public boolean onTouchEvent(MotionEvent e) { float x = e.getX(); float y = e.getY(); AppConfig.setTouchPosition(x, y); switch (e.getAction()) { case MotionEvent.ACTION_MOVE: // 经过中心点的手势方向逆时针旋转90°后的坐标 float dx = y - mPreviousY; float dy = x - mPreviousX; // 手势距离 float d = (float) (Math.sqrt(dx * dx + dy * dy)); // 旋转轴单位向量的x,y值(z=0) mRenderer.mfAngleX = dx; mRenderer.mfAngleY = dy; // 手势距离 mRenderer.gesDistance = d; // float dx = x - mPreviousX; // float dy = y - mPreviousY; // mRenderer.mfAngleY += dx * TOUCH_SCALE_FACTOR; // mRenderer.mfAngleX += dy * TOUCH_SCALE_FACTOR; // PickFactory.update(x, y); AppConfig.gbNeedPick = false; break; case MotionEvent.ACTION_DOWN: AppConfig.gbNeedPick = false; break; case MotionEvent.ACTION_UP: AppConfig.gbNeedPick = true; break; case MotionEvent.ACTION_CANCEL: AppConfig.gbNeedPick = false; break; } mPreviousX = x; mPreviousY = y; return true; } }
相关文章推荐
- Android OpenGL射线拾取&手势旋转(二)
- Android OpenGL射线拾取&手势旋转(一) 推荐
- Android OpenGL射线拾取&手势旋转(二)
- Android opengl 正方形 手势三维旋转、缩小、放大
- android中手势操作图片的平移、缩放、旋转
- 【Android】自定义ImageView实现图片的平移、缩放、旋转(手势操作)
- 【附源码】【Android 3D OpenGL】开发之二——旋转的三角形【MacroCheng原创】
- Android OpenGL实战二——颜色和旋转
- android中手势操作图片的平移、缩放、旋转
- [OpenGL]从零开始写一个Android平台下的全景视频播放器——5.2 使用旋转矢量传感器改变球的朝向
- Android OpenGL之二图像旋转实例
- Android高级进阶六 Android OpenGL旋转多边形
- Android开发学习之ImageView手势拖拽、缩放、旋转
- android中手势操作图片的平移、缩放、旋转
- OpenGL: 通过射线求交实现物体的拾取
- 自定义控件: android中手势操作图片的平移、缩放、旋转 并保存
- Android手势识别02——旋转、移动等使用第三方库
- Android OpenGL YUV 旋转花屏解决、Camera获取图像
- Android 随手势进行3D旋转的源码
- Androidb不使用OpenGL实现3D旋转效果