使用GLES20,顶点缓冲区对象(VBO)无法在Android 2.3.3上运行

时间:2011-12-18 14:29:21

标签: android opengl-es-2.0 shader vbo

在Android上,我试图让一个简单的OpenGL ES 2.0应用程序运行,它使用顶点缓冲区对象,但我失败了。

我从这个项目开始:

http://developer.android.com/resources/tutorials/opengl/opengl-es20.html

一切都很好地解释并按照描述工作。细

我添加了一些代码,用glDrawElements命令代替glDrawArrays替换渲染。 我成功了。

现在进行下一步:我想使用顶点缓冲区对象来做同样的事情。

所以我补充说:

  1. 新变种:

    private int [] mVBOid = new int [2]; // VBO和索引缓冲区对象需要2个ID 私人ShortBuffer mIndices; //使用的索引

  2. 添加了创建VBO的代码:

        ByteBuffer vbb = ByteBuffer.allocateDirect(
                triangleCoords.length * SIZEOF_FLOAT); 
        vbb.order(ByteOrder.nativeOrder());// use the device hardware's native byte order
        mTriangleVB = vbb.asFloatBuffer();  // create a floating point buffer from the ByteBuffer
        mTriangleVB.put(triangleCoords);    // add the coordinates to the FloatBuffer
        mTriangleVB.position(0);            // set the buffer to read the first coordinate
    
        ByteBuffer ibb = ByteBuffer.allocateDirect(
                indices.length * SIZEOF_SHORT);
        ibb.order(ByteOrder.nativeOrder()); // use the device hardware's native byte order
        mIndices = ibb.asShortBuffer();     // create a short buffer from the ByteBuffer
        mIndices.put(indices);              // add the indices to the Buffer
        mIndices.position(0);               // set the buffer to read the first index
    
        GLES20.glGenBuffers(2, mVBOid, 0);
    
        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mVBOid[0]);
        GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER,
                numComponentsPerVertex * SIZEOF_FLOAT,
                mTriangleVB,
                GLES20.GL_STATIC_DRAW);
    
        GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, mVBOid[1]);
        GLES20.glBufferData(GLES20.GL_ELEMENT_ARRAY_BUFFER,
                mNumIndices * SIZEOF_SHORT,
                mIndices,
                GLES20.GL_STATIC_DRAW);
    
  3. 添加了绘制几何图形的代码:

        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mVBOid[0]);
        GLES20.glVertexAttribPointer(maPositionHandle, nc, GLES20.GL_FLOAT, false, stride, 0);
        GLES20.glEnableVertexAttribArray(maPositionHandle);
    
        GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, mVBOid[1]);
        GLES20.glDrawElements(GLES20.GL_TRIANGLE_FAN, mNumIndices, GLES20.GL_UNSIGNED_SHORT, 0);
    
  4. 注意:由于首次使用glDrawElements渲染几何体而不使用VBO的新功能正在运行,我知道mTriangleVB和mIndices变量正确填充了所需的数据。 maPositionHandle和muMVPMatrixHandle也是正确的。 在我的代码中,我检查了GL错误 - 没有找到

    我的问题:VBO技术不起作用;除了清晰的颜色外,屏幕上看不到任何内容。 在更复杂的应用程序中,我遇到了更多问题:

    • 在引入基于VBO的几何体时,不使用正确渲染的VBO的其他几何体是不可见的

    • 偶然报告
    • 分段错误。试图找到确切的原因我注释了很多代码,最后发现,即使几何体根本没有渲染,也会发生崩溃。 崩溃的原因必须是VBO的初始化 - 虽然崩溃不会立即发生,但会在一段时间之后发生 但我仍然无法弄清楚它为什么不起作用。

    这里有更多信息:

    1. 我的环境:

      • Android 2.3.3
      • 构建目标:Android 2.3.3
      • Android SDK工具:Rev。15
      • Android SDK平台工具:Rev。9
      • 设备:华为Ideos X3智能手机
    2. SimpleOpenGLES20Renderer类的完整Source 代码基于此示例:
      http://developer.android.com/resources/tutorials/opengl/opengl-es20.html

    3. package com.hugo.simplegles20;
      
      import java.nio.ByteBuffer;
      import java.nio.ByteOrder;
      import java.nio.FloatBuffer;
      import java.nio.ShortBuffer;
      
      import javax.microedition.khronos.egl.EGLConfig;
      import javax.microedition.khronos.opengles.GL10;
      
      import android.opengl.GLES20;
      import android.opengl.GLSurfaceView;
      import android.opengl.Matrix;
      import android.util.Log;
      
      
      public class SimpleOpenGLES20Renderer implements GLSurfaceView.Renderer {
      
          public float mAngle;
      
          static String TAG = "SimpleTest";
      
          final int SIZEOF_FLOAT = Float.SIZE / 8;
          final int SIZEOF_SHORT = Short.SIZE / 8;
      
      
          private int[] mVBOid = new int[2];      // 2 ids needed for VBO and index buffer oject
      
      
          enum TestType {
              USE_ARRAY,          // (almost) the original code
              USE_ELEMENTS,       // rendering, using glDrawElements call
              USE_VBO_ELEMENTS    // using a vertex buffer object (VBO)
          }
          private TestType mUsage = TestType.USE_VBO_ELEMENTS;
      
      
          private boolean mFourComponents = true;
      
          private int mNumIndices = 0;
      
          private FloatBuffer mTriangleVB;
          private ShortBuffer mIndices;
          private final String vertexShaderCode = 
              // This matrix member variable provides a hook to manipulate
              // the coordinates of the objects that use this vertex shader
              "uniform mat4 uMVPMatrix;   \n" +
      
              "attribute vec4 vPosition;  \n" +
              "void main(){               \n" +
      
              // the matrix must be included as a modifier of gl_Position
              " gl_Position = uMVPMatrix * vPosition; \n" +
      
              "}  \n";
      
          private final String fragmentShaderCode = 
              "precision mediump float;  \n" +
              "void main(){              \n" +
              " gl_FragColor = vec4 (0.63671875, 0.76953125, 0.22265625, 1.0); \n" +
              "}                         \n";
      
          private int mProgram;
      
          private int maPositionHandle;
      
          private int muMVPMatrixHandle;
      
          private float[] mMVPMatrix = new float[16];
          private float[] mMMatrix = new float[16];
          private float[] mVMatrix = new float[16];
          private float[] mProjMatrix = new float[16];
      
          public static void checkGLError(String msg) {
              int e = GLES20.glGetError();
              if (e != GLES20.GL_NO_ERROR) {
                  Log.d(TAG, "GLES20 ERROR: " + msg + " " + e);
                  Log.d(TAG, errString(e));
              }
          }
          public static String errString(int ec) {
              switch (ec) {
              case GLES20.GL_NO_ERROR:
                  return "No error has been recorded.";
              case GLES20.GL_INVALID_ENUM:
                  return "An unacceptable value is specified for an enumerated argument.";
              case GLES20.GL_INVALID_VALUE:
                  return "A numeric argument is out of range.";
              case GLES20.GL_INVALID_OPERATION:
                  return "The specified operation is not allowed in the current state.";
              case GLES20.GL_INVALID_FRAMEBUFFER_OPERATION:
                  return "The command is trying to render to or read from the framebuffer" +
                  " while the currently bound framebuffer is not framebuffer complete (i.e." +
                  " the return value from glCheckFramebufferStatus is not" +
                  " GL_FRAMEBUFFER_COMPLETE).";
              case GLES20.GL_OUT_OF_MEMORY:
                  return "There is not enough memory left to execute the command." +
                          " The state of the GL is undefined, except for the state" +
                          " of the error flags, after this error is recorded.";
              default :
                  return "UNKNOW ERROR";
              }
          }
      
          @Override
          public void onSurfaceCreated(GL10 uu, EGLConfig config) {
              // Set the background frame color
              GLES20.glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
              checkGLError("onSurfaceCreated 1");
      
              initShapes();
      
              Log.d(TAG, "load vertex shader");
              int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode);
              Log.d(TAG, "load fragment shader");
              int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode);
      
              mProgram = GLES20.glCreateProgram();             // create empty OpenGL Program
              checkGLError("onSurfaceCreated 2");
              GLES20.glAttachShader(mProgram, vertexShader);   // add the vertex shader to program
              checkGLError("onSurfaceCreated 3");
              GLES20.glAttachShader(mProgram, fragmentShader); // add the fragment shader to program
              checkGLError("onSurfaceCreated 4");
              GLES20.glLinkProgram(mProgram);                  // creates OpenGL program executables
              checkGLError("onSurfaceCreated 5");
      
              // get handle to the vertex shader's vPosition member
              maPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
              checkGLError("onSurfaceCreated 6");
              muMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
              checkGLError("onSurfaceCreated 7");
          }
      
          @Override
          public void onSurfaceChanged(GL10 unused, int width, int height) {
              GLES20.glViewport(0, 0, width, height);
      
              float ratio = (float) width / height;
      
              // this projection matrix is applied to object coordinates
              // in the onDrawFrame() method
              Matrix.frustumM(mProjMatrix, 0, -ratio, ratio, -1, 1, 3, 7);
              Matrix.setLookAtM(mVMatrix, 0, 0, 0, -3, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
          }  
          @Override
          public void onDrawFrame(GL10 uu) {
              GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
              checkGLError("onDrawFrame 1");
      
              // Add program to OpenGL environment
              GLES20.glUseProgram(mProgram);
              checkGLError("onDrawFrame 2");
      
              // Use the mAngle member as the rotation value
              Matrix.setRotateM(mMMatrix, 0, mAngle, 0, 0, 1.0f);
      
              // Apply a ModelView Projection transformation
              Matrix.multiplyMM(mMVPMatrix, 0, mVMatrix, 0, mMMatrix, 0);
              Matrix.multiplyMM(mMVPMatrix, 0, mProjMatrix, 0, mMVPMatrix, 0);
      
              GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, mMVPMatrix, 0);
              checkGLError("onDrawFrame 3");
      
              int nc = mFourComponents ? 4 : 3;
              int stride = nc * SIZEOF_FLOAT;
      
              switch (mUsage) {
              case USE_ARRAY:
                  // Prepare the triangle data
                  GLES20.glVertexAttribPointer(maPositionHandle, nc, GLES20.GL_FLOAT, false, stride, mTriangleVB);
                  checkGLError("onDrawFrame 4");
                  GLES20.glEnableVertexAttribArray(maPositionHandle);
                  checkGLError("onDrawFrame 5");
      
                  // Draw the triangle
                  GLES20.glDrawArrays(GLES20.GL_TRIANGLE_FAN, 0, mNumIndices);
                  checkGLError("onDrawFrame 6");
                  break;
              case USE_ELEMENTS:
                  // Prepare the triangle data
                  GLES20.glVertexAttribPointer(maPositionHandle, nc, GLES20.GL_FLOAT, false, stride, mTriangleVB);
                  checkGLError("onDrawFrame 7");
                  GLES20.glEnableVertexAttribArray(maPositionHandle);
                  checkGLError("onDrawFrame 8");
      
                  // Draw the triangle
                  // int indicesSizeInBytes = SIZEOF_SHORT * mNumIndices;
                  GLES20.glDrawElements(GLES20.GL_TRIANGLE_FAN, mNumIndices, GLES20.GL_UNSIGNED_SHORT, mIndices);
                  checkGLError("onDrawFrame 9");
                  break;
              case USE_VBO_ELEMENTS:
                  GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mVBOid[0]);
                  checkGLError("onDrawFrame 14");
                  GLES20.glVertexAttribPointer(maPositionHandle, nc, GLES20.GL_FLOAT, false, stride, 0);
                  checkGLError("onDrawFrame 15");
                  GLES20.glEnableVertexAttribArray(maPositionHandle);
                  checkGLError("onDrawFrame 16");
      
                  GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, mVBOid[1]);
                  checkGLError("onDrawFrame 17");
                  GLES20.glDrawElements(GLES20.GL_TRIANGLE_FAN, mNumIndices, GLES20.GL_UNSIGNED_SHORT, 0);
                  checkGLError("onDrawFrame 18");
                  break;
              }
          }
      
          private void initShapes(){
      
              float triangleCoords3[] = {
                      // X, Y, Z
                      -0.5f, -0.5f, 0,
                      -0.5f,  0.5f, 0,
                      -0.2f, -0.2f, 0,
                       0.5f, -0.5f, 0
                  };
              float triangleCoords4[] = {
                      // X, Y, Z, W
                      -0.5f, -0.5f, 0, 1,
                      -0.5f,  0.5f, 0, 1,
                      -0.2f, -0.2f, 0, 1,
                       0.5f, -0.5f, 0, 1
                  };
      
              short[] indices = {0,1,2,3};
      
              float[] triangleCoords;
      
              int numComponentsPerVertex;
              if (mFourComponents) {
                  triangleCoords = triangleCoords4;
                  numComponentsPerVertex = 4;
              } else {
                  triangleCoords = triangleCoords3;
                  numComponentsPerVertex = 3;
              }
      
              mNumIndices = triangleCoords.length / numComponentsPerVertex;
      
              Log.d(TAG, "Components per Vertex: " + numComponentsPerVertex);
              Log.d(TAG, "Number of Indices    : " + mNumIndices);
      
              switch (mUsage) {
              case USE_ARRAY:
              {
                  Log.d(TAG, "using array");
                  // initialize vertex Buffer for triangle  
                  ByteBuffer vbb = ByteBuffer.allocateDirect(
                          // (# of coordinate values * 4 bytes per float)
                          triangleCoords.length * SIZEOF_FLOAT); 
                  vbb.order(ByteOrder.nativeOrder());// use the device hardware's native byte order
                  mTriangleVB = vbb.asFloatBuffer();  // create a floating point buffer from the ByteBuffer
                  mTriangleVB.put(triangleCoords);    // add the coordinates to the FloatBuffer
                  mTriangleVB.position(0);            // set the buffer to read the first coordinate
                  break;
              }
              case USE_ELEMENTS:
              {
                  Log.d(TAG, "using VBO elements");
                  // initialize vertex Buffer for triangle  
                  ByteBuffer vbb = ByteBuffer.allocateDirect(
                          // (# of coordinate values * 4 bytes per float)
                          triangleCoords.length * SIZEOF_FLOAT); 
                  vbb.order(ByteOrder.nativeOrder());// use the device hardware's native byte order
                  mTriangleVB = vbb.asFloatBuffer();  // create a floating point buffer from the ByteBuffer
                  mTriangleVB.put(triangleCoords);    // add the coordinates to the FloatBuffer
                  mTriangleVB.position(0);            // set the buffer to read the first coordinate
      
                  vbb = ByteBuffer.allocateDirect(
                          // (# of coordinate values * 2 bytes per short)
                          indices.length * SIZEOF_SHORT);
                  vbb.order(ByteOrder.nativeOrder()); // use the device hardware's native byte order
                  mIndices = vbb.asShortBuffer();     // create a short buffer from the ByteBuffer
                  mIndices.put(indices);              // add the indices to the Buffer
                  mIndices.position(0);               // set the buffer to read the first index
                  break;
              }
              case USE_VBO_ELEMENTS:
              {
                  Log.d(TAG, "using VBO elements");
                  ByteBuffer vbb = ByteBuffer.allocateDirect(
                          // (# of coordinate values * 4 bytes per float)
                          triangleCoords.length * SIZEOF_FLOAT); 
                  vbb.order(ByteOrder.nativeOrder());// use the device hardware's native byte order
                  mTriangleVB = vbb.asFloatBuffer();  // create a floating point buffer from the ByteBuffer
                  mTriangleVB.put(triangleCoords);    // add the coordinates to the FloatBuffer
                  mTriangleVB.position(0);            // set the buffer to read the first coordinate
      
                  ByteBuffer ibb = ByteBuffer.allocateDirect(
                          indices.length * SIZEOF_SHORT);
                  ibb.order(ByteOrder.nativeOrder()); // use the device hardware's native byte order
                  mIndices = ibb.asShortBuffer();     // create a short buffer from the ByteBuffer
                  mIndices.put(indices);              // add the indices to the Buffer
                  mIndices.position(0);               // set the buffer to read the first index
      
                  GLES20.glGenBuffers(2, mVBOid, 0);
                  checkGLError("initShapes 4");
      
                  GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mVBOid[0]);
                  checkGLError("initShapes 5");
                  GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER,
                          numComponentsPerVertex * SIZEOF_FLOAT,
                          mTriangleVB,
                          GLES20.GL_STATIC_DRAW);
                  checkGLError("initShapes 6");
      
                  GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, mVBOid[1]);
                  checkGLError("initShapes 7");
                  GLES20.glBufferData(GLES20.GL_ELEMENT_ARRAY_BUFFER,
                          mNumIndices * SIZEOF_SHORT,
                          mIndices,
                          GLES20.GL_STATIC_DRAW);
                  checkGLError("initShapes 8");
                  break;
              }
              }
          }
      
          private int loadShader(int type, String shaderCode){
      
              // create a vertex shader type (GLES20.GL_VERTEX_SHADER)
              // or a fragment shader type (GLES20.GL_FRAGMENT_SHADER)
              int shader = GLES20.glCreateShader(type); 
              checkGLError("loadShader 1");
      
              // add the source code to the shader and compile it
              GLES20.glShaderSource(shader, shaderCode);
              checkGLError("loadShader 2");
              GLES20.glCompileShader(shader);
              checkGLError("loadShader 3");
      
              // Get the compilation status.
              final int[] compileStatus = new int[1];
              GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compileStatus, 0);
              checkGLError("loadShader 4");
      
              // If the compilation failed, delete the shader.
              if (compileStatus[0] == 0) 
              {
                  Log.e(TAG, "Error compiling shader: " + GLES20.glGetShaderInfoLog(shader));
                  GLES20.glDeleteShader(shader);
                  checkGLError("loadShader 5");
                  shader = 0;
              }
      
              return shader;
          }
      }
      

      package com.hugo.simplegles20; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.FloatBuffer; import java.nio.ShortBuffer; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.opengles.GL10; import android.opengl.GLES20; import android.opengl.GLSurfaceView; import android.opengl.Matrix; import android.util.Log; public class SimpleOpenGLES20Renderer implements GLSurfaceView.Renderer { public float mAngle; static String TAG = "SimpleTest"; final int SIZEOF_FLOAT = Float.SIZE / 8; final int SIZEOF_SHORT = Short.SIZE / 8; private int[] mVBOid = new int[2]; // 2 ids needed for VBO and index buffer oject enum TestType { USE_ARRAY, // (almost) the original code USE_ELEMENTS, // rendering, using glDrawElements call USE_VBO_ELEMENTS // using a vertex buffer object (VBO) } private TestType mUsage = TestType.USE_VBO_ELEMENTS; private boolean mFourComponents = true; private int mNumIndices = 0; private FloatBuffer mTriangleVB; private ShortBuffer mIndices; private final String vertexShaderCode = // This matrix member variable provides a hook to manipulate // the coordinates of the objects that use this vertex shader "uniform mat4 uMVPMatrix; \n" + "attribute vec4 vPosition; \n" + "void main(){ \n" + // the matrix must be included as a modifier of gl_Position " gl_Position = uMVPMatrix * vPosition; \n" + "} \n"; private final String fragmentShaderCode = "precision mediump float; \n" + "void main(){ \n" + " gl_FragColor = vec4 (0.63671875, 0.76953125, 0.22265625, 1.0); \n" + "} \n"; private int mProgram; private int maPositionHandle; private int muMVPMatrixHandle; private float[] mMVPMatrix = new float[16]; private float[] mMMatrix = new float[16]; private float[] mVMatrix = new float[16]; private float[] mProjMatrix = new float[16]; public static void checkGLError(String msg) { int e = GLES20.glGetError(); if (e != GLES20.GL_NO_ERROR) { Log.d(TAG, "GLES20 ERROR: " + msg + " " + e); Log.d(TAG, errString(e)); } } public static String errString(int ec) { switch (ec) { case GLES20.GL_NO_ERROR: return "No error has been recorded."; case GLES20.GL_INVALID_ENUM: return "An unacceptable value is specified for an enumerated argument."; case GLES20.GL_INVALID_VALUE: return "A numeric argument is out of range."; case GLES20.GL_INVALID_OPERATION: return "The specified operation is not allowed in the current state."; case GLES20.GL_INVALID_FRAMEBUFFER_OPERATION: return "The command is trying to render to or read from the framebuffer" + " while the currently bound framebuffer is not framebuffer complete (i.e." + " the return value from glCheckFramebufferStatus is not" + " GL_FRAMEBUFFER_COMPLETE)."; case GLES20.GL_OUT_OF_MEMORY: return "There is not enough memory left to execute the command." + " The state of the GL is undefined, except for the state" + " of the error flags, after this error is recorded."; default : return "UNKNOW ERROR"; } } @Override public void onSurfaceCreated(GL10 uu, EGLConfig config) { // Set the background frame color GLES20.glClearColor(0.5f, 0.5f, 0.5f, 1.0f); checkGLError("onSurfaceCreated 1"); initShapes(); Log.d(TAG, "load vertex shader"); int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode); Log.d(TAG, "load fragment shader"); int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode); mProgram = GLES20.glCreateProgram(); // create empty OpenGL Program checkGLError("onSurfaceCreated 2"); GLES20.glAttachShader(mProgram, vertexShader); // add the vertex shader to program checkGLError("onSurfaceCreated 3"); GLES20.glAttachShader(mProgram, fragmentShader); // add the fragment shader to program checkGLError("onSurfaceCreated 4"); GLES20.glLinkProgram(mProgram); // creates OpenGL program executables checkGLError("onSurfaceCreated 5"); // get handle to the vertex shader's vPosition member maPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition"); checkGLError("onSurfaceCreated 6"); muMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix"); checkGLError("onSurfaceCreated 7"); } @Override public void onSurfaceChanged(GL10 unused, int width, int height) { GLES20.glViewport(0, 0, width, height); float ratio = (float) width / height; // this projection matrix is applied to object coordinates // in the onDrawFrame() method Matrix.frustumM(mProjMatrix, 0, -ratio, ratio, -1, 1, 3, 7); Matrix.setLookAtM(mVMatrix, 0, 0, 0, -3, 0f, 0f, 0f, 0f, 1.0f, 0.0f); } @Override public void onDrawFrame(GL10 uu) { GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT); checkGLError("onDrawFrame 1"); // Add program to OpenGL environment GLES20.glUseProgram(mProgram); checkGLError("onDrawFrame 2"); // Use the mAngle member as the rotation value Matrix.setRotateM(mMMatrix, 0, mAngle, 0, 0, 1.0f); // Apply a ModelView Projection transformation Matrix.multiplyMM(mMVPMatrix, 0, mVMatrix, 0, mMMatrix, 0); Matrix.multiplyMM(mMVPMatrix, 0, mProjMatrix, 0, mMVPMatrix, 0); GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, mMVPMatrix, 0); checkGLError("onDrawFrame 3"); int nc = mFourComponents ? 4 : 3; int stride = nc * SIZEOF_FLOAT; switch (mUsage) { case USE_ARRAY: // Prepare the triangle data GLES20.glVertexAttribPointer(maPositionHandle, nc, GLES20.GL_FLOAT, false, stride, mTriangleVB); checkGLError("onDrawFrame 4"); GLES20.glEnableVertexAttribArray(maPositionHandle); checkGLError("onDrawFrame 5"); // Draw the triangle GLES20.glDrawArrays(GLES20.GL_TRIANGLE_FAN, 0, mNumIndices); checkGLError("onDrawFrame 6"); break; case USE_ELEMENTS: // Prepare the triangle data GLES20.glVertexAttribPointer(maPositionHandle, nc, GLES20.GL_FLOAT, false, stride, mTriangleVB); checkGLError("onDrawFrame 7"); GLES20.glEnableVertexAttribArray(maPositionHandle); checkGLError("onDrawFrame 8"); // Draw the triangle // int indicesSizeInBytes = SIZEOF_SHORT * mNumIndices; GLES20.glDrawElements(GLES20.GL_TRIANGLE_FAN, mNumIndices, GLES20.GL_UNSIGNED_SHORT, mIndices); checkGLError("onDrawFrame 9"); break; case USE_VBO_ELEMENTS: GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mVBOid[0]); checkGLError("onDrawFrame 14"); GLES20.glVertexAttribPointer(maPositionHandle, nc, GLES20.GL_FLOAT, false, stride, 0); checkGLError("onDrawFrame 15"); GLES20.glEnableVertexAttribArray(maPositionHandle); checkGLError("onDrawFrame 16"); GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, mVBOid[1]); checkGLError("onDrawFrame 17"); GLES20.glDrawElements(GLES20.GL_TRIANGLE_FAN, mNumIndices, GLES20.GL_UNSIGNED_SHORT, 0); checkGLError("onDrawFrame 18"); break; } } private void initShapes(){ float triangleCoords3[] = { // X, Y, Z -0.5f, -0.5f, 0, -0.5f, 0.5f, 0, -0.2f, -0.2f, 0, 0.5f, -0.5f, 0 }; float triangleCoords4[] = { // X, Y, Z, W -0.5f, -0.5f, 0, 1, -0.5f, 0.5f, 0, 1, -0.2f, -0.2f, 0, 1, 0.5f, -0.5f, 0, 1 }; short[] indices = {0,1,2,3}; float[] triangleCoords; int numComponentsPerVertex; if (mFourComponents) { triangleCoords = triangleCoords4; numComponentsPerVertex = 4; } else { triangleCoords = triangleCoords3; numComponentsPerVertex = 3; } mNumIndices = triangleCoords.length / numComponentsPerVertex; Log.d(TAG, "Components per Vertex: " + numComponentsPerVertex); Log.d(TAG, "Number of Indices : " + mNumIndices); switch (mUsage) { case USE_ARRAY: { Log.d(TAG, "using array"); // initialize vertex Buffer for triangle ByteBuffer vbb = ByteBuffer.allocateDirect( // (# of coordinate values * 4 bytes per float) triangleCoords.length * SIZEOF_FLOAT); vbb.order(ByteOrder.nativeOrder());// use the device hardware's native byte order mTriangleVB = vbb.asFloatBuffer(); // create a floating point buffer from the ByteBuffer mTriangleVB.put(triangleCoords); // add the coordinates to the FloatBuffer mTriangleVB.position(0); // set the buffer to read the first coordinate break; } case USE_ELEMENTS: { Log.d(TAG, "using VBO elements"); // initialize vertex Buffer for triangle ByteBuffer vbb = ByteBuffer.allocateDirect( // (# of coordinate values * 4 bytes per float) triangleCoords.length * SIZEOF_FLOAT); vbb.order(ByteOrder.nativeOrder());// use the device hardware's native byte order mTriangleVB = vbb.asFloatBuffer(); // create a floating point buffer from the ByteBuffer mTriangleVB.put(triangleCoords); // add the coordinates to the FloatBuffer mTriangleVB.position(0); // set the buffer to read the first coordinate vbb = ByteBuffer.allocateDirect( // (# of coordinate values * 2 bytes per short) indices.length * SIZEOF_SHORT); vbb.order(ByteOrder.nativeOrder()); // use the device hardware's native byte order mIndices = vbb.asShortBuffer(); // create a short buffer from the ByteBuffer mIndices.put(indices); // add the indices to the Buffer mIndices.position(0); // set the buffer to read the first index break; } case USE_VBO_ELEMENTS: { Log.d(TAG, "using VBO elements"); ByteBuffer vbb = ByteBuffer.allocateDirect( // (# of coordinate values * 4 bytes per float) triangleCoords.length * SIZEOF_FLOAT); vbb.order(ByteOrder.nativeOrder());// use the device hardware's native byte order mTriangleVB = vbb.asFloatBuffer(); // create a floating point buffer from the ByteBuffer mTriangleVB.put(triangleCoords); // add the coordinates to the FloatBuffer mTriangleVB.position(0); // set the buffer to read the first coordinate ByteBuffer ibb = ByteBuffer.allocateDirect( indices.length * SIZEOF_SHORT); ibb.order(ByteOrder.nativeOrder()); // use the device hardware's native byte order mIndices = ibb.asShortBuffer(); // create a short buffer from the ByteBuffer mIndices.put(indices); // add the indices to the Buffer mIndices.position(0); // set the buffer to read the first index GLES20.glGenBuffers(2, mVBOid, 0); checkGLError("initShapes 4"); GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mVBOid[0]); checkGLError("initShapes 5"); GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, numComponentsPerVertex * SIZEOF_FLOAT, mTriangleVB, GLES20.GL_STATIC_DRAW); checkGLError("initShapes 6"); GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, mVBOid[1]); checkGLError("initShapes 7"); GLES20.glBufferData(GLES20.GL_ELEMENT_ARRAY_BUFFER, mNumIndices * SIZEOF_SHORT, mIndices, GLES20.GL_STATIC_DRAW); checkGLError("initShapes 8"); break; } } } private int loadShader(int type, String shaderCode){ // create a vertex shader type (GLES20.GL_VERTEX_SHADER) // or a fragment shader type (GLES20.GL_FRAGMENT_SHADER) int shader = GLES20.glCreateShader(type); checkGLError("loadShader 1"); // add the source code to the shader and compile it GLES20.glShaderSource(shader, shaderCode); checkGLError("loadShader 2"); GLES20.glCompileShader(shader); checkGLError("loadShader 3"); // Get the compilation status. final int[] compileStatus = new int[1]; GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compileStatus, 0); checkGLError("loadShader 4"); // If the compilation failed, delete the shader. if (compileStatus[0] == 0) { Log.e(TAG, "Error compiling shader: " + GLES20.glGetShaderInfoLog(shader)); GLES20.glDeleteShader(shader); checkGLError("loadShader 5"); shader = 0; } return shader; } }

      1. 崩溃转储: 12-18 14:59:02.790:I / DEBUG(85): * * * * * * * * * * < / strong> * * * * * *

        12-18 14:59:02.790:I / DEBUG(85):构建指纹:&#39; Huawei / U8510 / hwu8510:2.3.3 / HuaweiU8510 / C169B831:user / ota-rel-keys,release-密钥&#39;

        12-18 14:59:02.790:I / DEBUG(85):pid:1638,tid:1646&gt;&gt;&gt; com.gles20.step1&lt;&lt;&lt; 12-18 14:59:02.790:I / DEBUG(85):信号11(SIGSEGV),代码1(SEGV_MAPERR),故障地址00368000 12-18 14:59:02.790:I / DEBUG(85):r0 44affc80 r1 00367ff0 r2 0004f03c r3 00000000 12-18 14:59:02.790:I / DEBUG(85):r4 00000000 r5 00000000 r6 00000000 r7 00000028 12-18 14:59:02.790:I / DEBUG(85):r8 00000000 r9 00000000 10 00000000 fp 00000000 12-18 14:59:02.790:I / DEBUG(85):ip 00368000 sp 443ef9d0 lr 80e02a08 pc afd0cd7c cpsr 20000010 12-18 14:59:02.790:I / DEBUG(85):d0 c420e36a40000000 d1 3f800000c4a0e36a 12-18 14:59:02.790:I / DEBUG(85):d2 000000003f800000 d3 000000003f800000 12-18 14:59:02.790:I / DEBUG(85):d4 0000000000000000 d5 0000000000000000 12-18 14:59:02.790:I / DEBUG(85):d6 3f80000000000000 d7 3f8000003f800000 12-18 14:59:02.790:I / DEBUG(85):d8 0000000000000000 d9 0000000000000000 12-18 14:59:02.800:I / DEBUG(85):d10 0000000000000000 d11 0000000000000000 12-18 14:59:02.800:I / DEBUG(85):d12 0000000000000000 d13 0000000000000000 12-18 14:59:02.800:I / DEBUG(85):d14 0000000000000000 d15 0000000000000000 12-18 14:59:02.800:I / DEBUG(85):scr 20000010 12-18 14:59:02.860:I / DEBUG(85):#00 pc 0000cd7c /system/lib/libc.so 12-18 14:59:02.860:I / DEBUG(85):#01 pc 00002a04 /system/lib/libgsl.so 12-18 14:59:02.860:I / DEBUG(85):#02 pc 00089de0 /system/lib/egl/libGLESv2_adreno200.so 12-18 14:59:02.860:I / DEBUG(85):#03 pc 00091a4a /system/lib/egl/libGLESv2_adreno200.so 12-18 14:59:02.860:I / DEBUG(85):#04 pc 000612ca /system/lib/egl/libGLESv2_adreno200.so 12-18 14:59:02.860:I / DEBUG(85):#05 pc 0006138a /system/lib/egl/libGLESv2_adreno200.so 12-18 14:59:02.860:I / DEBUG(85):#06 pc 00063d94 /system/lib/egl/libGLESv2_adreno200.so 12-18 14:59:02.860:I / DEBUG(85):#07 pc 000836aa /system/lib/egl/libGLESv2_adreno200.so 12-18 14:59:02.860:I / DEBUG(85):#08 pc 0003fd66 /system/lib/libandroid_runtime.so 12-18 14:59:02.860:I / DEBUG(85):#09 pc 00012174 /system/lib/libdvm.so 12-18 14:59:02.860:I / DEBUG(85):pc周围的代码: 12-18 14:59:02.860:I / DEBUG(85):afd0cd5c e0422003 e2522020 3a000008 e3c1c01f 12-18 14:59:02.860:I / DEBUG(85):afd0cd6c e28cc040 e8b10ff0 f5dcf040 e2522020 12-18 14:59:02.860:I / DEBUG(85):afd0cd7c 849c3020 e8a00ff0 2afffff9 e2822020 12-18 14:59:02.860:I / DEBUG(85):afd0cd8c e312001f 0a00000c e1b0ce02 28b100f0 12-18 14:59:02.860:I / DEBUG(85):afd0cd9c 48b10300 28a000f0 48a00300 e1b0cf02 12-18 14:59:02.860:I / DEBUG(85):lr周围的代码: 12-18 14:59:02.860:I / DEBUG(85):80e029e8 e5906008 e0831001 e1510006 8a000006 12-18 14:59:02.860:I / DEBUG(85):80e029f8 e5903000 e1a0100e e0830005 eb000a13 12-18 14:59:02.860:I / DEBUG(85):80e02a08 e1a00004 e28dd008 e8bd8070 e59f104c 12-18 14:59:02.860:I / DEBUG(85):80e02a18 e59fe04c e1a02005 e79c0001 e08f100e 12-18 14:59:02.860:I / DEBUG(85):80e02a28 e58d6000 e28000a8 ebfffef8 e3e00000 12-18 14:59:02.860:I / DEBUG(85):堆栈: 12-18 14:59:02.860:I / DEBUG(85):443ef990 0000018c
        12-18 14:59:02.860:I / DEBUG(85):443ef994 811bd8b0
        12-18 14:59:02.860:I / DEBUG(85):443ef998 000000c6
        12-18 14:59:02.860:I / DEBUG(85):443ef99c 443efb68
        12-18 14:59:02.860:I / DEBUG(85):443ef9a0 4360beb4
        12-18 14:59:02.860:I / DEBUG(85):443ef9a4 4360bea0
        12-18 14:59:02.860:I / DEBUG(85):443ef9a8 428da7b4
        12-18 14:59:02.870:I / DEBUG(85):443ef9ac 81089e25 /system/lib/egl/libGLESv2_adreno200.so 12-18 14:59:02.870:I / DEBUG(85):443ef9b0 001e8cc8
        12-18 14:59:02.870:I / DEBUG(85):443ef9b4 443efa6c
        12-18 14:59:02.870:I / DEBUG(85):443ef9b8 00000001
        12-18 14:59:02.870:I / DEBUG(85):443ef9bc 00000001
        12-18 14:59:02.870:I / DEBUG(85):443ef9c0 0000018c
        12-18 14:59:02.870:I / DEBUG(85):443ef9c4 afd10f08 /system/lib/libc.so 12-18 14:59:02.870:I / DEBUG(85):443ef9c8 df002777
        12-18 14:59:02.870:I / DEBUG(85):443ef9cc e3a070ad
        12-18 14:59:02.870:I / DEBUG(85):#00 443ef9d0 00000000
        12-18 14:59:02.870:I / DEBUG(85):443ef9d4 000a3000
        12-18 14:59:02.870:I / DEBUG(85):443ef9d8 0018b834
        12-18 14:59:02.870:I / DEBUG(85):443ef9dc 443efb68
        12-18 14:59:02.870:I / DEBUG(85):443ef9e0 4360beb4
        12-18 14:59:02.870:I / DEBUG(85):443ef9e4 4360bea0
        12-18 14:59:02.870:I / DEBUG(85):443ef9e8 428da7b4
        12-18 14:59:02.870:I / DEBUG(85):443ef9ec 44aac000
        12-18 14:59:02.870:I / DEBUG(85):443ef9f0 00000000
        12-18 14:59:02.870:I / DEBUG(85):443ef9f4 80e02a08 /system/lib/libgsl.so 12-18 14:59:02.870:I / DEBUG(85):#01 443ef9f8 001e9320
        12-18 14:59:02.870:I / DEBUG(85):443ef9fc 00000001
        12-18 14:59:02.870:I / DEBUG(85):443efa00 001e9320
        12-18 14:59:02.870:I / DEBUG(85):443efa04 00000001
        12-18 14:59:02.870:I / DEBUG(85):443efa08 001e9328
        12-18 14:59:02.870:I / DEBUG(85):443efa0c 81089de3 /system/lib/egl/libGLESv2_adreno200.so

1 个答案:

答案 0 :(得分:16)

经过一天的调查后,我发现了一些问题:

  • 忘了取消绑定使用过的缓冲区;在用数据填充缓冲区之后以及在使用它们绘制基元之后,这些调用都丢失了:

    GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mArray);
    GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, mIndices);
    // fill or draw
    // ...
    // unbind:
    GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
    GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, 0);
    
  • 调用glBindAttribLocation必须在正确的时间发生:编译着色器后,但在链接程序之前

    // load and compile shaders ...
    mProgramId = loadProgram(vertexShaderSource, fragmentShaderSource);
    
    // Bind the locations
    GLES20.glBindAttribLocation(mProgramId, Shader.VERTEX_POS, "position");
    GLES20.glBindAttribLocation(mProgramId, Shader.NORMAL_POS, "normal");
    
    // finally link program
    GLES20.glLinkProgram(mProgramId);
    
  • 误解了

    中的索引参数
    GLES20.glBindAttribLocation
    GLES20.glEnableVertexAttribArray
    GLES20.glVertexAttribPointer
    

    呼叫。深入了解规范可以帮助我。这似乎总是一个好主意。

