Объекты буфера вершин (VBO) не работают на Android 2.3.3, используя GLES20
на Android я пытаюсь запустить простое приложение OpenGL ES 2.0, которое использует объект буфера вершин, но мне не удалось.
Я начал с этого проекта:
http://developer.android.com/resources/tutorials/opengl/opengl-es20.html
все хорошо объяснено и работает, как описано. Штраф.
Я добавил код, чтобы сделать рендеринг альтернативно с командой glDrawElements вместо glDrawArrays. Мне это удалось.
теперь следующий шаг: я хочу использовать объект буфера вершин, чтобы сделать то же самое вещь.
поэтому я добавил следующее:
-
новые vars:
private int[] mVBOid = new int[2]; / / 2 идентификатора, необходимые для извлечения буфера VBO и индекса private ShortBuffer mIndices; / / используемые индексы
-
добавлен код для создания VBO:
ByteBuffer vbb = ByteBuffer.allocateDirect( triangleCoords.length * SIZEOF_FLOAT); vbb.order(ByteOrder.nativeOrder());// use the device hardware's native byte order mTriangleVB = vbb.asFloatBuffer(); // create a floating point buffer from the ByteBuffer mTriangleVB.put(triangleCoords); // add the coordinates to the FloatBuffer mTriangleVB.position(0); // set the buffer to read the first coordinate ByteBuffer ibb = ByteBuffer.allocateDirect( indices.length * SIZEOF_SHORT); ibb.order(ByteOrder.nativeOrder()); // use the device hardware's native byte order mIndices = ibb.asShortBuffer(); // create a short buffer from the ByteBuffer mIndices.put(indices); // add the indices to the Buffer mIndices.position(0); // set the buffer to read the first index GLES20.glGenBuffers(2, mVBOid, 0); GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mVBOid[0]); GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, numComponentsPerVertex * SIZEOF_FLOAT, mTriangleVB, GLES20.GL_STATIC_DRAW); GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, mVBOid[1]); GLES20.glBufferData(GLES20.GL_ELEMENT_ARRAY_BUFFER, mNumIndices * SIZEOF_SHORT, mIndices, GLES20.GL_STATIC_DRAW);
-
добавлен код для рисования геометрии:
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mVBOid[0]); GLES20.glVertexAttribPointer(maPositionHandle, nc, GLES20.GL_FLOAT, false, stride, 0); GLES20.glEnableVertexAttribArray(maPositionHandle); GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, mVBOid[1]); GLES20.glDrawElements(GLES20.GL_TRIANGLE_FAN, mNumIndices, GLES20.GL_UNSIGNED_SHORT, 0);
Примечание: как первая реализованная новая функциональность для визуализация геометрии с помощью glDrawElements без использования VBO работает, я знаю, что переменные mTriangleVB и mIndices правильно заполнены необходимыми данными. Также mapositionhandle и muMVPMatrixHandle являются правильными. В моем коде я проверяю ошибки GL - ни одной не найдено
моя проблема: техника VBO не работает; на экране ничего не видно, кроме прозрачного цвета. В более сложном приложении я получаю больше проблем:
другие геометрии без использования VBOs, которые были визуализированы правильно, невидимы, когда вводится геометрия на основе VBO
ошибки сегментации сообщаются в отдельных случаях. Пытаясь получить точную причину, я прокомментировал много кода и, наконец, обнаружил, что сбой происходит, даже если геометрия не отображается вообще. Причиной аварии должна быть инициализация VBO-хотя авария происходит не сразу, а некоторое время спустя.
Но я все еще не могу понять. почему это не работает.
вот еще информация:
-
Мое Окружение:
- Android 2.3.3
- Цель Сборки: Android 2.3.3
- Android SDK Tools: Rev. 15
- инструменты платформы Android SDK: Rev. 9
- Устройство: Huawei Ideos X3 Смартфон
полный источник для класса SimpleOpenGLES20Renderer.
Этот код основан на этом sample:
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;
}
}
-
аварийный дамп: 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.Шаг1 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.так что ... 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.так что ... 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.так что ... 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.так что ...
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
звонки. Более глубокий взгляд в спецификации помогает мне. Кажется, это всегда хорошая идея.
это может быть полезно для других, у кого есть некоторые проблемы с настройкой и использованием VBO, чтобы иметь простое, но полное приложение OpenGL ES 2.0 в качестве отправной точки, поэтому я опубликую код здесь.
Я изменил приложение, найденное здесь:https://code.google.com/p/gdc2011-android-opengl, удалил все, кроме VBO соответствующий код,
настройте некоторые классы для инкапсуляции функциональности и преуспели в создании стартового набора Android / VBO.
Этот пакет представляет собой один файл, содержащий Activity, некоторые вспомогательные классы, базовый шейдер и класс камеры и-самое главное -
базовый класс VBO, который обертывает все функции для создания, использования и уничтожения объектов буфера вершин.
Приложение:
- настройка среды OpenGL ES 2.0
- создайте шейдер, который способен рендеринга освещенной / неосвещенной геометрии
- создать фиксированную камеру
- создать экземпляр 3 геометрии на основе VBO, одной из которых является каркасная сетка
- отображать цветное геометрия
чтобы использовать его, просто создайте новый проект Android, создайте действие "GLES20VBOTest" и используйте следующий файл.
package com.example.vbo;
/*
Note: these not exist or not work before Android 2.3
GLES20.glVertexAttribPointer
GLES20.glDrawElements
*/
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.ShortBuffer;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
import android.app.Activity;
import android.opengl.GLES20;
import android.opengl.GLSurfaceView;
import android.opengl.Matrix;
import android.os.Bundle;
import android.util.Log;
public class GLES20VBOTest extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
GLSurfaceView view = new GLSurfaceView(this);
view.setEGLContextClientVersion(2);
view.setRenderer(new GDC11Renderer());
setContentView(view);
}
}
// Helper class to create some different geometries
class GeoData {
public float[] mVertices;
public short[] mIndices;
private GeoData() {}
static public GeoData halfpipe() {
GeoData creator = new GeoData();
creator.mVertices = createVertices1(44);
creator.mIndices = createIndices1(44);
return creator;
}
static public GeoData circle() {
GeoData creator = new GeoData();
creator.mVertices = createVertices2(32);
creator.mIndices = createIndices2(32);
return creator;
}
static public GeoData grid() {
GeoData creator = new GeoData();
creator.mVertices = createGridVertices(30,30);
creator.mIndices = createGridIndices(30,30);
return creator;
}
static float[] createGridVertices(int m, int n) {
float[] vertices = new float[3*(2*m + 2*n + 4)];
float y = 0.1f;
float S = 2.8f;
for (int i=0; i<=m; i++) {
float x = S*(float) (-0.5 + (1.0*i)/m);
float z = S*0.5f;
vertices[6*i + 0] = x;
vertices[6*i + 1] = y;
vertices[6*i + 2] = z;
vertices[6*i + 3] = x;
vertices[6*i + 4] = y;
vertices[6*i + 5] = -z;
}
int start = 3*(2*m + 2);
// start = 0;
for (int i=0; i<=n; i++) {
float z = S*(float) (-0.5 + (1.0*i)/n);
float x = S*0.5f;
vertices[start + 6*i + 0] = x;
vertices[start + 6*i + 1] = y;
vertices[start + 6*i + 2] = z;
vertices[start + 6*i + 3] = -x;
vertices[start + 6*i + 4] = y;
vertices[start + 6*i + 5] = z;
}
float[] M = new float[16];
Matrix.setIdentityM(M, 0);
Matrix.rotateM(M, 0, 27, 0.76f, -0.9f, 1.5f);
int count = (2*m + 2*n + 4);
Log.d("MKZ", "A: " + count);
Log.d("MKZ", "B: " + vertices.length / 3);
for (int i=0; i<count-1; i++) {
int offset = 3*i;
Log.d("MKZ", "offset: " + offset);
Matrix.multiplyMV(vertices, offset, M, 0, vertices, offset);
}
return vertices;
}
static short[] createGridIndices(int m, int n) {
int N = 2*(m+n+2);
short[] indices = new short[N];
for (int i=0; i<N; i++) {
indices[i] = (short)i;
}
return indices;
}
static float[] createVertices1(int n) {
int NUM_COMPONENTS = 6;
float S = 0.75f;
float X = 1f;
float z0 = 1.3f;
float z1 = 1.1f;
float dx = 2*X / n;
float[] vertices = new float[NUM_COMPONENTS*(n+1)*2];
for (int i=0; i<(n+1); i++) {
int I0 = 2*NUM_COMPONENTS*i;
int I1 = 2*NUM_COMPONENTS*i + NUM_COMPONENTS;
float x = -X + dx*i;
float y = -(float) Math.sqrt(1.0 - x*x);
vertices[I0 + 0] = S*x;
vertices[I0 + 1] = S*y;
vertices[I0 + 2] = S*z0;
vertices[I0 + 3] = x;
vertices[I0 + 4] = y;
vertices[I0 + 5] = 0;
vertices[I1 + 0] = S*x;
vertices[I1 + 1] = S*y;
vertices[I1 + 2] = S*z1;
vertices[I1 + 3] = x;
vertices[I1 + 4] = y;
vertices[I1 + 5] = 0;
}
return vertices;
}
static short[] createIndices1(int n) {
short[] indices = new short[(n+1)*2];
for (short i=0; i<(n+1)*2; i++) {
indices[i] = i;
}
return indices;
}
static float[] createVertices2(int n) {
int NUM_COMPONENTS = 6;
float[] vertices = new float[NUM_COMPONENTS*(n+2)];
final float S = 0.9f;
final float Y = -0.0f;
vertices[0] = 0;
vertices[1] = Y;
vertices[2] = 0;
vertices[3] = 0;
vertices[4] =-1;
vertices[5] = 0;
for (int i=0; i<=n; i++) {
int I = 6 + 6*i;
float a = (float) (0.75*2*Math.PI*i/n);
float x = (float) (S*Math.cos(a));
float z = (float) (S*Math.sin(a));
vertices[I+0] = x;
vertices[I+1] = Y;
vertices[I+2] = z;
vertices[I+3] = 0;
vertices[I+4] =-1;
vertices[I+5] = 0;
}
return vertices;
}
static short[] createIndices2(int n) {
short[] indices = new short[(n+2)];
for (short i=0; i<(n+2); i++) {
indices[i] = i;
}
return indices;
}
}
// all GLES20 calls are made here
class Shader {
// THESE ARE ARBITRARY VALUES, the only constraints are
// - must be different
// - must be less than a maximum value
static final int VERTEX_POS = 3;
static final int NORMAL_POS = 4;
static final int TEX_POS = 5;
static final String TAG = "VBOTest";
private int mProgramId;
private int mViewProjectionLoc;
private int mLightVectorLoc;
private int mColorLoc;
private int mEnableLightLoc;
Shader() {
mProgramId = loadProgram(kVertexShader, kFragmentShader);
GLES20.glBindAttribLocation(mProgramId, Shader.VERTEX_POS, "position");
GLES20.glBindAttribLocation(mProgramId, Shader.NORMAL_POS, "normal");
GLES20.glLinkProgram(mProgramId);
mViewProjectionLoc =
GLES20.glGetUniformLocation(mProgramId, "worldViewProjection");
mLightVectorLoc =
GLES20.glGetUniformLocation(mProgramId, "lightVector");
mColorLoc =
GLES20.glGetUniformLocation(mProgramId, "color");
mEnableLightLoc =
GLES20.glGetUniformLocation(mProgramId, "enableLight");
// Other state.
GLES20.glClearColor(0.7f, 0.7f, 0.7f, 1.0f);
GLES20.glEnable(GLES20.GL_CULL_FACE);
GLES20.glEnable(GLES20.GL_DEPTH_TEST);
}
public void use() {
GLES20.glUseProgram(mProgramId);
}
public void setCamera(float[] viewProjectionMatrix) {
GLES20.glUniformMatrix4fv(mViewProjectionLoc,
1,
false, // transpose isn't supported
viewProjectionMatrix, 0);
}
public void setLight(float[] transformedLightVector) {
GLES20.glUniform3fv(mLightVectorLoc, 1, transformedLightVector, 0);
}
public void setColor(float[] color) {
GLES20.glUniform3fv(mColorLoc, 1, color, 0);
}
public void enableLight(boolean val) {
GLES20.glUniform1i(mEnableLightLoc, val ? 1 : 0);
}
static public void setViewPort(int width, int height) {
GLES20.glViewport(0, 0, width, height);
}
private static String kLogTag = "GDC11";
private static int getShader(String source, int type) {
int shader = GLES20.glCreateShader(type);
if (shader == 0) return 0;
GLES20.glShaderSource(shader, source);
GLES20.glCompileShader(shader);
int[] compiled = { 0 };
GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);
if (compiled[0] == 0) {
Log.e(kLogTag, GLES20.glGetShaderInfoLog(shader));
}
return shader;
}
public static int loadProgram(String vertexShader,
String fragmentShader) {
int vs = getShader(vertexShader, GLES20.GL_VERTEX_SHADER);
int fs = getShader(fragmentShader, GLES20.GL_FRAGMENT_SHADER);
if (vs == 0 || fs == 0) return 0;
int program = GLES20.glCreateProgram();
GLES20.glAttachShader(program, vs);
GLES20.glAttachShader(program, fs);
GLES20.glLinkProgram(program);
int[] linked = { 0 };
GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linked, 0);
if (linked[0] == 0) {
Log.e(kLogTag, GLES20.glGetProgramInfoLog(program));
return 0;
}
return program;
}
private static final String kVertexShader =
"precision mediump float; \n" +
"uniform mat4 worldViewProjection; \n" +
"uniform vec3 lightVector; \n" +
"attribute vec3 position; \n" +
"attribute vec3 normal; \n" +
"varying float light; \n" +
"void main() { \n" +
// |lightVector| is in the model space, so the model
// doesn't have to be transformed.
" light = max(dot(normal, lightVector), 0.0) + 0.2; \n" +
" gl_Position = worldViewProjection * vec4(position, 1.0); \n" +
"}";
private static final String kFragmentShader =
"precision mediump float; \n" +
"uniform sampler2D textureSampler; \n" +
"uniform vec3 color; \n" +
"uniform int enableLight; \n" +
"varying float light; \n" +
"void main() { \n" +
" if (1 == enableLight) { \n" +
" gl_FragColor = light * vec4(color,1); \n" +
" } else { \n" +
" gl_FragColor = vec4(color,1); \n" +
" } \n" +
// " gl_FragColor = light * vec4(0.1,0.7,0.0,1); \n" +
"}";
public void clearView() {
int clearMask = GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT;
GLES20.glClear(clearMask);
}
}
// view matrices
class Camera {
private float mPhi, mZ = 3.5f;
private float[] mProjectionMatrix = new float[16];
private float[] mViewMatrix = new float[16];
private float[] mViewProjectionMatrix = new float[16];
// Updates mViewProjectionMatrix with the current camera position.
public void updateMatrices() {
Matrix.setIdentityM(mViewMatrix, 0);
Matrix.translateM(mViewMatrix, 0, 0, 0, -mZ);
Matrix.rotateM(mViewMatrix, 0, mPhi, 0, 1, 0);
Matrix.rotateM(mViewMatrix, 0, -90, 1, 0, 0);
Matrix.multiplyMM(
mViewProjectionMatrix, 0, mProjectionMatrix, 0, mViewMatrix, 0);
}
public float[] viewMatrix() {
return mViewMatrix;
}
public void perspective(int width, int height) {
float aspect = width / (float)height;
perspectiveM(
mProjectionMatrix,
(float)Math.toRadians(45),
aspect, 0.1f, 15.f);
// aspect, 0.5f, 5.f);
updateMatrices();
}
// Like gluPerspective(), but writes the output to a Matrix.
static private void perspectiveM(
float[] m, float angle, float aspect, float near, float far) {
float f = (float)Math.tan(0.5 * (Math.PI - angle));
float range = near - far;
m[0] = f / aspect;
m[1] = 0;
m[2] = 0;
m[3] = 0;
m[4] = 0;
m[5] = f;
m[6] = 0;
m[7] = 0;
m[8] = 0;
m[9] = 0;
m[10] = far / range;
m[11] = -1;
m[12] = 0;
m[13] = 0;
m[14] = near * far / range;
m[15] = 0;
}
public void use(Shader shader) {
shader.setCamera(mViewProjectionMatrix);
}
}
// The renderer object.
// Manages the graphic view / content
class GDC11Renderer implements GLSurfaceView.Renderer {
// OpenGL state stuff.
private Shader mShader;
private Camera mCamera;
VBO mVBO1, mVBO2, mVBO3;
private float[] mLightVector = { 2/3.f, 1/3.f, 2/3.f }; // Needs to be normalized
private float[] mTransformedLightVector = new float[3];
private void updateLightVector() {
// Transform the light vector into model space. Since mViewMatrix
// is orthogonal, the reverse transform can be done by multiplying
// with the transpose.
float[] viewMatrix = mCamera.viewMatrix();
mTransformedLightVector[0] =
viewMatrix[0] * mLightVector[0] +
viewMatrix[1] * mLightVector[1] +
viewMatrix[2] * mLightVector[2];
mTransformedLightVector[1] =
viewMatrix[4] * mLightVector[0] +
viewMatrix[5] * mLightVector[1] +
viewMatrix[6] * mLightVector[2];
mTransformedLightVector[2] =
viewMatrix[8] * mLightVector[0] +
viewMatrix[9] * mLightVector[1] +
viewMatrix[10] * mLightVector[2];
}
// This is called continuously to render.
@Override
public void onDrawFrame(GL10 unused) {
mShader.use();
mShader.clearView();
mCamera.use(mShader);
mShader.setLight(mTransformedLightVector);
// VBO
mShader.enableLight(true);
mShader.setColor(red);
mVBO1.draw();
mShader.setColor(gold);
mVBO2.draw();
mShader.enableLight(false);
mShader.setColor(brown);
mVBO3.draw();
}
static float[] green = {0.2f,1,0.2f};
static float[] brown = {0.7f,0.4f,0.2f};
static float[] red = {0.9f,0,0};
static float[] gold = {0.9f,0.8f,0.1f};
static float[] black = {0,0,0};
@Override
public void onSurfaceCreated(GL10 unused, EGLConfig config) {
// CREATE GEOMETRY
// NEVER load stuff on the render thread in real life!
// You'd call fc.map() and b.load() on a loader thread, and
// only then upload that to GL once it's done.
mShader = new Shader();
mCamera = new Camera();
GeoData data = GeoData.halfpipe();
mVBO1 = new VBO(data.mVertices, data.mIndices, GLES20.GL_TRIANGLE_STRIP, true, false, -1);
data = GeoData.circle();
mVBO2 = new VBO(data.mVertices, data.mIndices, GLES20.GL_TRIANGLE_FAN, true, false, -1);
data = GeoData.grid();
mVBO3 = new VBO(data.mVertices, data.mIndices, GLES20.GL_LINES, false, false, -1);
}
// This is called when the surface changes, e.g. after screen rotation.
@Override
public void onSurfaceChanged(GL10 unused, int width, int height) {
mCamera.perspective(width, height);
updateLightVector();
// Necessary if the manifest contains |android:configChanges="orientation"|.
Shader.setViewPort(width, height);
}
}
class VBO {
int mNumIndices;
int mIndexBufferId;
int mVertexBufferId;
boolean mUseNormals;
boolean mUseTexCoords;
int mType;
int mNumComponents;
int mStride;
VBO(float[] vertices, // array of vertex data
short[] indices, // indices
int type, // GL_POINTS, GL_LINE_STRIP, GL_LINE_LOOP, GL_LINES,
// GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN, and GL_TRIANGLES
boolean vertexNormals, // normals used ?
boolean vertexTexCoords, // texCoords used ?
int stride) { // struct size in bytes; if stride <= 0 -> stride will be calculated
mType = type;
mUseNormals = vertexNormals;
mUseTexCoords = vertexTexCoords;
mNumComponents = 3;
if (mUseNormals) {
mNumComponents += 3;
}
if (mUseTexCoords) {
mNumComponents += 2;
}
if (stride <= 0) {
mStride = 4 * mNumComponents;
} else {
mStride = stride;
}
int[] buffers = {0,0};
GLES20.glGenBuffers(2, buffers, 0);
mVertexBufferId = buffers[0];
mIndexBufferId = buffers[1];
createVertexBuffer(GLES20.GL_ARRAY_BUFFER, vertices, mVertexBufferId);
createIndexBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, indices, mIndexBufferId);
mNumIndices = indices.length;
}
void deleteBuffers() {
int[] buffers = {mVertexBufferId, mIndexBufferId};
GLES20.glDeleteBuffers(2, buffers, 0);
mVertexBufferId = 0;
mIndexBufferId = 0;
}
void draw() {
if (0 == mVertexBufferId) {
return;
}
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mVertexBufferId);
GLES20.glEnableVertexAttribArray(Shader.VERTEX_POS);
if (mUseNormals) {
GLES20.glEnableVertexAttribArray(Shader.NORMAL_POS);
}
if (mUseTexCoords) {
GLES20.glEnableVertexAttribArray(Shader.TEX_POS);
}
int offset = 0;
GLES20.glVertexAttribPointer(
Shader.VERTEX_POS, // generic id
3, // vertex has 3 components
GLES20.GL_FLOAT, // data type
false, // no normalizing
mStride, // stride: sizeof(float) * number of components
offset); // offset 0; vertex starts at zero
offset += 4 * 3;
if (mUseNormals) {
GLES20.glVertexAttribPointer(
Shader.NORMAL_POS,
3,
GLES20.GL_FLOAT,
false,
mStride,
offset);
offset += 4 * 3;
}
if (mUseTexCoords) {
GLES20.glVertexAttribPointer(
Shader.TEX_POS,
2, // texCoord has 2 components
GLES20.GL_FLOAT,
false,
mStride,
offset);
offset += 4 * 3;
}
GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, mIndexBufferId);
GLES20.glDrawElements(mType, mNumIndices, GLES20.GL_UNSIGNED_SHORT, 0);
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, 0);
GLES20.glDisableVertexAttribArray(Shader.VERTEX_POS);
GLES20.glDisableVertexAttribArray(Shader.NORMAL_POS);
GLES20.glDisableVertexAttribArray(Shader.TEX_POS);
}
static void createVertexBuffer(int target, float[] vertices, int bufferId) {
int size = vertices.length * 4;
FloatBuffer fb = ByteBuffer.allocateDirect(4*vertices.length).order(ByteOrder.nativeOrder()).asFloatBuffer();
fb.put(vertices);
fb.position(0);
createBuffer(target, fb, size, bufferId);
}
static void createIndexBuffer(int target, short[] indices, int bufferId) {
int size = indices.length * 2;
ShortBuffer sb = ByteBuffer.allocateDirect(size).order(ByteOrder.nativeOrder()).asShortBuffer();
sb.put(indices);
sb.position(0);
createBuffer(target, sb, size, bufferId);
}
static void createBuffer(int target, Buffer buf, int size, int bufferId) {
GLES20.glBindBuffer(target, bufferId);
GLES20.glBufferData(target, size, buf, GLES20.GL_STATIC_DRAW);
GLES20.glBindBuffer(target, 0);
}
}