Mateus Zitelli Mateus Zitelli - 2 months ago 18
Java Question

Empty render with OpenGL on Android

I'm trying to render a scene using OpenGL ES 2.0 in an Android App, however, the screen keeps being drawn with the defined clear color.

I had success rendering simpler examples, but I'm failing when using packed vertex buffer.

The idea is to render a scene with a camera inside a Sphere, here are some code snippets which should give some context and also is where I think the bug is.

Fragment Shader:

#extension GL_OES_EGL_image_external : require
precision mediump float;
varying vec2 vTextureCoord;
uniform inputImageTexture;
void main()
{
vec4 color = texture2D(inputImageTexture, vTextureCoord);
gl_FragColor = vec4(1.0);
}


Vertex Shader:

uniform mat4 uMVPMatrix;
uniform mat4 uTransform;
attribute vec4 aPosition;
attribute vec4 aTextureCoord;
varying vec2 vTextureCoord;
void main()
{
gl_Position = uMVPMatrix * aPosition * vec4(1, -1, 1, 1);
vec4 coord = (aTextureCoord / 2.0) + 0.5;
vTextureCoord = (uTransform * coord).xy;
}


The GL program initialization:

mProgram = new ProgramObject();
if(mProgram.init(vsh, fshResult)) {
GLES20.glClearColor(1.0f, 0.f, 0.f, 1.f);
GLES20.glDisable(GLES20.GL_DEPTH_TEST);
GLES20.glDisable(GLES20.GL_CULL_FACE);
uMVPMatrixLocation = mProgram.getUniformLoc("uMVPMatrix");
aPositionLocation = mProgram.attributeLocation("aPosition");
aTextureCoordLocation = mProgram.attributeLocation("aTextureCoord");
mTransformLoc = mProgram.getUniformLoc("uTransform");
}


Vertex loading:

sphere = new Sphere(SPHERE_SLICES, 0.f, 0.f, 0.f, SPHERE_RADIUS, SPHERE_INDICES_PER_VERTEX);
mProgram.bind();
final int buffers[] = new int[1];
GLES20.glGenBuffers(1, buffers, 0);
mVertexBuffer = buffers[0];
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mVertexBuffer);
GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, sphere.getVerticesSize(), sphere.getVertices(), GLES20.GL_STATIC_DRAW);


Render:

GLES20.glViewport(viewport.x, viewport.y, viewport.width, viewport.height);
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GLES20.glBindTexture(TEXTURE_2D_BINDABLE, texID);

GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mVertexBuffer);
GLES20.glEnableVertexAttribArray(aPositionLocation);
GLES20.glVertexAttribPointer(aPositionLocation, 3,
GLES20.GL_FLOAT, false, sphere.getVerticesStride(), sphere.getVertices());

GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mVertexBuffer);
GLES20.glEnableVertexAttribArray(aTextureCoordLocation);
GLES20.glVertexAttribPointer(aTextureCoordLocation, 2,
GLES20.GL_FLOAT, false, sphere.getVerticesStride(),
sphere.getVertices().duplicate().position(3));

Matrix.multiplyMM(pvMatrix, 0, projectionMatrix, 0, viewMatrix, 0);
Matrix.multiplyMM(mvpMatrix, 0, pvMatrix, 0, modelMatrix , 0);
GLES20.glUniformMatrix4fv(uMVPMatrixLocation, 1, false, mvpMatrix, 0);

for (int j = 0; j < sphere.getNumIndices().length; ++j) {
GLES20.glDrawElements(GLES20.GL_TRIANGLES, sphere.getNumIndices()[j], GLES20.GL_UNSIGNED_SHORT, sphere.getIndices()[j]);
}


Any insights are welcome, thank you very much.

Update

The could was running without any errors on a Galaxy S6, but, now running on a One Plus X I'm getting the following error:

<gl_draw_error_checks:598>: GL_INVALID_OPERATION
glDrawElements: glError 0x502


After running:

for (int j = 0; j < sphere.getNumIndices().length; ++j) {
GLES20.glDrawElements(GLES20.GL_TRIANGLES, sphere.getNumIndices()[j], GLES20.GL_UNSIGNED_SHORT, sphere.getIndices()[j]);
}

Answer

You're mixing up two ways of using vertex arrays:

GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mVertexBuffer);
...
GLES20.glVertexAttribPointer(aPositionLocation, 3,
    GLES20.GL_FLOAT, false, sphere.getVerticesStride(), sphere.getVertices());

In ES 2.0, there are two ways of using vertex arrays:

  • Client-side vertex arrays.
  • Vertex-buffer objects (VBOs).

With client-side vertex arrays, you don't store your vertex data in VBOs, but pass the vertex data directly as the last argument to the glVertexAttribPointer() call. In C/C++ bindings, this is a pointer to the data, in the Java bindings it's a Java Buffer object.

When using VBOs, you store your vertex data in a VBO, using glBufferData(). Then you bind the VBO before the glVertexAttribPointer() call, and pass the offset of the data relative to the start of the VBO as the last argument.

In your example:

GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mVertexBuffer);
GLES20.glEnableVertexAttribArray(aPositionLocation);
GLES20.glVertexAttribPointer(aPositionLocation, 3,
    GLES20.GL_FLOAT, false, sphere.getVerticesStride(), 0);

GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mVertexBuffer);
GLES20.glEnableVertexAttribArray(aTextureCoordLocation);
GLES20.glVertexAttribPointer(aTextureCoordLocation, 2,
    GLES20.GL_FLOAT, false, sphere.getVerticesStride(), 12);

Note that the offset is in bytes, so it's 12 bytes if your colors are offset by 3 floats relative to the positions.

Client-side vertex arrays are deprecated, and partly unavailable, in newer versions of OpenGL. So storing the data in VBOs, as you already are, is the right way to go.

Comments