对于那些在VBO设置和使用方面存在一些问题的人来说,将一个简单但完整的OpenGL ES 2.0应用作为起点可能会有所帮助,所以我将在这里发布代码。

我修改了此处找到的应用:https://code.google.com/p/gdc2011-android-opengl,删除了除VBO相关代码之外的所有内容, 设置一些类来封装功能,并成功制作了一个Android / VBO入门套件 这个包是一个单独的文件,包含Activity,一些帮助类,一个基本着色器和一个摄像头类 - 最重要的是 - 一个基本的VBO类,它包含了创建,使用和销毁顶点缓冲区对象的所有功能 该应用程序确实:

  • 设置OpenGL ES 2.0环境
  • 创建一个着色器,它可以渲染亮起/不亮的几何图形
  • 制作固定相机
  • 实例化3个基于VBO的几何,其中一个是线框网格
  • 渲染彩色几何

要使用它,只需创建一个新的Android项目,创建一个活动'GLES20VBOTest'并使用以下文件。

package com.example.vbo;

/*
Note: these not exist or not work before Android 2.3

GLES20.glVertexAttribPointer
GLES20.glDrawElements
 */

import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.ShortBuffer;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

import android.app.Activity;
import android.opengl.GLES20;
import android.opengl.GLSurfaceView;
import android.opengl.Matrix;
import android.os.Bundle;
import android.util.Log;


