Android OpenGL射线拾取&手势旋转(一)
Android OpenGL射线拾取&手势旋转
实现这个工程,主要参考了《OPhone 3D开发之射线拾取》一文。这次又是在家写的,没网T^T,所以需要的还劳烦自己Google吧。
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 };
- // 触碰的立方体某一面的标记(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]);
- }
- }
- 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;
- }
- }
超过8W字符T^T,继续->
本文转自winorlose2000 51CTO博客,原文链接:http://blog.51cto.com/vaero/790620,如需转载请自行联系原作者

