Android OpenGL射线拾取&手势旋转
实现这个工程,主要参考了《OPhone 3D开发之射线拾取》一文。这次又是在家写的,没网T^T,所以需要的还劳烦自己Google吧。
一是通过射线拾取监听正方体各面的点击事件,二是使用绕任意轴旋转实现正方体一直按手势方向进行旋转(即无论正方体如何翻转,旋转方向一直跟随手势方向)。
OpenGL为右手笛卡尔坐标系统。这么描述吧:高抬右手,大拇指向右、食指向上、中指弯90°。大拇指方向为X正轴方向(从左到右),食指方向为Y正轴方向(从下到上),中指呢就是Z正轴方向(从里到外)。
绕轴旋转,遵循右手法则。例如右手握住Y轴,大拇指指向Y正轴方向,四指弯曲方向即为绕轴旋转方向。
(以上手头文档发现都没描述==,记忆里是这样的,有误还请指正!)
OpenGL中从三维场景到屏幕图形要经历如下所示的变换过程:模型坐标->世界坐标->观察坐标->投影坐标->设备坐标。
其中四种坐标经常要在程序中用到:物体坐标(也叫模型坐标、局部坐标),世界坐标,眼坐标(也叫观察坐标)和设备坐标。
世界坐标即刚开始描述的坐标系统,是用来描述OpenGL中的场景。
OpenGL中的坐标变换都是通过矩阵运算完成的,与图形学课本描述一致。由于样例工做了一些自己的矩阵变换,有些概念最好过目一下。小弟也就过目了下^^,了解矩阵为啥都是4X4的,以及简单的缩放矩阵、平移矩阵什么的就成。在这基础上写绕任意单位轴旋转任意角度的矩阵时,就不至于迷茫了。
貌似有核心库(gl)、实用库(glu)、辅助库(aux)、实用工具库(glut)、窗口库(glx、agl、wgl)和扩展函数库等==。也就不详细说了(事实上是,我没用过啊T^T)。
样例工程很简单的,知道下Java层的GL10和GLU就成了-_-!
空间几何:和我一样已经生疏了的同学,还请搜“空间解析几何与向量代数”。
线性代数:这门课的书,都卖了没啊?仍上学的不算==。
图形学:这个不懂啊,总之学OpenGL要看这个T^T。
OpenGL:OpenGL编程指南^^,就从这开始吧。瞄过,还不错的样子。
小弟路走岔了,不是从事OpenGL的,上述也就是自己瞎磨叽的。不过应该是这样的吧,应该错不了==。
根据2D屏幕坐标来选取3D空间中图元的操作,就是拾取。
z = 0处为视锥体近剪裁面,z = 1处为远剪裁面。拾取射线,就是由触摸位置在近剪裁面上的位置P0,以及在远剪裁面上的位置P1所组成的,其中,P0为射线原点,射线由P0发射指向P1。概括点就是,2D平面的一个点映射至3D空间为一射线。
好吧,这严重涉及了我们工程的目标之一:点击立方体某一面触发某事件。样例工程做的是渲染选中的三角以及提示当前选中了哪面。
绘制正方体所需的相关信息,以及射线与其的碰撞检测方法(及射线与正方形各个小三角形的碰撞检测)、返回外接圆信息的方法(用于快速排除外切圆区域外的触摸事件)。
- 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 };
-
-
- 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);
- }
-
-
- public float getSphereRadius() {
- return 1.732051f;
- }
-
- private static Vector4f location = new Vector4f();
-
-
-
-
-
-
-
-
-
-
- public boolean intersect(Ray ray, Vector3f[] trianglePosOut) {
- boolean bFound = false;
-
-
- float closeDis = 0.0f;
-
- Vector3f v0, v1, v2;
-
-
- 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 RayPickRenderer mRenderer;
-
-
-
- private float mPreviousX, mPreviousY;
-
- public MyGLSurfaceView(Context context,
- OnSurfacePickedListener onSurfacePickedListener) {
- super(context);
-
- mRenderer = new RayPickRenderer(context);
-
-
- setZOrderOnTop(true);
- setEGLConfigChooser(8, 8, 8, 8, 16, 0);
-
- 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:
-
-
- float dx = y - mPreviousY;
- float dy = x - mPreviousX;
-
- float d = (float) (Math.sqrt(dx * dx + dy * dy));
-
- mRenderer.mfAngleX = dx;
- mRenderer.mfAngleY = dy;
-
- mRenderer.gesDistance = d;
-
-
-
-
-
-
-
- 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;
- }
- }
超过8W字符T^T,继续->
本文转自winorlose2000 51CTO博客,原文链接: http://blog.51cto.com/vaero/790620 ,如需转载请自行联系原作者