public class GLES20VBOTest extends Activity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        GLSurfaceView view = new GLSurfaceView(this);
        view.setEGLContextClientVersion(2);
        view.setRenderer(new GDC11Renderer());

        setContentView(view);
    }

}

// Helper class to create some different geometries
class GeoData {

    public float[] mVertices;
    public short[] mIndices;

    private GeoData() {}

    static public GeoData halfpipe() {
        GeoData creator = new GeoData();
        creator.mVertices = createVertices1(44);
        creator.mIndices = createIndices1(44);
        return creator;
    }

    static public GeoData circle() {
        GeoData creator = new GeoData();
        creator.mVertices = createVertices2(32);
        creator.mIndices = createIndices2(32);
        return creator;
    }

    static public GeoData grid() {
        GeoData creator = new GeoData();
        creator.mVertices = createGridVertices(30,30);
        creator.mIndices = createGridIndices(30,30);
        return creator;
    }

    static float[] createGridVertices(int m, int n) {
        float[] vertices = new float[3*(2*m + 2*n + 4)];

        float y = 0.1f;
        float S = 2.8f;
        for (int i=0; i<=m; i++) {
            float x = S*(float) (-0.5 + (1.0*i)/m);
            float z = S*0.5f;
            vertices[6*i + 0] = x;
            vertices[6*i + 1] = y;
            vertices[6*i + 2] = z;
            vertices[6*i + 3] = x;
            vertices[6*i + 4] = y;
            vertices[6*i + 5] = -z;
        }

        int start = 3*(2*m + 2);
        // start = 0;
        for (int i=0; i<=n; i++) {
            float z = S*(float) (-0.5 + (1.0*i)/n);
            float x = S*0.5f;
            vertices[start + 6*i + 0] = x;
            vertices[start + 6*i + 1] = y;
            vertices[start + 6*i + 2] = z;
            vertices[start + 6*i + 3] = -x;
            vertices[start + 6*i + 4] = y;
            vertices[start + 6*i + 5] = z;
        }

        float[] M = new float[16];
        Matrix.setIdentityM(M, 0);
        Matrix.rotateM(M, 0, 27, 0.76f, -0.9f, 1.5f);
        int count = (2*m + 2*n + 4);
        Log.d("MKZ", "A: " + count);
        Log.d("MKZ", "B: " + vertices.length / 3);
        for (int i=0; i<count-1; i++) {
            int offset = 3*i;
            Log.d("MKZ", "offset: " + offset);
            Matrix.multiplyMV(vertices, offset, M, 0, vertices, offset);
        }

        return vertices;
    }

