【问题标题】:Vertex Buffer Objects (VBO) not working on Android 2.3.3, using GLES20顶点缓冲区对象 (VBO) 无法在 Android 2.3.3 上使用 GLES20
【发布时间】:2012-01-23 01:17:49
【问题描述】:

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

我是从这个项目开始的:

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

一切都得到了很好的解释,并且按照描述的方式工作。很好。

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

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

所以我添加了这个:

  1. 新变量:

    私有 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);
    

注意:由于第一个使用 glDrawElements 渲染几何图形而不使用 VBO 的新功能正在运行,我知道 mTriangleVB 和 mIndices 变量正确填充了所需的数据。 maPositionHandle 和 muMVPMatrixHandle 也是正确的。 在我的代码中,我检查了 GL 错误 - 没有找到

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

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

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

这里有更多信息:

  1. 我的环境:

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

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): * ** * ** * ** * ** * * * *

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

    12-18 14:59:02.790: I/DEBUG(85): pid: 1638, tid: 1646 >>> com.gles20.step1 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

【问题讨论】:

    标签: android opengl-es-2.0 shader vbo


    【解决方案1】:

    经过一天的调查,我发现我的代码存在一些问题:

    • 忘记解除绑定使用过的缓冲区;在用数据填充缓冲区并使用它们绘制图元后,这些调用丢失了:

      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);
      
    • 对index参数的误解

      GLES20.glBindAttribLocation
      GLES20.glEnableVertexAttribArray
      GLES20.glVertexAttribPointer
      

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

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

    我已经修改了此处找到的应用程序:https://code.google.com/p/gdc2011-android-opengl,删除了除 VBO 相关代码之外的所有内容, 设置了一些类来封装功能,并成功制作了一个Android / VBO starter kit。
    这个包是一个单独的文件,包含 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);
        }
    }
    

    【讨论】:

    • 感谢您发布此内容!我也遇到了 VBO 问题。当然这对我有帮助
    猜你喜欢
    • 2011-11-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-06-12
    • 2012-08-08
    • 1970-01-01
    相关资源
    最近更新 更多