Vertex Buffer Objects (VBO) не работает на Android 2.3.3, используя GLES20

На Android я пытаюсь запустить приложение OpenGL ES 2.0, которое использует объект буфера вершин, но я потерпел неудачу.

Я начал с этого проекта:

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

Все хорошо объяснено и работает, как описано. Хорошо.

Я добавил код, чтобы сделать рендеринг с помощью команды glDrawElements вместо glDrawArrays. Мне удалось.

Теперь следующий шаг: я хочу использовать объект буфера вершин, чтобы сделать то же самое.

Поэтому я добавил следующее:

  1. Новые vars:

    Private int [] mVBOid = new int [2]; // 2 идентификатора, необходимые для VBO и индексного буфера, для выделения частных команд ShortBuffer; // используемые индексы

  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 не работает; На экране ничего не видно, кроме прозрачного цвета. В более сложном приложении возникают дополнительные проблемы:

  • Другие геометрии без использования VBOs, которые были обработаны правильно, невидимы, когда вводится геометрия на основе VBO

  • Часто встречаются ошибки сегментации. Пытаясь получить точную причину, я прокомментировал много кода и, наконец, обнаружил, что авария происходит, даже если геометрия вообще не отображается. Причиной аварии является инициализация VBO – хотя авария происходит не сразу, а через некоторое время.
    Но я до сих пор не могу понять, почему он не работает.

Вот еще информация:

  1. Моя окружающая среда:

    • Android 2.3.3
    • Цель сборки: Android 2.3.3
    • Android SDK Tools: Rev. 15
    • Инструменты платформы Android SDK: откр. 9
    • Устройство: смартфон Huawei 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 (ie" + " 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: пользователь / ota-rel-keys,

    12-18 14: 59: 02.790: I / DEBUG (85): pid: 1638, tid: 1646 >>> com.gles20.step1 <<< 12-18 14: 59: 02.790: I / DEBUG (85): Сигнал 11 (SIGSEGV), код 1 (SEGV_MAPERR), ошибка addr 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 0000000000 000000 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 (8 5): код около 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

После другого дня расследования я обнаружил некоторые проблемы с моим кодом:

  • Забыл отменить используемые буферы; Эти вызовы отсутствовали после заполнения буфера данными и после их использования для рисования примитива:

     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
  • Создать шейдер, который способен отображать голые / неосвещенные геометрии
  • Создать неподвижную камеру
  • Создать три геометрии на основе 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, eg 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; /* 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, eg 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); } } 
Intereting Posts