    static short[] createGridIndices(int m, int n) {
        int N = 2*(m+n+2);
        short[] indices = new short[N];
        for (int i=0; i<N; i++) {
            indices[i] = (short)i;
        }
        return indices;
    }

    static float[] createVertices1(int n) {
        int NUM_COMPONENTS = 6;
        float S = 0.75f;
        float X = 1f;
        float z0 = 1.3f;
        float z1 = 1.1f;
        float dx = 2*X / n;
        float[] vertices = new float[NUM_COMPONENTS*(n+1)*2];
        for (int i=0; i<(n+1); i++) {
            int I0 = 2*NUM_COMPONENTS*i;
            int I1 = 2*NUM_COMPONENTS*i + NUM_COMPONENTS;
            float x = -X + dx*i;
            float y = -(float) Math.sqrt(1.0 - x*x);
            vertices[I0 + 0] = S*x;
            vertices[I0 + 1] = S*y;
            vertices[I0 + 2] = S*z0;
            vertices[I0 + 3] = x;
            vertices[I0 + 4] = y;
            vertices[I0 + 5] = 0;

            vertices[I1 + 0] = S*x; 
            vertices[I1 + 1] = S*y;
            vertices[I1 + 2] = S*z1;
            vertices[I1 + 3] = x;
            vertices[I1 + 4] = y;
            vertices[I1 + 5] = 0;
        }
        return vertices;
    }
    static short[] createIndices1(int n) {
        short[] indices = new short[(n+1)*2];
        for (short i=0; i<(n+1)*2; i++) {
            indices[i] = i;
        }
        return indices;
    }

