Can't achieve 60fps rendering simple quad, Android, Opengl ES 2.0 - android

I'm working on a simple pong type game to get to grips with opengl and android, and seem to have hit a brick wall in terms of performance.
I've got my game logic on a separate thread, with draw commands sent to the gl thread through a blocking queue. The problem is that I'm stuck at around 40fps, and nothing I've tried seems to improve the framerate.
To keep things simple I set up opengl with:
GLES20.glDisable(GLES20.GL_CULL_FACE);
GLES20.glDisable(GLES20.GL_DEPTH_TEST);
GLES20.glDisable(GLES20.GL_BLEND);
Set up of the opengl program and drawing is handled by the following class:
class GLRectangle {
private final String vertexShaderCode =
"precision lowp float;" +
// This matrix member variable provides a hook to manipulate
// the coordinates of the objects that use this vertex shader
"uniform lowp mat4 uMVPMatrix;" +
"attribute lowp vec4 vPosition;" +
"void main() {" +
// the matrix must be included as a modifier of gl_Position
" gl_Position = vPosition * uMVPMatrix;" +
"}";
private final String fragmentShaderCode =
"precision lowp float;" +
"uniform lowp vec4 vColor;" +
"void main() {" +
" gl_FragColor = vColor;" +
"}";
protected static int mProgram = 0;
private static ShortBuffer drawListBuffer;
private static short drawOrder[] = { 0, 1, 2, 0, 2, 3};//, 4, 5, 6, 4, 6, 7 }; // order to draw vertices
// number of coordinates per vertex in this array
private static final int COORDS_PER_VERTEX = 3;
private static final int vertexStride = COORDS_PER_VERTEX * 4; // bytes per vertex
GLRectangle(){
int vertexShader = GameRenderer.loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode);
int fragmentShader = GameRenderer.loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode);
mProgram = GLES20.glCreateProgram(); // create empty OpenGL ES Program
GLES20.glAttachShader(mProgram, vertexShader); // add the vertex shader to program
GLES20.glAttachShader(mProgram, fragmentShader); // add the fragment shader to program
GLES20.glLinkProgram(mProgram); // creates OpenGL ES program executables
// initialize byte buffer for the index list
ByteBuffer dlb = ByteBuffer.allocateDirect(
// (# of coordinate values * 2 bytes per short)
drawOrder.length * 2);
dlb.order(ByteOrder.nativeOrder());
drawListBuffer = dlb.asShortBuffer();
drawListBuffer.put(drawOrder);
drawListBuffer.position(0);
}
protected static void Draw(Drawable dObj, float mvpMatrix[])
{
FloatBuffer vertexBuffer = dObj.vertexBuffer;
GLES20.glUseProgram(mProgram);
//GameRenderer.checkGlError("glUseProgram");
// get handle to fragment shader's vColor member
int mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor");
//GameRenderer.checkGlError("glGetUniformLocation");
// get handle to shape's transformation matrix
int mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
//GameRenderer.checkGlError("glGetUniformLocation");
// get handle to vertex shader's vPosition member
int mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
//GameRenderer.checkGlError("glGetAttribLocation");
// Apply the projection and view transformation
GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mvpMatrix, 0);
//GameRenderer.checkGlError("glUniformMatrix4fv");
// Set color for drawing the quad
GLES20.glUniform4fv(mColorHandle, 1, dObj.color, 0);
//GameRenderer.checkGlError("glUniform4fv");
// Enable a handle to the square vertices
GLES20.glEnableVertexAttribArray(mPositionHandle);
//GameRenderer.checkGlError("glEnableVertexAttribArray");
// Prepare the square coordinate data
GLES20.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX,
GLES20.GL_FLOAT, false,
vertexStride, vertexBuffer);
//GameRenderer.checkGlError("glVertexAttribPointer");
// Draw the square
GLES20.glDrawElements(GLES20.GL_TRIANGLES, drawOrder.length,
GLES20.GL_UNSIGNED_SHORT, drawListBuffer);
//GameRenderer.checkGlError("glDrawElements");
// Disable vertex array
GLES20.glDisableVertexAttribArray(mPositionHandle);
//GameRenderer.checkGlError("glDisableVertexAttribArray");
}
}
I've done plenty of profiling and googling, but cant find anything to make this work faster... I've included a screenshot of the DDMS output:
To me, it looks like glClear is causeing GLThread to sleep for 23ms... though I doubt that's really the case.
I have absolutely no idea how I can make this more efficient, there's nothing fancy going on. In my quest for better rendering performance I have switched to the multi-threaded approach I described, turned off alpha blending and depth testing, changed to a batched drawing approach (not applicable for this simple example), and switched everything to lowp in the shaders.
Any assistance with getting this to 60fps would be greatly appreciated!
Bruce
edit Well talk about overthinking a problem. It turns out that I've had the powersaving mode switched on for the past week... it seems to lock rendering to 40fps.

This behavior occurs when Power Saving mode is switched on, using a Galaxy S3.
It appears the power saving mode locks the framerate to 40fps. Switching it off easily achieved the desired 60fps.

