顶点缓冲区对象(VBO)不适用于Android 2.3.3,使用GLES20

顶点缓冲区对象(VBO)不适用于Android 2.3.3,使用GLES20,android,opengl-es-2.0,shader,vbo,Android,Opengl Es 2.0,Shader,Vbo,在Android上,我尝试运行一个简单的OpenGL ES 2.0应用程序,它使用顶点缓冲区对象,但失败了 我从这个项目开始: http://developer.android.com/resources/tutorials/opengl/opengl-es20.html 每件事都很好地解释了,并且按照描述工作。好的 我添加了一些代码,以交替使用glDrawElements命令而不是gldrawArray进行渲染。 我成功了 现在,下一步:我想使用顶点缓冲区对象来做同样的事情 所以我补充说:

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

我从这个项目开始:

http://developer.android.com/resources/tutorials/opengl/opengl-es20.html
每件事都很好地解释了,并且按照描述工作。好的

我添加了一些代码,以交替使用glDrawElements命令而不是gldrawArray进行渲染。 我成功了

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

所以我补充说:

  • 新变量:

    私有int[]mVBOid=新int[2];//VBO和索引缓冲区项目需要2个ID 私人短缓冲区mIndices;//使用的索引

  • 添加了创建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);
    
  • 添加了绘制几何图形的代码:

        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);
    
  • 注意:由于第一个实现的新功能在不使用VBO的情况下使用GLDraweElements渲染几何体正在工作,我知道mTriangleVB和mIndices变量正确地填充了所需的数据。 此外,MappositionHandle和muMVPMatrixHandle也是正确的。 在我的代码中,我检查总账错误-未找到任何错误

    我的问题是:VBO技术不起作用;除了清晰的颜色,屏幕上什么也看不到。 在更复杂的应用程序中,我会遇到更多问题:

    • 当引入基于VBO的几何体时,其他未使用VBO且已正确渲染的几何体将不可见

    • 偶尔会报告分段错误。为了找出确切的原因,我注释了很多代码,最后发现,即使没有渲染几何体,也会发生崩溃。 崩溃的原因必须是VBO的初始化-虽然崩溃不是立即发生的,而是一段时间后发生的。
      但我还是不明白为什么它不起作用

    以下是更多信息:

  • 我的环境:

    • 安卓2.3.3
    • 构建目标:安卓2.3.3
    • Android SDK工具:Rev。十五
    • Android SDK平台工具:修订版。九,
    • 设备:华为Ideos X3智能手机
  • SimplePengles20Renderer类的完整源代码。
    代码基于此示例:

  • 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;
        }
    }
    

  • 崩溃转储: 12-18 14:59:02.790:I/DEBUG(85):**********************

    12-18 14:59:02.790:I/DEBUG(85):构建指纹:“华为/U8510/hwu8510:2.3.3/HuaweiU8510/C169B831:用户/ota rel密钥,释放密钥”


    12-18 14:59:02.790:I/DEBUG(85):pid:1638,tid:1646>>>com.gles20.step1经过一天的调查,我发现我的代码存在一些问题:

    • 忘记解开用过的缓冲区;在用数据填充缓冲区并使用它们绘制原语后,这些调用丢失:

      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);
      
    • 调用GLBindAttriblLocation必须在正确的时间发生:编译着色器之后,链接程序之前

      // 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
      
      电话。对规格的深入了解有助于我解决问题。这似乎总是个好主意

    有一个简单但完整的OpenGL ES 2.0应用程序作为起点,对其他在VBO设置和使用方面有问题的人可能会有所帮助,因此我将在这里发布代码

    我修改了此处找到的应用程序:,删除了除VBO相关代码以外的所有内容, 设置一些类来封装功能,并成功制作了Android/VBO初学者工具包。
    这个包是一个单独的文件,包含活动、一些辅助类、一个基本着色器和一个摄影机类,最重要的是- 一个基本的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);
        }
    }
    
    package com.example.vbo;
    /*
    注意:在Android 2.3之前,这些都不存在或不起作用
    GLES20.GlvertexAttribute指针
    GLES20.GldUnderElements
    */
    导入java.nio.Buffer;
    导入java.nio.ByteBuffer;
    导入java.nio.ByteOrder;
    导入java.nio.FloatBuffer;
    导入java.nio.ShortBuffer;
    导入javax.microedition.khronos.egl.EGLConfig;
    导入javax.microedition.khronos.opengles.GL10;
    导入android.app.Activity;
    导入android.opengl.GLES20;
    导入android.opengl.GLSurfaceView;
    导入android.opengl.Matrix;
    导入android.os.Bundle;
    导入android.util.Log;
    公共类GLES20VBOTest扩展活动{
    @凌驾
    创建时的公共void(Bundle savedInstanceState){
    super.onCreate(savedInstanceState);
    GLSURFACHEVIEW视图=新GLSURFACHEVIEW(此);
    view.setEGLContextClientVersion(2);
    view.setRenderer(新的GDC11Renderer());
    setContentView(视图);
    }
    }
    //帮助器类来创建一些不同的几何图形
    类地理数据{
    公共浮动利率;
    公众短期思维;
    专用地理数据(){}
    静态公共地理数据半管(){
    地理数据创建者=新地理数据();
    creator.mVertices=createVertices1(44);
    creator.mIndices=createIndices1(44);
    回归创造者;
    }
    静态公共地理数据圆(){
    地理数据创建者=新地理数据();
    creator.mVertices=createVertices2(32);
    creator.mIndices=createIndices2(32);
    回归创造者;
    }
    静态公共地理数据网格(){
    地理数据创建者=新地理数据();
    creator.mVertices=createGridVertices(30,30);
    creator.mIndices=createGridIndex(30,30);
    回归创造者;
    }
    静态浮点[]CreateGrid顶点(int m,int n){
    float[]顶点=新的float[3*(