    static float[] createVertices2(int n) {
        int NUM_COMPONENTS = 6;
        float[] vertices = new float[NUM_COMPONENTS*(n+2)];
        final float S = 0.9f;
        final float Y = -0.0f;
        vertices[0] = 0;
        vertices[1] = Y;
        vertices[2] = 0;
        vertices[3] = 0;
        vertices[4] =-1;
        vertices[5] = 0;
        for (int i=0; i<=n; i++) {
            int I = 6 + 6*i;
            float a = (float) (0.75*2*Math.PI*i/n);
            float x = (float) (S*Math.cos(a));
            float z = (float) (S*Math.sin(a));
            vertices[I+0] = x;
            vertices[I+1] = Y;
            vertices[I+2] = z;
            vertices[I+3] = 0;
            vertices[I+4] =-1;
            vertices[I+5] = 0;
        }
        return vertices;
    }
    static short[] createIndices2(int n) {
        short[] indices = new short[(n+2)];
        for (short i=0; i<(n+2); i++) {
            indices[i] = i;
        }
        return indices;
    }
}

// all GLES20 calls are made here
class Shader {
    // THESE ARE ARBITRARY VALUES, the only constraints are
    // - must be different
    // - must be less than a maximum value
    static final int VERTEX_POS = 3;
    static final int NORMAL_POS = 4;
    static final int TEX_POS = 5;
    static final String TAG = "VBOTest";