Not sure if you have control over the EGL setup to the device surface, but if you can there is a way to set the update to run in "non-sync" mode.
egl.eglSwapInterval( dpy, 0 )
This isn't available on all devices but allows some control of your rendering.

Related

Cannot get attribute location for attributes after Varying Variable has been introduced

I'm trying to implement a ModelViewer that can visualize triangulated shapes with realistic lighting.
Since realistic lighting doesn't seem possible with OpenGL ES 1.0 and I need a way to present depth for a single colored object, the project uses OpenGL ES 2.0 which is new to me.
The object itself consists of triangles that are drawn using:
GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount);
As for a test project, I've set up simple shaders that draw the object in consideration of the model-view-projection matrix. After that, my intention was to implement lighting but I cannot get past the first steps.
When working with varying variables, the vertex attributes won't be found.
Removing the varying attributes works, but i need to pass data. I've searched for days on how to implement vertex and fragment shaders that do more than present an object at a location.
// VERTEX SHADER CODE
attribute vec4 v_Position;
uniform mat4 u_MVPMatrix;
uniform vec4 u_Color;
varying vec4 v_Color;
void main() {
gl_Position = u_MVPMatrix * v_Position;
v_Color = u_Color;
};
// FRAGMENT SHADER CODE
precision mediump float;
varying vec4 v_Color;
void main() {
gl_FragColor = v_Color;
}
The whole Class:
public class Object3D {
private FloatBuffer vertexBuffer;
// number of coordinates per vertex in this array
static final int COORDS_PER_VERTEX = 3;
// static float triangleCoords[] = { // in counterclockwise order:
// 0.0f, 0.622008459f, 0.0f, // top
// -0.5f, -0.311004243f, 0.0f, // bottom left
// 0.5f, -0.311004243f, 0.0f // bottom right
// };
float[] triangleCoords;
// Set color with red, green, blue and alpha (opacity) values
float[] colors = { 0.63671875f, 0.76953125f, 0.22265625f, 1.0f };
private final String vertexShaderCode =
"attribute vec4 v_Position;" +
"uniform float u_Color" +
"uniform mat4 u_MVPMatrix;" +
// outgoing
"varying vec4 v_Color" +
"void main() {" +
// the matrix must be included as a modifier of gl_Position
// Note that the uMVPMatrix factor *must be first* in order
// for the matrix multiplication product to be correct.
"gl_Position = u_MVPMatrix * v_Position;" +
"v_Color = u_Color;" +
"}";
private final String fragmentShaderCode =
"precision mediump float;" +
"varying vec4 v_Color;" +
"void main() {" +
"gl_FragColor = v_Color;" +
"}";
// Use to access and set the view transformation
private int mMVPMatrixHandle;
private final int mProgram;
private int mPositionHandle;
private int mColorHandle;
private final int vertexCount;
private final int vertexStride;
public Object3D(float[] triangleCoords) {
this.triangleCoords = triangleCoords;
vertexCount = triangleCoords.length / COORDS_PER_VERTEX;
vertexStride = COORDS_PER_VERTEX * 4; // 4 bytes per vertex
this.colors = new float[4*vertexCount];
for (int i = 0; i < colors.length; i+=4) {
colors[i] = 0.63671875f;
colors[i+1] = 0.76953125f;
colors[i+2] = 0.22265625f;
colors[i+3] = 1.0f;
}
// initialize vertex byte buffer for shape coordinates
ByteBuffer bb = ByteBuffer.allocateDirect(
// (number of coordinate values * 4 bytes per float)
triangleCoords.length * 4);
// use the device hardware's native byte order
bb.order(ByteOrder.nativeOrder());
// create a floating point buffer from the ByteBuffer
vertexBuffer = bb.asFloatBuffer();
// add the coordinates to the FloatBuffer
vertexBuffer.put(triangleCoords);
// set the buffer to read the first coordinate
vertexBuffer.position(0);
int vertexShader = MyGLRenderer.loadShader(GLES20.GL_VERTEX_SHADER,
vertexShaderCode);
int fragmentShader = MyGLRenderer.loadShader(GLES20.GL_FRAGMENT_SHADER,
fragmentShaderCode);
// create empty OpenGL ES Program
mProgram = GLES20.glCreateProgram();
// add the vertex shader to program
GLES20.glAttachShader(mProgram, vertexShader);
// add the fragment shader to program
GLES20.glAttachShader(mProgram, fragmentShader);
GLES20.glBindAttribLocation(mProgram, 0, "v_Position");
// GLES20.glBindAttribLocation(mProgram, 1, "vColor");
// creates OpenGL ES program executables
GLES20.glLinkProgram(mProgram);
}
public void draw(float[] mvpMatrix) { // pass in the calculated transformation matrix
// Add program to OpenGL environment
GLES20.glUseProgram(mProgram);
// get handle to vertex shader's vPosition member
mPositionHandle = GLES20.glGetAttribLocation(mProgram, "v_Position");
// if (mPositionHandle == -1) {
// throw new RuntimeException(
// "Could not get attrib location for v_Position");
// }
// Enable a handle to the triangle vertices
GLES20.glEnableVertexAttribArray(mPositionHandle);
// Prepare the triangle coordinate data
GLES20.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX,
GLES20.GL_FLOAT, false,
vertexStride, vertexBuffer);
// get handle to fragment shader's vColor member
mColorHandle = GLES20.glGetUniformLocation(mProgram, "v_Color");
// Set color for drawing the triangle
GLES20.glUniform4fv(mColorHandle, 1, colors, 0);
// get handle to shape's transformation matrix
mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "u_MVPMatrix");
// Apply the projection and view transformation
GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mvpMatrix, 0);
// // get handle to shape's transformation matrix
// int mColorHandleU = GLES20.glGetUniformLocation(mProgram, "u_Color");
//
// // Apply the projection and view transformation
// GLES20.glUniform4fv(mColorHandleU, 1, new float[] {}, 0);
// Draw the triangle
GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount);
// Disable vertex array
GLES20.glDisableVertexAttribArray(mPositionHandle);
}
}
If I want to do some necessary color calculations, i need varying variables to pass information from the vertex to the fragment shader. However, I cannot seem to get this done.
The Error I keep getting is:
2019-05-14 21:54:25.122 8281-8316/com.example.opengles20 E/emuglGLESv2_enc: device/generic/goldfish-opengl/system/GLESv2_enc/GL2Encoder.cpp:s_glEnableVertexAttribArray:892 GL error 0x501
Info: Invalid vertex attribute index. Wanted index: 4294967295. Max index: 16
2019-05-14 21:54:25.123 8281-8316/com.example.opengles20 E/emuglGLESv2_enc: device/generic/goldfish-opengl/system/GLESv2_enc/GL2Encoder.cpp:s_glVertexAttribPointer:604 GL error 0x501
Info: Invalid vertex attribute index. Wanted index: 4294967295. Max index: 16
2019-05-14 21:54:25.124 8281-8316/com.example.opengles20 E/emuglGLESv2_enc: device/generic/goldfish-opengl/system/GLESv2_enc/GL2Encoder.cpp:s_glDisableVertexAttribArray:901 GL error 0x501
Info: Invalid vertex attribute index. Wanted index: 4294967295. Max index: 16
2019-05-14 21:54:25.237 8281-8316/com.example.opengles20 E/emuglGLESv2_enc: device/generic/goldfish-opengl/system/GLESv2_enc/GL2Encoder.cpp:s_glEnableVertexAttribArray:892 GL error 0x501
Info: Invalid vertex attribute index. Wanted index: 4294967295. Max index: 16
2019-05-14 21:54:25.237 8281-8316/com.example.opengles20 E/emuglGLESv2_enc: device/generic/goldfish-opengl/system/GLESv2_enc/GL2Encoder.cpp:s_glVertexAttribPointer:604 GL error 0x501
Info: Invalid vertex attribute index. Wanted index: 4294967295. Max index: 16
2019-05-14 21:54:25.238 8281-8316/com.example.opengles20 E/emuglGLESv2_enc: device/generic/goldfish-opengl/system/GLESv2_enc/GL2Encoder.cpp:s_glDisableVertexAttribArray:901 GL error 0x501
Info: Invalid vertex attribute index. Wanted index: 4294967295. Max index: 16
Also, the following exception, when implemented, is thrown:
mPositionHandle = GLES20.glGetAttribLocation(mProgram, "v_Position");
if (mPositionHandle == -1) {
throw new RuntimeException(
"Could not get attrib location for v_Position");
}
I know that for attributes, the instruction flow goes like this:
GLES20.glBindAttribLocation(...);
-- Link Shader Program --
attributeHandle = GLES20.glGetAttribLocation(programHandle, "a_AttributenName");
GLES20.glEnableVertexAttribArray(attributeHandle);
GLES20.glVertexAttribPointer(programHandle, ..., buffer);
The instruction sequence for uniforms go like this:
uniformHandle = GLES20.glGetUniformLocation(mProgram, "u_UniformName");
// do something with it, for example:
GLES20.glUniform4fv(uniformHandle, ...);
But what is there to do for varying variables?
Thanks in advance!
private final String vertexShaderCode =
"attribute vec4 v_Position;" +
"uniform float u_Color" +
"uniform mat4 u_MVPMatrix;" +
// outgoing
"varying vec4 v_Color" +
"void main() {" +
You're missing a semi-colon after u_Color and also v_Color. Presumably your vertex shader is not compiling and that's cascading down into the errors you're seeing.
It's a pain, but it really does save time in the long run to check for errors after every OpenGLES call (glGetError). Getting detailed shader compile error logs is also fiddly but worth putting in place - see here (glGetShaderInfoLog, GL_COMPILE_STATUS).

