上回分解-_-!,Android OpenGL射线拾取&手势旋转(一)

 

3)Renderer:RayPickRenderer.java
         OpenGL渲染器,比较多的东西都在这里面了。
 

  1. public class RayPickRenderer implements Renderer { 
  2.  
  3.     private Context mContext; 
  4.     private Cube cube; 
  5.  
  6.     int texture = -1
  7.  
  8.     public float mfAngleX = 0.0f; 
  9.     public float mfAngleY = 0.0f; 
  10.  
  11.     public float gesDistance = 0.0f; 
  12.  
  13.     // 观察者、中心和上方 
  14.     private Vector3f mvEye = new Vector3f(00, 7f), mvCenter = new Vector3f(0
  15.             00), mvUp = new Vector3f(010); 
  16.  
  17.     private OnSurfacePickedListener onSurfacePickedListener; 
  18.  
  19.     public RayPickRenderer(Context context) { 
  20.         mContext = context; 
  21.         cube = new Cube(); 
  22.     } 
  23.  
  24.     /** 
  25.      * 逐帧渲染 
  26.      */ 
  27.     @Override 
  28.     public void onDrawFrame(GL10 gl) { 
  29.  
  30.         gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); // 清除屏幕和深度缓存 
  31.         gl.glLoadIdentity(); // 重置当前的模型观察矩阵 
  32.  
  33.         // 紧接着设置模型视图矩阵 
  34.         setUpCamera(gl); 
  35.  
  36.         gl.glPushMatrix(); 
  37.         { 
  38.             // 渲染物体 
  39.             drawModel(gl); 
  40.         } 
  41.         gl.glPopMatrix(); 
  42.  
  43.         // gl.glPushMatrix(); 
  44.         // { 
  45.         // // 渲染射线 
  46.         // PickFactory.getPickRay().draw(gl); 
  47.         // } 
  48.         // gl.glPopMatrix(); 
  49.  
  50.         gl.glPushMatrix(); 
  51.         { 
  52.             // 渲染选中的三角形 
  53.             drawPickedTriangle(gl); 
  54.         } 
  55.         gl.glPopMatrix(); 
  56.  
  57.         updatePick(); 
  58.     } 
  59.  
  60.     /** 
  61.      * 设置相机矩阵 
  62.      *  
  63.      * @param gl 
  64.      */ 
  65.     private void setUpCamera(GL10 gl) { 
  66.         // 设置模型视图矩阵 
  67.         gl.glMatrixMode(GL10.GL_MODELVIEW); 
  68.         gl.glLoadIdentity(); 
  69.         // GLU.gluLookAt(gl, mfEyeX, mfEyeY, mfEyeZ, mfCenterX, mfCenterY, 
  70.         // mfCenterZ, 0, 1, 0);//系统提供 
  71.         Matrix4f.gluLookAt(mvEye, mvCenter, mvUp, AppConfig.gMatView); 
  72.         gl.glLoadMatrixf(AppConfig.gMatView.asFloatBuffer()); 
  73.     } 
  74.  
  75.     // private Matrix4f matRotX = new Matrix4f(); 
  76.     // private Matrix4f matRotY = new Matrix4f(); 
  77.  
  78.     private Matrix4f matRot = new Matrix4f(); 
  79.     private Vector3f point; 
  80.  
  81.     /** 
  82.      * 渲染模型 
  83.      */ 
  84.     private void drawModel(GL10 gl) { 
  85.  
  86.         // 首先对模型进行变换 
  87.         // -------使用系统函数进行变换 
  88.         // gl.glRotatef(mfAngleX, 1, 0, 0);//绕X轴旋转 
  89.         // gl.glRotatef(mfAngleY, 0, 1, 0);//绕Y轴旋转 
  90.  
  91.         // -------托管方式进行变换 
  92.         // matRotX.setIdentity(); 
  93.         // matRotY.setIdentity(); 
  94.         // matRotX.rotX((float) (mfAngleX * Math.PI / 180)); 
  95.         // matRotY.rotY((float) (mfAngleY * Math.PI / 180)); 
  96.         // AppConfig.gMatModel.set(matRotX); 
  97.         // AppConfig.gMatModel.mul(matRotY); 
  98.  
  99.         /* 以下方式完全按照手势方向旋转 */ 
  100.         matRot.setIdentity(); 
  101.  
  102.         // 世界坐标系的向量点 
  103.         point = new Vector3f(mfAngleX, mfAngleY, 0); 
  104.  
  105.         try { 
  106.             // 转换到模型内部的点,先要求逆 
  107.             matInvertModel.set(AppConfig.gMatModel); 
  108.             matInvertModel.invert(); 
  109.             matInvertModel.transform(point, point); 
  110.  
  111.             float d = Vector3f.distance(new Vector3f(), point); 
  112.  
  113.             // 再减少误差 
  114.             if (Math.abs(d - gesDistance) <= 1E-4) { 
  115.  
  116.                 // 绕这个单位向量旋转(由于误差可能会产生缩放而使得模型消失不见) 
  117.                 matRot.glRotatef((float) (gesDistance * Math.PI / 180), point.x 
  118.                         / d, point.y / d, point.z / d); 
  119.  
  120.                 // 旋转后在原基础上再转 
  121.                 if (0 != gesDistance) { 
  122.                     AppConfig.gMatModel.mul(matRot); 
  123.                 } 
  124.             } 
  125.         } catch (Exception e) { 
  126.             // 由于四舍五入求逆矩阵失败 
  127.         } 
  128.         gesDistance = 0
  129.  
  130.         gl.glMultMatrixf(AppConfig.gMatModel.asFloatBuffer()); 
  131.  
  132.         // 设置默认颜色 
  133.         gl.glColor4f(1.0f, 1.0f, 1.0f, 0.0f); 
  134.  
  135.         gl.glBindTexture(GL10.GL_TEXTURE_2D, texture); 
  136.  
  137.         gl.glEnable(GL10.GL_TEXTURE_2D); 
  138.         gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); 
  139.         gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY); 
  140.  
  141.         gl.glVertexPointer(3, GL10.GL_FLOAT, 0
  142.                 cube.getCoordinate(Cube.VERTEX_BUFFER)); 
  143.         gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0
  144.                 cube.getCoordinate(Cube.TEXTURE_BUFFER)); 
  145.         gl.glDrawElements(GL10.GL_TRIANGLE_STRIP, 24, GL10.GL_UNSIGNED_BYTE, 
  146.                 cube.getIndices()); 
  147.  
  148.         gl.glDisable(GL10.GL_TEXTURE_2D); 
  149.         gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY); 
  150.         gl.glDisableClientState(GL10.GL_VERTEX_ARRAY); 
  151.  
  152.         // 渲染坐标系 
  153.         drawCoordinateSystem(gl); 
  154.     } 
  155.  
  156.     private Vector3f transformedSphereCenter = new Vector3f(); 
  157.     private Ray transformedRay = new Ray(); 
  158.     private Matrix4f matInvertModel = new Matrix4f(); 
  159.     private Vector3f[] mpTriangle = { new Vector3f(), new Vector3f(), 
  160.             new Vector3f() }; 
  161.     private FloatBuffer mBufPickedTriangle = IBufferFactory 
  162.             .newFloatBuffer(3 * 3); 
  163.  
  164.     /** 
  165.      * 更新拾取事件 
  166.      */ 
  167.     private void updatePick() { 
  168.         if (!AppConfig.gbNeedPick) { 
  169.             return
  170.         } 
  171.         AppConfig.gbNeedPick = false
  172.         // 更新最新的拾取射线 
  173.         PickFactory.update(AppConfig.gScreenX, AppConfig.gScreenY); 
  174.         // 获得最新的拾取射线 
  175.         Ray ray = PickFactory.getPickRay(); 
  176.  
  177.         // 首先把模型的绑定球通过模型矩阵,由模型局部空间变换到世界空间 
  178.         AppConfig.gMatModel.transform(cube.getSphereCenter(), 
  179.                 transformedSphereCenter); 
  180.  
  181.         // 触碰的立方体面的标记为无 
  182.         cube.surface = -1
  183.  
  184.         // 首先检测拾取射线是否与模型绑定球发生相交 
  185.         // 这个检测很快,可以快速排除不必要的精确相交检测 
  186.         if (ray.intersectSphere(transformedSphereCenter, cube.getSphereRadius())) { 
  187.             // 如果射线与绑定球发生相交,那么就需要进行精确的三角面级别的相交检测 
  188.             // 由于我们的模型渲染数据,均是在模型局部坐标系中 
  189.             // 而拾取射线是在世界坐标系中 
  190.             // 因此需要把射线转换到模型坐标系中 
  191.             // 这里首先计算模型矩阵的逆矩阵 
  192.             matInvertModel.set(AppConfig.gMatModel); 
  193.             matInvertModel.invert(); 
  194.             // 把射线变换到模型坐标系中,把结果存储到transformedRay中 
  195.             ray.transform(matInvertModel, transformedRay); 
  196.             // 将射线与模型做精确相交检测 
  197.             if (cube.intersect(transformedRay, mpTriangle)) { 
  198.                 // 如果找到了相交的最近的三角形 
  199.                 AppConfig.gbTrianglePicked = true
  200.                 // 触碰了哪一个面 
  201.                 Log.i("触碰的立方体面""=标记=" + cube.surface); 
  202.                 // 回调 
  203.                 if (null != onSurfacePickedListener) { 
  204.                     onSurfacePickedListener.onSurfacePicked(cube.surface); 
  205.                 } 
  206.                 // 填充数据到被选取三角形的渲染缓存中 
  207.                 mBufPickedTriangle.clear(); 
  208.                 for (int i = 0; i < 3; i++) { 
  209.                     IBufferFactory 
  210.                             .fillBuffer(mBufPickedTriangle, mpTriangle[i]); 
  211.                     // Log.i("点" + i, mpTriangle[i].x + "\t" + mpTriangle[i].y 
  212.                     // + "\t" + mpTriangle[i].z); 
  213.                 } 
  214.                 mBufPickedTriangle.position(0); 
  215.             } 
  216.         } else { 
  217.             AppConfig.gbTrianglePicked = false
  218.         } 
  219.     } 
  220.  
  221.     /** 
  222.      * 渲染选中的三角形 
  223.      */ 
  224.     private void drawPickedTriangle(GL10 gl) { 
  225.         if (!AppConfig.gbTrianglePicked) { 
  226.             return
  227.         } 
  228.         // 由于返回的拾取三角形数据是出于模型坐标系中 
  229.         // 因此需要经过模型变换,将它们变换到世界坐标系中进行渲染 
  230.         // 设置模型变换矩阵 
  231.         gl.glMultMatrixf(AppConfig.gMatModel.asFloatBuffer()); 
  232.         // 设置三角形颜色,alpha为0.7 
  233.         gl.glColor4f(1.0f, 0.0f, 0.0f, 0.7f); 
  234.         // 开启Blend混合模式 
  235.         gl.glEnable(GL10.GL_BLEND); 
  236.         gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA); 
  237.         // 禁用无关属性,仅仅使用纯色填充 
  238.         gl.glDisable(GL10.GL_DEPTH_TEST); 
  239.         gl.glDisable(GL10.GL_TEXTURE_2D); 
  240.         // 开始绑定渲染顶点数据 
  241.         gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); 
  242.  
  243.         gl.glVertexPointer(3, GL10.GL_FLOAT, 0, mBufPickedTriangle); 
  244.         // 提交渲染 
  245.         gl.glDrawArrays(GL10.GL_TRIANGLES, 03); 
  246.         // 重置相关属性 
  247.         gl.glDisableClientState(GL10.GL_VERTEX_ARRAY); 
  248.         gl.glEnable(GL10.GL_DEPTH_TEST); 
  249.         gl.glDisable(GL10.GL_BLEND); 
  250.     } 
  251.  
  252.     /** 
  253.      * 渲染坐标系 
  254.      */ 
  255.     private void drawCoordinateSystem(GL10 gl) { 
  256.         // 暂时禁用深度测试 
  257.         gl.glDisable(GL10.GL_DEPTH_TEST); 
  258.         // 设置点和线的宽度 
  259.         gl.glLineWidth(2.0f); 
  260.         // 仅仅启用顶点数据 
  261.         gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); 
  262.  
  263.         FloatBuffer fb = IBufferFactory.newFloatBuffer(3 * 2); 
  264.         fb.put(new float[] { 0001.4f, 00 }); 
  265.         fb.position(0); 
  266.  
  267.         // 渲染X轴 
  268.         gl.glColor4f(1.0f, 0.0f, 0.0f, 1.0f);// 设置红色 
  269.         gl.glVertexPointer(3, GL10.GL_FLOAT, 0, fb); 
  270.         // 提交渲染 
  271.         gl.glDrawArrays(GL10.GL_LINES, 02); 
  272.  
  273.         fb.clear(); 
  274.         fb.put(new float[] { 00001.4f, 0 }); 
  275.         fb.position(0); 
  276.         // 渲染Y轴 
  277.         gl.glColor4f(0.0f, 1.0f, 0.0f, 1.0f);// 设置绿色 
  278.         gl.glVertexPointer(3, GL10.GL_FLOAT, 0, fb); 
  279.         // 提交渲染 
  280.         gl.glDrawArrays(GL10.GL_LINES, 02); 
  281.  
  282.         fb.clear(); 
  283.         fb.put(new float[] { 000001.4f }); 
  284.         fb.position(0); 
  285.         // 渲染Z轴 
  286.         gl.glColor4f(0.0f, 0.0f, 1.0f, 1.0f);// 设置蓝色 
  287.         gl.glVertexPointer(3, GL10.GL_FLOAT, 0, fb); 
  288.         // 提交渲染 
  289.         gl.glDrawArrays(GL10.GL_LINES, 02); 
  290.  
  291.         // 重置 
  292.         gl.glDisableClientState(GL10.GL_VERTEX_ARRAY); 
  293.         gl.glLineWidth(1.0f); 
  294.         gl.glEnable(GL10.GL_DEPTH_TEST); 
  295.     } 
  296.  
  297.     /** 
  298.      * 创建绘图表面时调用 
  299.      */ 
  300.     @Override 
  301.     public void onSurfaceCreated(GL10 gl, EGLConfig config) { 
  302.         // 全局性设置 
  303.         gl.glEnable(GL10.GL_DITHER); 
  304.  
  305.         gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_NICEST); 
  306.         // 设置清屏背景颜色 
  307.         // gl.glClearColor(0, 0, 0, 0); 
  308.         gl.glClearColor(0.5f, 0.5f, 0.5f, 1); 
  309.         // 设置着色模型为平滑着色 
  310.         gl.glShadeModel(GL10.GL_SMOOTH); 
  311.  
  312.         // 启用背面剪裁 
  313.         gl.glEnable(GL10.GL_CULL_FACE); 
  314.         gl.glCullFace(GL10.GL_BACK); 
  315.         // 启用深度测试 
  316.         gl.glEnable(GL10.GL_DEPTH_TEST); 
  317.         // 禁用光照和混合 
  318.         gl.glDisable(GL10.GL_LIGHTING); 
  319.         gl.glDisable(GL10.GL_BLEND); 
  320.  
  321.         loadTexture(gl); 
  322.  
  323.         AppConfig.gMatModel.setIdentity(); 
  324.     } 
  325.  
  326.     /** 
  327.      * 当绘图表面尺寸发生改变时调用 
  328.      */ 
  329.     @Override 
  330.     public void onSurfaceChanged(GL10 gl, int width, int height) { 
  331.         // 设置视口 
  332.         gl.glViewport(00, width, height); 
  333.         AppConfig.gpViewport[0] = 0
  334.         AppConfig.gpViewport[1] = 0
  335.         AppConfig.gpViewport[2] = width; 
  336.         AppConfig.gpViewport[3] = height; 
  337.  
  338.         // 设置投影矩阵 
  339.         float ratio = (float) width / height;// 屏幕宽高比 
  340.         gl.glMatrixMode(GL10.GL_PROJECTION); 
  341.         gl.glLoadIdentity(); 
  342.         // GLU.gluPerspective(gl, 45.0f, ratio, 1, 5000);系统提供 
  343.         Matrix4f.gluPersective(45.0f, ratio, 110, AppConfig.gMatProject); 
  344.         gl.glLoadMatrixf(AppConfig.gMatProject.asFloatBuffer()); 
  345.         AppConfig.gMatProject.fillFloatArray(AppConfig.gpMatrixProjectArray); 
  346.         // 每次修改完GL_PROJECTION后,最好将当前矩阵模型设置回GL_MODELVIEW 
  347.         gl.glMatrixMode(GL10.GL_MODELVIEW); 
  348.     } 
  349.  
  350.     private void loadTexture(GL10 gl) { 
  351.  
  352.         // 启用纹理映射 
  353.         gl.glClearDepthf(1.0f); 
  354.         // 允许2D贴图,纹理 
  355.         gl.glEnable(GL10.GL_TEXTURE_2D); 
  356.  
  357.         try { 
  358.             IntBuffer intBuffer = IntBuffer.allocate(1); 
  359.             // 创建纹理 
  360.             gl.glGenTextures(1, intBuffer); 
  361.             texture = intBuffer.get(); 
  362.             // 设置要使用的纹理 
  363.             gl.glBindTexture(GL10.GL_TEXTURE_2D, texture); 
  364.  
  365.             // 打开二进制流 
  366.             InputStream is = mContext.getResources().openRawResource( 
  367.                     R.drawable.snow_leopard); 
  368.             Bitmap mBitmap = BitmapFactory.decodeStream(is); 
  369.             // Log.i("宽度|高度", mBitmap.getWidth() + "|" + mBitmap.getHeight()); 
  370.  
  371.             // 生成纹理 
  372.             GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, mBitmap, 0); 
  373.  
  374.             // 线形滤波 
  375.             gl.glTexParameterx(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, 
  376.                     GL10.GL_LINEAR); 
  377.             gl.glTexParameterx(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, 
  378.                     GL10.GL_LINEAR); 
  379.  
  380.             is.close(); 
  381.         } catch (IOException e) { 
  382.             // TODO Auto-generated catch block 
  383.             e.printStackTrace(); 
  384.         } 
  385.     } 
  386.  
  387.     public void setOnSurfacePickedListener( 
  388.             OnSurfacePickedListener onSurfacePickedListener) { 
  389.         this.onSurfacePickedListener = onSurfacePickedListener; 
  390.     } 
  391.  