    private int mProgramId;
    private int mViewProjectionLoc;
    private int mLightVectorLoc;
    private int mColorLoc;
    private int mEnableLightLoc;


    Shader() {
        mProgramId = loadProgram(kVertexShader, kFragmentShader);
        GLES20.glBindAttribLocation(mProgramId, Shader.VERTEX_POS, "position");
        GLES20.glBindAttribLocation(mProgramId, Shader.NORMAL_POS, "normal");
        GLES20.glLinkProgram(mProgramId);
        mViewProjectionLoc =
            GLES20.glGetUniformLocation(mProgramId, "worldViewProjection");
        mLightVectorLoc =
            GLES20.glGetUniformLocation(mProgramId, "lightVector");
        mColorLoc =
            GLES20.glGetUniformLocation(mProgramId, "color");
        mEnableLightLoc =
            GLES20.glGetUniformLocation(mProgramId, "enableLight");

        // Other state.
        GLES20.glClearColor(0.7f, 0.7f, 0.7f, 1.0f);
        GLES20.glEnable(GLES20.GL_CULL_FACE);
        GLES20.glEnable(GLES20.GL_DEPTH_TEST);
    }

    public void use() {
        GLES20.glUseProgram(mProgramId);
    }
    public void setCamera(float[] viewProjectionMatrix) {
        GLES20.glUniformMatrix4fv(mViewProjectionLoc,
                1,
                false, // transpose isn't supported
                viewProjectionMatrix, 0);
    }
    public void setLight(float[] transformedLightVector) {
        GLES20.glUniform3fv(mLightVectorLoc, 1, transformedLightVector, 0);
    }
    public void setColor(float[] color) {
        GLES20.glUniform3fv(mColorLoc, 1, color, 0);
    }
    public void enableLight(boolean val) {
        GLES20.glUniform1i(mEnableLightLoc, val ? 1 : 0);
    }