Android - Draw a Cylinder

I'm using OpenGL and I already have everything up and running. I can plot triangles and other shapes but now I need to make a tube. It doesnt need to hollow(if it makes it easier).
Something like this: Image of the cilinder
How can I define a cilinder in a 3D? I think I need x,y,z and Radius. Can someone give me ideas where to start? Thank you.
Draw a triangle code:
class Triangle {
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;" +
"attribute vec4 vPosition;" +
"void main() {" +
// the matrix must be included as a modifier of gl_Position
// Note that the uMVPMatrix factor *must be first* in order
// for the matrix multiplication product to be correct.
" gl_Position = uMVPMatrix * vPosition;" +
"}";
// Use to access and set the view transformation
private int mMVPMatrixHandle;
private final String fragmentShaderCode =
"precision mediump float;" +
"uniform vec4 vColor;" +
"void main() {" +
" gl_FragColor = vColor;" +
"}";
// number of coordinates per vertex in this array
static final int COORDS_PER_VERTEX = 3;
static float triangleCoords[] = { // in counterclockwise order:
0.0f, 0.622008459f, 0.0f, // top
-0.5f, -0.311004243f, 0.0f, // bottom left
0.5f, -0.311004243f, 0.0f // bottom right
};
// Set color with red, green, blue and alpha (opacity) values
float color[] = { 0.5f, 0.5f, 0.5f, 1.0f };
private final int mProgram;
private short[] indices = {0,1,2,0,2,3};
private FloatBuffer vertexBuffer;
private ShortBuffer indexBuffer;
public Triangle() {
// initialize vertex byte buffer for shape coordinates
ByteBuffer bb = ByteBuffer.allocateDirect(
// (number of coordinate values * 4 bytes per float)
triangleCoords.length * 4);
// use the device hardware's native byte order
bb.order(ByteOrder.nativeOrder());
// create a floating point buffer from the ByteBuffer
vertexBuffer = bb.asFloatBuffer();
// add the coordinates to the FloatBuffer
vertexBuffer.put(triangleCoords);
// set the buffer to read the first coordinate
vertexBuffer.position(0);
int vertexShader = OpenGLRenderer.loadShader(GLES20.GL_VERTEX_SHADER,
vertexShaderCode);
int fragmentShader = OpenGLRenderer.loadShader(GLES20.GL_FRAGMENT_SHADER,
fragmentShaderCode);
// create empty OpenGL ES Program
mProgram = GLES20.glCreateProgram();
// add the vertex shader to program
GLES20.glAttachShader(mProgram, vertexShader);
// add the fragment shader to program
GLES20.glAttachShader(mProgram, fragmentShader);
// creates OpenGL ES program executables
GLES20.glLinkProgram(mProgram);
}
private int mPositionHandle;
private int mColorHandle;
private final int vertexCount = triangleCoords.length / COORDS_PER_VERTEX;
private final int vertexStride = COORDS_PER_VERTEX * 4; // 4 bytes per vertex
public void drawMatrix(float[] mvpMatrix) { // pass in the calculated transformation matrix
// Add program to OpenGL ES environment
GLES20.glUseProgram(mProgram);
// get handle to vertex shader's vPosition member
mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
// Enable a handle to the triangle vertices
GLES20.glEnableVertexAttribArray(mPositionHandle);
// Prepare the triangle coordinate data
GLES20.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX,
GLES20.GL_FLOAT, false,
vertexStride, vertexBuffer);
// get handle to fragment shader's vColor member
mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor");
// Set color for drawing the triangle
GLES20.glUniform4fv(mColorHandle, 1, color, 0);
// get handle to shape's transformation matrix
mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
// Pass the projection and view transformation to the shader
GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mvpMatrix, 0);
// Draw the triangle
GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount);
// Disable vertex array
GLES20.glDisableVertexAttribArray(mPositionHandle);
}
No matter how you define it internally, you still have to provide a set of triangles, lines or points for OpenGL ES.
So you would need to define two equal regular polygons that lay on parallel planes. The more vertices they have the coser they will be to the circles. You can see this yourself with the formula:
R is a radius of circumscribed circle, r is a radius of inscribed circle, n is anumber of vertices. The closer r is to R the closer the polygon is to the circle. So, the polygon will appear more like a circle if you use more vertices: when n→∞, π/n→0, cos(π/n)→1 so r→R.
You could divide the polygon in different ways. For example, like this:
This way you'll have n triangles.
Or you could go this way:
This way you will have n-2 triangles.
There could be better ways, search for them on the net.