4)主Activity:RayPickActivity
         继承各面点击的监听接口,提示了个Toast就OK了。很简单,当时都没注释。


  1. public class RayPickActivity extends Activity implements 
  2.         OnSurfacePickedListener { 
  3.  
  4.     private GLSurfaceView mGLSurfaceView; 
  5.  
  6.     @Override 
  7.     public void onCreate(Bundle savedInstanceState) { 
  8.         super.onCreate(savedInstanceState); 
  9.  
  10.         mGLSurfaceView = new MyGLSurfaceView(thisthis); 
  11.         mGLSurfaceView.getHolder().setFormat(PixelFormat.TRANSLUCENT); 
  12.         setContentView(mGLSurfaceView); 
  13.         mGLSurfaceView.requestFocus(); 
  14.         mGLSurfaceView.setFocusableInTouchMode(true); 
  15.     } 
  16.  
  17.     @Override 
  18.     protected void onResume() { 
  19.         super.onResume(); 
  20.         mGLSurfaceView.onResume(); 
  21.     } 
  22.  
  23.     @Override 
  24.     protected void onPause() { 
  25.         super.onPause(); 
  26.         mGLSurfaceView.onPause(); 
  27.     } 
  28.  
  29.     private Handler myHandler = new Handler() { 
  30.         @Override 
  31.         public void handleMessage(Message msg) { 
  32.             Toast.makeText(RayPickActivity.this"选中了" + msg.what + "面"
  33.                     Toast.LENGTH_SHORT).show(); 
  34.         } 
  35.     }; 
  36.  
  37.     @Override 
  38.     public void onSurfacePicked(int which) { 
  39.         myHandler.sendEmptyMessage(which); 
  40.     } 
  41.  
 
5)Matrix4f内的glRotatef(…)方法
         公式如下图,其中其中C为cosθ,S为sinθ,A为单位化的旋转轴。