    static public void setViewPort(int width, int height) {
        GLES20.glViewport(0, 0, width, height);
    }



    private static String kLogTag = "GDC11";

    private static int getShader(String source, int type) {
        int shader = GLES20.glCreateShader(type);
        if (shader == 0) return 0;

        GLES20.glShaderSource(shader, source);
        GLES20.glCompileShader(shader);
        int[] compiled = { 0 };
        GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);
        if (compiled[0] == 0) {
            Log.e(kLogTag, GLES20.glGetShaderInfoLog(shader));
        }
        return shader;
    }

    public static int loadProgram(String vertexShader,
            String fragmentShader) {
        int vs = getShader(vertexShader, GLES20.GL_VERTEX_SHADER);
        int fs = getShader(fragmentShader, GLES20.GL_FRAGMENT_SHADER);
        if (vs == 0 || fs == 0) return 0;

        int program = GLES20.glCreateProgram();
        GLES20.glAttachShader(program, vs);
        GLES20.glAttachShader(program, fs);
        GLES20.glLinkProgram(program);

        int[] linked = { 0 };
        GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linked, 0);
        if (linked[0] == 0) {
            Log.e(kLogTag, GLES20.glGetProgramInfoLog(program));
            return 0;
        }
        return program;
    }


    private static final String kVertexShader =
        "precision mediump float;                                   \n" +
        "uniform mat4 worldViewProjection;                          \n" +
        "uniform vec3 lightVector;                                  \n" +
        "attribute vec3 position;                                   \n" +
        "attribute vec3 normal;                                     \n" +
        "varying float light;                                       \n" +
        "void main() {                                              \n" +
        // |lightVector| is in the model space, so the model
        // doesn't have to be transformed.
        "  light = max(dot(normal, lightVector), 0.0) + 0.2;        \n" +
        "  gl_Position = worldViewProjection * vec4(position, 1.0); \n" +
        "}";

    private static final String kFragmentShader =
        "precision mediump float;                                   \n" +
        "uniform sampler2D textureSampler;                          \n" +
        "uniform vec3 color;                                        \n" +
        "uniform int enableLight;                                   \n" +
        "varying float light;                                       \n" +
        "void main() {                                              \n" +
        "  if (1 == enableLight) {                                  \n" +
        "    gl_FragColor = light * vec4(color,1);                  \n" +
        "  } else {                                                 \n" +
        "    gl_FragColor = vec4(color,1);                          \n" +
        "  }                                                        \n" +
        // "  gl_FragColor = light * vec4(0.1,0.7,0.0,1);               \n" +
        "}";


    public void clearView() {
        int clearMask = GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT;
        GLES20.glClear(clearMask);
    }
}

// view matrices
class Camera {
    private float mPhi, mZ = 3.5f;
    private float[] mProjectionMatrix = new float[16];
    private float[] mViewMatrix = new float[16];
    private float[] mViewProjectionMatrix = new float[16];


    // Updates mViewProjectionMatrix with the current camera position.
    public void updateMatrices() {
        Matrix.setIdentityM(mViewMatrix, 0);
        Matrix.translateM(mViewMatrix, 0, 0, 0, -mZ);
        Matrix.rotateM(mViewMatrix, 0, mPhi, 0, 1, 0);
        Matrix.rotateM(mViewMatrix, 0, -90, 1, 0, 0);
        Matrix.multiplyMM(
                mViewProjectionMatrix, 0, mProjectionMatrix, 0, mViewMatrix, 0);
    }

    public float[] viewMatrix() {
        return mViewMatrix;
    }

    public void perspective(int width, int height) {
        float aspect = width / (float)height;
        perspectiveM(
                mProjectionMatrix,
                (float)Math.toRadians(45),
                aspect, 0.1f, 15.f);
        // aspect, 0.5f, 5.f);
        updateMatrices();
    }

    // Like gluPerspective(), but writes the output to a Matrix.
    static private void perspectiveM(
            float[] m, float angle, float aspect, float near, float far) {
        float f = (float)Math.tan(0.5 * (Math.PI - angle));
        float range = near - far;

        m[0] = f / aspect;
        m[1] = 0;
        m[2] = 0;
        m[3] = 0;

        m[4] = 0;
        m[5] = f;
        m[6] = 0;
        m[7] = 0;

        m[8] = 0;
        m[9] = 0; 
        m[10] = far / range;
        m[11] = -1;

        m[12] = 0;
        m[13] = 0;
        m[14] = near * far / range;
        m[15] = 0;
    }

    public void use(Shader shader) {
        shader.setCamera(mViewProjectionMatrix);
    }
}