set diffusion, specular lightening of square opengl 2.0 in Android

I am new to opengl ES 2.0. I want to set differents colors to each vertex of a triangle
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.android.opengl;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.ShortBuffer;
import android.opengl.GLES20;
/**
* A two-dimensional square for use as a drawn object in OpenGL ES 2.0.
*/
public class Square {
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;" +
"attribute vec4 vPosition;" +
"void main() {" +
// The matrix must be included as a modifier of gl_Position.
// Note that the uMVPMatrix factor *must be first* in order
// for the matrix multiplication product to be correct.
" gl_Position = uMVPMatrix * vPosition;" +
"}";
private final String fragmentShaderCode =
"precision mediump float;" +
"uniform vec4 vColor;" +
"void main() {" +
" gl_FragColor = vColor;" +
"}";
private final FloatBuffer vertexBuffer;
private final ShortBuffer drawListBuffer;
private final int mProgram;
private int mPositionHandle;
private int mColorHandle;
private int mMVPMatrixHandle;
// number of coordinates per vertex in this array
static final int COORDS_PER_VERTEX = 3;
static float squareCoords[] = {
-0.5f, 0.5f, 0.0f, // top left
-0.5f, -0.5f, 0.0f, // bottom left
0.5f, -0.5f, 0.0f, // bottom right
0.5f, 0.5f, 0.0f }; // top right
private final short drawOrder[] = { 0, 1, 2, 0, 2, 3 }; // order to draw vertices
private final int vertexStride = COORDS_PER_VERTEX * 4; // 4 bytes per vertex
float color[] = { 0.2f, 0.709803922f, 0.898039216f, 1.0f,
1.0f, 0.0f, 0.0f, 1.0f,
0.0f, 1.0f, 0.0f, 1.0f };
/**
* Sets up the drawing object data for use in an OpenGL ES context.
*/
public Square() {
// initialize vertex byte buffer for shape coordinates
ByteBuffer bb = ByteBuffer.allocateDirect(
// (# of coordinate values * 4 bytes per float)
squareCoords.length * 4);
bb.order(ByteOrder.nativeOrder());
vertexBuffer = bb.asFloatBuffer();
vertexBuffer.put(squareCoords);
vertexBuffer.position(0);
// initialize byte buffer for the draw list
ByteBuffer dlb = ByteBuffer.allocateDirect(
// (# of coordinate values * 2 bytes per short)
drawOrder.length * 2);
dlb.order(ByteOrder.nativeOrder());
drawListBuffer = dlb.asShortBuffer();
drawListBuffer.put(drawOrder);
drawListBuffer.position(0);
// prepare shaders and OpenGL program
int vertexShader = MyGLRenderer.loadShader(
GLES20.GL_VERTEX_SHADER,
vertexShaderCode);
int fragmentShader = MyGLRenderer.loadShader(
GLES20.GL_FRAGMENT_SHADER,
fragmentShaderCode);
mProgram = GLES20.glCreateProgram(); // create empty OpenGL Program
GLES20.glAttachShader(mProgram, vertexShader); // add the vertex shader to program
GLES20.glAttachShader(mProgram, fragmentShader); // add the fragment shader to program
GLES20.glLinkProgram(mProgram); // create OpenGL program executables
}
/**
* Encapsulates the OpenGL ES instructions for drawing this shape.
*
* #param mvpMatrix - The Model View Project matrix in which to draw
* this shape.
*/
public void draw(float[] mvpMatrix) {
// Add program to OpenGL environment
GLES20.glUseProgram(mProgram);
// get handle to vertex shader's vPosition member
mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
// Enable a handle to the triangle vertices
GLES20.glEnableVertexAttribArray(mPositionHandle);
// Prepare the triangle coordinate data
GLES20.glVertexAttribPointer(
mPositionHandle, COORDS_PER_VERTEX,
GLES20.GL_FLOAT, false,
vertexStride, vertexBuffer);
// get handle to fragment shader's vColor member
mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor");
// Set color for drawing the triangle
GLES20.glUniform4fv(mColorHandle, 1, color, 0);
// get handle to shape's transformation matrix
mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
MyGLRenderer.checkGlError("glGetUniformLocation");
// Apply the projection and view transformation
GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mvpMatrix, 0);
MyGLRenderer.checkGlError("glUniformMatrix4fv");
// Draw the square
GLES20.glDrawElements(
GLES20.GL_TRIANGLES, drawOrder.length,
GLES20.GL_UNSIGNED_SHORT, drawListBuffer);
// Disable vertex array
GLES20.glDisableVertexAttribArray(mPositionHandle);
}
}
I have one more question: Should I create matrix for rotation, scaling and tranformation or I should use Matrix.multiplyMM(scratch, 0, mMVPMatrix, 0, mRotationMatrix, 0);
functions as it is.
If this is a question about lighting a square you should add what have you tried and what went wrong or what part are you unclear of. There seem to be no code relevant to lighting in what you posted. There are a lot of posts on how to do that on the web.
To set different colors to the vertices you need a new attribute in your shaders instead of the uniform. The fragment shader should have attribute vec4 aColor; and varying vec4 vColor; then set vColor = aColor; in main. The fragment shader should also contain varying vec4 vColor; which replaces uniform vec4 vColor;. In the code you need to do all the same as for the positions but use the color array and bind it to aColor.
To create matrix for rotation, scaling and translation depends on what you are doing but generally no, it is a bad idea. If you need a reference to those values it is better to keep the angle, scale and translation data as floats and vectors which you use to construct the matrix when needed.