rotate.jpg

         但这是左手坐标系下的公式,之前有改过右手坐标系的但不能实现效果。其他几个绕X、Y、Z轴的也是这样。
         这应该是因为OpenGL坐标系原点在左下角,而窗口坐标系的原点为右上角,程序在Y轴上做了修正的关系导致的。这次可以高抬左手,大拇指向右、食指向下、弯曲中指90°,是不是各正轴都对应上了呢^^。
 

  1. /** 
  2.  * 绕任意单位轴旋转任意角度的矩阵 
  3.  */ 
  4. public final void glRotatef(float angle, float x, float y, float z) { 
  5.     float sinAngle, cosAngle; 
  6.  
  7.     sinAngle = (float) Math.sin((double) angle); 
  8.     cosAngle = (float) Math.cos((double) angle); 
  9.  
  10.     this.m00 = cosAngle + (1 - cosAngle) * x * x; 
  11.     this.m01 = (1 - cosAngle) * x * y - sinAngle * z; 
  12.     this.m02 = (1 - cosAngle) * x * z + sinAngle * y; 
  13.     this.m03 = 0
  14.  
  15.     this.m10 = (1 - cosAngle) * x * y + sinAngle * z; 
  16.     this.m11 = cosAngle + (1 - cosAngle) * y * y; 
  17.     this.m12 = (1 - cosAngle) * y * z - sinAngle * x; 
  18.     this.m13 = 0
  19.  
  20.     this.m20 = (1 - cosAngle) * x * z - sinAngle * y; 
  21.     this.m21 = (1 - cosAngle) * y * z + sinAngle * x; 
  22.     this.m22 = cosAngle + (1 - cosAngle) * z * z; 
  23.     this.m23 = 0
  24.  
  25.     this.m30 = 0
  26.     this.m31 = 0
  27.     this.m32 = 0
  28.     this.m33 = 1
 
三、后记
         小弟那时做这个,貌似是为了做个3D魔方啊。然后无聊可以在那转啊转啊的,不过愿望很美好……总之,我没做下去,忘记当时又忙活啥去了T^T。
         现在也没这个激情继续做这个呢,希望这篇文档能给那些有同样想法的人给予一定的帮助吧,加油^^。