// The renderer object.
// Manages the graphic view / content
class GDC11Renderer implements GLSurfaceView.Renderer {

    // OpenGL state stuff.
    private Shader mShader;
    private Camera mCamera;

    VBO mVBO1, mVBO2, mVBO3;

    private float[] mLightVector = { 2/3.f, 1/3.f, 2/3.f };  // Needs to be normalized
    private float[] mTransformedLightVector = new float[3];

    private void updateLightVector() {

        // Transform the light vector into model space. Since mViewMatrix
        // is orthogonal, the reverse transform can be done by multiplying
        // with the transpose.

        float[] viewMatrix = mCamera.viewMatrix();

        mTransformedLightVector[0] =
            viewMatrix[0] * mLightVector[0] +
            viewMatrix[1] * mLightVector[1] +
            viewMatrix[2] * mLightVector[2];
        mTransformedLightVector[1] =
            viewMatrix[4] * mLightVector[0] +
            viewMatrix[5] * mLightVector[1] +
            viewMatrix[6] * mLightVector[2];
        mTransformedLightVector[2] =
            viewMatrix[8] * mLightVector[0] +
            viewMatrix[9] * mLightVector[1] +
            viewMatrix[10] * mLightVector[2];            
    }

    // This is called continuously to render.
    @Override
    public void onDrawFrame(GL10 unused) {

        mShader.use();
        mShader.clearView();
        mCamera.use(mShader);
        mShader.setLight(mTransformedLightVector);

        // VBO
        mShader.enableLight(true);

        mShader.setColor(red);
        mVBO1.draw();

        mShader.setColor(gold);
        mVBO2.draw();

        mShader.enableLight(false);
        mShader.setColor(brown);
        mVBO3.draw();

    }
    static float[] green = {0.2f,1,0.2f};
    static float[] brown = {0.7f,0.4f,0.2f};
    static float[] red = {0.9f,0,0};
    static float[] gold = {0.9f,0.8f,0.1f};
    static float[] black = {0,0,0};


    @Override
    public void onSurfaceCreated(GL10 unused, EGLConfig config) {
        // CREATE GEOMETRY
        // NEVER load stuff on the render thread in real life!
        // You'd call fc.map() and b.load() on a loader thread, and
        // only then upload that to GL once it's done.

        mShader = new Shader();
        mCamera = new Camera();

        GeoData data = GeoData.halfpipe();
        mVBO1 = new VBO(data.mVertices, data.mIndices, GLES20.GL_TRIANGLE_STRIP, true, false, -1);

        data = GeoData.circle();
        mVBO2 = new VBO(data.mVertices, data.mIndices, GLES20.GL_TRIANGLE_FAN, true, false, -1);

        data = GeoData.grid();
        mVBO3 = new VBO(data.mVertices, data.mIndices, GLES20.GL_LINES, false, false, -1);
    }

    // This is called when the surface changes, e.g. after screen rotation.
    @Override
    public void onSurfaceChanged(GL10 unused, int width, int height) {
        mCamera.perspective(width, height);

        updateLightVector();

        // Necessary if the manifest contains |android:configChanges="orientation"|.
        Shader.setViewPort(width, height);
    }
}


class VBO {
    int mNumIndices;

    int mIndexBufferId; 
    int mVertexBufferId;
    boolean mUseNormals;
    boolean mUseTexCoords;

    int mType;
    int mNumComponents;
    int mStride;

    VBO(float[] vertices,               // array of vertex data
            short[] indices,            // indices
            int type,                   // GL_POINTS, GL_LINE_STRIP, GL_LINE_LOOP, GL_LINES,
            // GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN, and GL_TRIANGLES
            boolean vertexNormals,      // normals used ?
            boolean vertexTexCoords,    // texCoords used ?
            int stride) {               // struct size in bytes; if stride <= 0 -> stride will be calculated


        mType = type;
        mUseNormals = vertexNormals;
        mUseTexCoords = vertexTexCoords;

        mNumComponents = 3;
        if (mUseNormals) {
            mNumComponents += 3;
        }
        if (mUseTexCoords) {
            mNumComponents += 2;
        }

        if (stride <= 0) {
            mStride = 4 * mNumComponents;
        } else {
            mStride = stride;
        }

        int[] buffers = {0,0};
        GLES20.glGenBuffers(2, buffers, 0);

        mVertexBufferId = buffers[0];
        mIndexBufferId = buffers[1];

        createVertexBuffer(GLES20.GL_ARRAY_BUFFER, vertices, mVertexBufferId);
        createIndexBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, indices, mIndexBufferId);
        mNumIndices = indices.length;
    }

    void deleteBuffers() {
        int[] buffers = {mVertexBufferId, mIndexBufferId};
        GLES20.glDeleteBuffers(2, buffers, 0);
        mVertexBufferId = 0;
        mIndexBufferId = 0;
    }

    void draw() {
        if (0 == mVertexBufferId) {
            return;
        }
        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mVertexBufferId);

        GLES20.glEnableVertexAttribArray(Shader.VERTEX_POS);
        if (mUseNormals) {
            GLES20.glEnableVertexAttribArray(Shader.NORMAL_POS);
        }
        if (mUseTexCoords) {
            GLES20.glEnableVertexAttribArray(Shader.TEX_POS);
        }

        int offset = 0;

        GLES20.glVertexAttribPointer(
                Shader.VERTEX_POS,      // generic id
                3,                      // vertex has 3 components
                GLES20.GL_FLOAT,        // data type
                false,                  // no normalizing
                mStride,                // stride: sizeof(float) * number of components
                offset);                // offset 0; vertex starts at zero
        offset += 4 * 3;

        if (mUseNormals) {

            GLES20.glVertexAttribPointer(
                    Shader.NORMAL_POS,
                    3,
                    GLES20.GL_FLOAT,
                    false,
                    mStride,
                    offset);
            offset += 4 * 3;
        }

        if (mUseTexCoords) {

            GLES20.glVertexAttribPointer(
                    Shader.TEX_POS,
                    2,                      // texCoord has 2 components
                    GLES20.GL_FLOAT,
                    false,
                    mStride,
                    offset);
            offset += 4 * 3;
        }

        GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, mIndexBufferId);
        GLES20.glDrawElements(mType, mNumIndices, GLES20.GL_UNSIGNED_SHORT, 0);

        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
        GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, 0);

        GLES20.glDisableVertexAttribArray(Shader.VERTEX_POS);
        GLES20.glDisableVertexAttribArray(Shader.NORMAL_POS);
        GLES20.glDisableVertexAttribArray(Shader.TEX_POS);
    }
    static void createVertexBuffer(int target, float[] vertices, int bufferId) {
        int size = vertices.length * 4;
        FloatBuffer fb = ByteBuffer.allocateDirect(4*vertices.length).order(ByteOrder.nativeOrder()).asFloatBuffer();
        fb.put(vertices);
        fb.position(0);

        createBuffer(target, fb, size, bufferId);
    }
    static void createIndexBuffer(int target, short[] indices, int bufferId) {
        int size = indices.length * 2;
        ShortBuffer sb = ByteBuffer.allocateDirect(size).order(ByteOrder.nativeOrder()).asShortBuffer();
        sb.put(indices);
        sb.position(0);

        createBuffer(target, sb, size, bufferId);
    }
    static void createBuffer(int target, Buffer buf, int size, int bufferId) {
        GLES20.glBindBuffer(target, bufferId);
        GLES20.glBufferData(target, size, buf, GLES20.GL_STATIC_DRAW);
        GLES20.glBindBuffer(target, 0);
    }
}