OpenGL ES 2 on Android: how to use VBOs

this question is similar to something I asked here Android OpenGL ES 2: Introduction to VBOs
however I tried multiple aproaches since then and I still haven't succeeded, so I think posting another question where I offer aditional details would be a better aproach.
I am new to OpenGL ES 2 on Android (I have never worked with another OpenGL, I just need to draw something for an app I am developing for Android) and I would very much like to understand how to use VBOs. I tried to modify this OpenGL ES 2 for Android tutorial to use VBOs when drawing the triangle. I tried to use this step by step guide and this tutorial but I still don't understand everything, I am rather new to all of these things. My app currently crashes on start. Here's what I have:
public class Triangle {
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;" +
"attribute vec4 vPosition;" +
"void main() {" +
// the matrix must be included as a modifier of gl_Position
// Note that the uMVPMatrix factor *must be first* in order
// for the matrix multiplication product to be correct.
" gl_Position = uMVPMatrix * vPosition;" +
"}";
private final String fragmentShaderCode =
"precision mediump float;" +
"uniform vec4 vColor;" +
"void main() {" +
" gl_FragColor = vColor;" +
"}";
private final FloatBuffer vertexBuffer;
private final int mProgram;
private int mPositionHandle;
private int mColorHandle;
private int mMVPMatrixHandle;
private final int buffer[] = new int[1];
// number of coordinates per vertex in this array
static final int COORDS_PER_VERTEX = 3;
static float triangleCoords[] = {
// in counterclockwise order:
0.0f, 0.622008459f, 0.0f, // top
-0.5f, -0.311004243f, 0.0f, // bottom left
0.5f, -0.311004243f, 0.0f // bottom right
};
private final int vertexCount = triangleCoords.length / COORDS_PER_VERTEX;
private final int vertexStride = COORDS_PER_VERTEX * 4; // 4 bytes per vertex
float color[] = { 0.63671875f, 0.76953125f, 0.22265625f, 0.0f };
/**
* Sets up the drawing object data for use in an OpenGL ES context.
*/
public Triangle() {
// initialize vertex byte buffer for shape coordinates
ByteBuffer bb = ByteBuffer.allocateDirect(
// (number of coordinate values * 4 bytes per float)
triangleCoords.length * 4);
// use the device hardware's native byte order
bb.order(ByteOrder.nativeOrder());
// create a floating point buffer from the ByteBuffer
vertexBuffer = bb.asFloatBuffer();
// add the coordinates to the FloatBuffer
vertexBuffer.put(triangleCoords);
// set the buffer to read the first coordinate
vertexBuffer.position(0);
// First, generate as many buffers as we need.
// This will give us the OpenGL handles for these buffers.
GLES20.glGenBuffers(1, buffer, 0);
// prepare shaders and OpenGL program
int vertexShader = MyGLRenderer.loadShader(
GLES20.GL_VERTEX_SHADER, vertexShaderCode);
int fragmentShader = MyGLRenderer.loadShader(
GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode);
mProgram = GLES20.glCreateProgram(); // create empty OpenGL Program
GLES20.glAttachShader(mProgram, vertexShader); // add the vertex shader to program
GLES20.glAttachShader(mProgram, fragmentShader); // add the fragment shader to program
GLES20.glLinkProgram(mProgram); // create OpenGL program executables
}
/**
* Encapsulates the OpenGL ES instructions for drawing this shape.
*
* #param mvpMatrix - The Model View Project matrix in which to draw
* this shape.
*/
public void draw(float[] mvpMatrix) {
// get handle to fragment shader's vColor member
mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor");
// Set color for drawing the triangle
GLES20.glUniform4fv(mColorHandle, 1, color, 0);
// get handle to shape's transformation matrix
mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
MyGLRenderer.checkGlError("glGetUniformLocation");
// get handle to vertex shader's vPosition member
mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
//these I don't fully understand
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, buffer[0]);
GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER,vertexBuffer.capacity() * 4,vertexBuffer,GLES20.GL_STATIC_DRAW);
// Add program to OpenGL environment
GLES20.glUseProgram(mProgram);
// Enable a handle to the triangle vertices
GLES20.glEnableVertexAttribArray(mPositionHandle);
// Prepare the triangle coordinate data
GLES20.glVertexAttribPointer(
mPositionHandle, COORDS_PER_VERTEX,
GLES20.GL_FLOAT, false,
vertexStride, 0);
// Apply the projection and view transformation
GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mvpMatrix, 0);
MyGLRenderer.checkGlError("glUniformMatrix4fv");
// Draw the triangle
GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount);
// //is this still necesary? or do i have to use glDeleteBuffers?
// GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
}
}
when I put 0 instead of vertexBuffer inside glVertexAttribPointer() I get an error saying I don't provide the necesarry parameter: expected parameter: ptr: java.nio.Buffer; actual arguments: 0(int)
The transition to the VBO can be a bit strange due to the data pointer usage.
From a quick inspection your main issue is in
GLES20.glVertexAttribPointer(
mPositionHandle, COORDS_PER_VERTEX,
GLES20.GL_FLOAT, false,
vertexStride, vertexBuffer);
as this should be
GLES20.glVertexAttribPointer(
mPositionHandle, COORDS_PER_VERTEX,
GLES20.GL_FLOAT, false,
vertexStride, 0);
So about this buffers: A VBO is a custom buffer on the GPU generally used and optimised to store the vertex data directly to the GPU. The performance you gain by doing so is that the vertex data do not need to be copied to the GPU on every draw call.
These buffers are still custom and on generating them all you need to set is their size. I see you are using factor *4 on the vertex count assuming a float value has a size of 4 bytes, this is not the best idea since that might not always be true. If possible always try to use some form of "sizeOf". Anyway your buffer is created correctly and data are sent to it.
After the data are sent to the VBO you should keep them there until you need them. That means you generally create a single VBO per unique object (a square for instance) and then just hold its ID. Whenever you wish to draw it you just simply bind the buffer and draw as you did. In other words the buffer should never be created in the draw method. What you did there is a memory leak as well since you are responsible for releasing the buffer by calling delete once the buffer is no longer needed.
So about your pointer issue on glVertexAttribPointer: There are 2 ways to use this method. Without the VBO the last parameter is the pointer to the data on your CPU. With the VBO you need to set that as a relative pointer inside the VBO. That means when VBO is bound the beginning of the buffer would be NULL (0), you might even need to typecast that value. For other positions in the buffer you need to manually calculate them.
And the code you posted specifically:
// First, generate as many buffers as we need.
// This will give us the OpenGL handles for these buffers.
final int buffer[] = new int[1];
GLES20.glGenBuffers(1, buffer, 0);
//these I don't fully understand
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, buffer[0]);
GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER,vertexBuffer.capacity() * 4,vertexBuffer,GLES20.GL_STATIC_DRAW);
This all goes into some load time and have a reference to the buffer[] beside that you should add GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0); what this call does is unbind the buffer. This is not something you NEED to do but it is best that you do so you have no confusion what buffer is bound if any.
Before the glVertexAttribPointer is called you need to bind your buffer. Then set the last parameter as described above.
After you are done using this buffer (done drawing) you should (again not necessary) unbind the buffer.

