Android OpenGL ES 开发教程(19):绘制迷你太阳系

jerry OpenGL ES 2015年11月25日 收藏

前面介绍了3D坐标系统和3D坐标变换以及在OpenGL ES中坐标变换的过程,并与相机拍照片的过程做类比,以便更好的理解这OpenGL中构造3D模型的一部步骤:

提供绘制一个迷你太阳系系统作为前面知识的总结,这个迷你太阳系,有一个红色的太阳,一个蓝色的地图和一个白色的月亮构成:

  • 太阳居中,逆时针自转。
  • 地球绕太阳顺时针公转,本身不自转。
  • 月亮绕地球顺时针公转,自身逆时针自转。

为简单起见,使用一个2D五角星做为天体而没有使用球体(绘制球体在后面有介绍),构造一个Star类:

  1. public class Star {
  2. // Our vertices.
  3. protected float vertices[];
  4.  
  5. // Our vertex buffer.
  6. protected FloatBuffer vertexBuffer;
  7.  
  8. public Star() {
  9.  
  10. float a=(float)(1.0f/(2.0f-2f*Math.cos(72f*Math.PI/180.f)));
  11. float bx=(float)(a*Math.cos(18*Math.PI/180.0f));
  12. float by=(float)(a*Math.sin(18*Math.PI/180f));
  13. float cy=(float)(-a * Math.cos(18*Math.PI/180f));
  14. vertices=new float[]{
  15. 0,a,0.5f,cy,-bx,by,bx,by,-0.5f,cy
  16. };
  17.  
  18. ByteBuffer vbb
  19. = ByteBuffer.allocateDirect(vertices.length * 4);
  20. vbb.order(ByteOrder.nativeOrder());
  21. vertexBuffer = vbb.asFloatBuffer();
  22. vertexBuffer.put(vertices);
  23. vertexBuffer.position(0);
  24.  
  25. }
  26.  
  27. /**
  28. * This function draws our star on screen.
  29. * @param gl
  30. */
  31. public void draw(GL10 gl) {
  32. // Counter-clockwise winding.
  33. gl.glFrontFace(GL10.GL_CCW);
  34. // Enable face culling.
  35. gl.glEnable(GL10.GL_CULL_FACE);
  36. // What faces to remove with the face culling.
  37. gl.glCullFace(GL10.GL_BACK);
  38.  
  39. // Enabled the vertices buffer for writing
  40. //and to be used during
  41. // rendering.
  42. gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
  43. // Specifies the location and data format of
  44. //an array of vertex
  45. // coordinates to use when rendering.
  46. gl.glVertexPointer(2, GL10.GL_FLOAT, 0,
  47. vertexBuffer);
  48.  
  49. gl.glDrawArrays(GL10.GL_LINE_LOOP, 0,5);
  50.  
  51. // Disable the vertices buffer.
  52. gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
  53. // Disable face culling.
  54. gl.glDisable(GL10.GL_CULL_FACE);
  55. }
  56.  
  57. }

Star定义了五角星的五个顶点,并使用glDrawArrays 来绘制五角星,因此vertices 顶点的顺序比较重要。

然后定义一个DrawSolarSystem 来绘制这个迷你太阳系:

  1. public class DrawSolarSystem extends OpenGLESActivity
  2. implements IOpenGLDemo{
  3.  
  4. private Star sun=new Star();
  5. private Star earth=new Star();
  6. private Star moon=new Star();
  7.  
  8. private int angle=0;
  9.  
  10. /** Called when the activity is first created. */
  11. @Override
  12. public void onCreate(Bundle savedInstanceState) {
  13. super.onCreate(savedInstanceState);
  14.  
  15. }
  16.  
  17. public void DrawScene(GL10 gl) {
  18. super.DrawScene(gl);
  19. gl.glLoadIdentity();
  20. GLU.gluLookAt(gl,0.0f, 0.0f, 15.0f,
  21. 0.0f, 0.0f, 0.0f,
  22. 0.0f, 1.0f, 0.0f);
  23. // Star A
  24. // Save the current matrix.
  25. gl.glPushMatrix();
  26. // Rotate Star A counter-clockwise.
  27. gl.glRotatef(angle, 0, 0, 1);
  28. gl.glColor4f(1.0f, 0.0f, 0.0f, 1.0f);
  29. // Draw Star A.
  30. sun.draw(gl);
  31. // Restore the last matrix.
  32. gl.glPopMatrix();
  33. // Star B
  34. // Save the current matrix
  35. gl.glPushMatrix();
  36. // Rotate Star B before moving it,
  37. //making it rotate around A.
  38. gl.glRotatef(-angle, 0, 0, 1);
  39. // Move Star B.
  40. gl.glTranslatef(3, 0, 0);
  41. // Scale it to 50% of Star A
  42. gl.glScalef(.5f, .5f, .5f);
  43. gl.glColor4f(0.0f, 0.0f, 1.0f, 1.0f);
  44. // Draw Star B.
  45. earth.draw(gl);
  46. // Star C
  47. // Save the current matrix
  48. gl.glPushMatrix();
  49. // Make the rotation around B
  50. gl.glRotatef(-angle, 0, 0, 1);
  51. gl.glTranslatef(2, 0, 0);
  52. // Scale it to 50% of Star B
  53. gl.glScalef(.5f, .5f, .5f);
  54. // Rotate around it's own center.
  55. gl.glRotatef(angle*10, 0, 0, 1);
  56. gl.glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
  57. // Draw Star C.
  58. moon.draw(gl);
  59. // Restore to the matrix as it was before C.
  60. gl.glPopMatrix();
  61. // Restore to the matrix as it was before B.
  62. gl.glPopMatrix();
  63. // Increse the angle.
  64. angle++;
  65.  
  66. }
  67.  
  68. }
  69.  

使用GLU的gluLookAt 来定义modelview Matrix ,把相机放在正对太阳中心(0,0,0),距离15 (0,0,15)。

使用glPushMatrix和glPopMatrix 来将当前Matrix入栈或是出栈。

首先将当前matrix 入栈,以红色绘制太阳,并逆向转动,将当前matrix 入栈的目的是在能够在绘制地球时恢复当前栈。

然后绘制地球,使用局部坐标系来想象地球和太阳之间的相对运动,地球离开一距离绕太阳公转,相当于先旋转地球的局部坐标系,然后再平移地球的局部坐标系。对应到代码为先glRotatef ,然后glTranslate.

最后是绘制月亮,使用类似的空间想象方法。

下载