GLES20 Texture Not Working on Some Devices

I have tried to add a fairly simple extension on top of Android's example OpenGL 2.0 project in order to add texturing to basic shapes. This seems pretty straightforward, but on certain devices (Samsung Nexus S, LG Optimus 3D, Samsung Galaxy S) the texture just does not render.
This is actually a problem that I am having on a much larger project, but I was able to reproduce the issue with the simple project below in the hope that someone here has an idea of where my code presents issues, or how to specifically architect GL textures for these devices (maybe there are issues with the devices).
To give an idea of how this object is used: In the GLSurfaceView.Renderer's onSurfaceCreated method I am instantiating a Square() object and in the onDrawFrame method I am calling Square's draw() method. However, all of the relevant code to dealing with textures should appear in this Square class which is almost exactly identical to Google's own example.
Many thanks in advance to anyone who takes a crack at this.
class Square {
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;" +
"attribute vec4 vPosition;" +
"attribute vec2 a_TexCoordinate;" +
"varying vec2 v_TexCoordinate;" +
"void main() {" +
// the matrix must be included as a modifier of gl_Position
" gl_Position = vPosition * uMVPMatrix;" +
" v_TexCoordinate = a_TexCoordinate;" +
"}";
private final String fragmentShaderCode =
"precision mediump float;" +
"uniform sampler2D u_Texture;" +
"varying vec2 v_TexCoordinate;" +
"void main() {" +
" gl_FragColor = texture2D(u_Texture, v_TexCoordinate);" +
"}";
private final FloatBuffer vertexBuffer;
private final FloatBuffer textureBuffer;
private final ShortBuffer drawListBuffer;
private final int mProgram;
private int mPositionHandle;
private int mColorHandle;
private int mMVPMatrixHandle;
// number of coordinates per vertex in this array
static final int COORDS_PER_VERTEX = 3;
static float squareCoords[] = { -0.5f, 0.5f, 0.0f, // top left
-0.5f, -0.5f, 0.0f, // bottom left
0.5f, -0.5f, 0.0f, // bottom right
0.5f, 0.5f, 0.0f }; // top right
final float[] previewTextureCoordinateData =
{
0.0f, 1.0f,
0.0f, 0.0f,
1.0f, 1.0f,
1.0f, 0.0f
};
private int textureDataHandle;
private int textureUniformHandle;
private int textureCoordinateHandle;
private final short drawOrder[] = { 0, 1, 2, 0, 2, 3 }; // order to draw vertices
private final int vertexStride = COORDS_PER_VERTEX * 4; // 4 bytes per vertex
// Set color with red, green, blue and alpha (opacity) values
float color[] = { 0.2f, 0.709803922f, 0.898039216f, 1.0f };
private int loadTexture(final Context context, final int resourceId)
{
final int[] textureHandle = new int[1];
GLES20.glGenTextures(1, textureHandle, 0);
if (textureHandle[0] != 0)
{
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inScaled = false; // No pre-scaling
// Read in the resource
final Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), resourceId, options);
// Bind to the texture in OpenGL
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureHandle[0]);
// Set filtering
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_NEAREST);
// Load the bitmap into the bound texture.
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);
// Recycle the bitmap, since its data has been loaded into OpenGL.
bitmap.recycle();
}
if (textureHandle[0] == 0)
{
throw new RuntimeException("Error loading texture.");
}
return textureHandle[0];
}
public Square(Context context) {
// initialize vertex byte buffer for shape coordinates
ByteBuffer bb = ByteBuffer.allocateDirect(
// (# of coordinate values * 4 bytes per float)
squareCoords.length * 4);
bb.order(ByteOrder.nativeOrder());
vertexBuffer = bb.asFloatBuffer();
vertexBuffer.put(squareCoords);
vertexBuffer.position(0);
// initialize byte buffer for the draw list
ByteBuffer dlb = ByteBuffer.allocateDirect(
// (# of coordinate values * 2 bytes per short)
drawOrder.length * 2);
dlb.order(ByteOrder.nativeOrder());
drawListBuffer = dlb.asShortBuffer();
drawListBuffer.put(drawOrder);
drawListBuffer.position(0);
ByteBuffer texCoordinates = ByteBuffer.allocateDirect(previewTextureCoordinateData.length * 4);
texCoordinates.order(ByteOrder.nativeOrder());
textureBuffer = texCoordinates.asFloatBuffer();
textureBuffer.put(previewTextureCoordinateData);
textureBuffer.position(0);
// prepare shaders and OpenGL program
int vertexShader = MyGLRenderer.loadShader(GLES20.GL_VERTEX_SHADER,
vertexShaderCode);
int fragmentShader = MyGLRenderer.loadShader(GLES20.GL_FRAGMENT_SHADER,
fragmentShaderCode);
textureDataHandle = loadTexture(context, R.drawable.color_texture);
mProgram = GLES20.glCreateProgram(); // create empty OpenGL Program
GLES20.glAttachShader(mProgram, vertexShader); // add the vertex shader to program
GLES20.glAttachShader(mProgram, fragmentShader); // add the fragment shader to program
GLES20.glLinkProgram(mProgram); // create OpenGL program executables
}
public void draw(float[] mvpMatrix) {
// Add program to OpenGL environment
GLES20.glUseProgram(mProgram);
// get handle to vertex shader's vPosition member
mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
// Enable a handle to the triangle vertices
GLES20.glEnableVertexAttribArray(mPositionHandle);
// Prepare the triangle coordinate data
GLES20.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX,
GLES20.GL_FLOAT, false,
vertexStride, vertexBuffer);
textureCoordinateHandle = GLES20.glGetAttribLocation(mProgram, "a_TexCoordinate");
GLES20.glVertexAttribPointer(textureCoordinateHandle, 2, GLES20.GL_FLOAT, false,
0, textureBuffer);
GLES20.glEnableVertexAttribArray(textureCoordinateHandle);
textureUniformHandle = GLES20.glGetUniformLocation(mProgram, "u_Texture");
MyGLRenderer.checkGlError("glGetUniformLocation");
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureDataHandle);
GLES20.glUniform1i(textureUniformHandle, 0);
// get handle to shape's transformation matrix
mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
MyGLRenderer.checkGlError("glGetUniformLocation");
// Apply the projection and view transformation
GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mvpMatrix, 0);
MyGLRenderer.checkGlError("glUniformMatrix4fv");
// Draw the square
GLES20.glDrawElements(GLES20.GL_TRIANGLES, drawOrder.length,
GLES20.GL_UNSIGNED_SHORT, drawListBuffer);
// Disable vertex array
GLES20.glDisableVertexAttribArray(mPositionHandle);
}
}
I'll guess that this is a Power-of-two problem.
By default, the GL_TEXTURE_WRAP setting of glTexParameter is set to GL_REPEAT, and textures that use GL_REPEAT must be power-of-two sized:
Similarly, if the width or height of a texture image are not powers of two and either the
GL_TEXTURE_MIN_FILTER is set to one of the functions that requires mipmaps
or the GL_TEXTURE_WRAP_S or GL_TEXTURE_WRAP_T is not
set to GL_CLAMP_TO_EDGE, then the texture image unit will return
(R, G, B, A) = (0, 0, 0, 1).
You may start with a power-of-two texture, but when you use a BitmapFactory.decodeResource to generate a bitmap, it helpfully(?) scales this based on the density of a device. So for example if you load a 512*512 source texture from drawable folder on a HDPI device, I believe it scales it by 1.5x, so you're left with something that is not Po2.
This gives you the result that your textures don't work on a ton of devices, because those devices are all of a density that causes you to generate illegal texture sizes.
The solution in this case would be to place your (power of 2) source texture into the resource folder drawable-nodpi, which will prevent any density-based scaling. Either that or use CLAMP_TO_EDGE, which doesn't care about Po2.

Categories

Resources