I am trying to make a simple 2D game. I have noticed that the onDrawFrame() function is called in 60 FPS with vsync. However for some reason on older devices if rendering time exceeds 1ms then it will skip a frame every few frame and the animation doesn't look smooth at all. And I am talking about devices that aren't too old...
90% of the drawing code looks like this:
gl.glBindTexture(GL11.GL_TEXTURE_2D, MainProgram.glSurfaceView.renderer.ResourceIdToTexture(R.drawable.tank));
gl.glFrontFace(GL11.GL_CW);
gl.glVertexPointer(3, GL11.GL_FLOAT, 0, vertexBuffer);
gl.glTexCoordPointer(2, GL11.GL_FLOAT, 0, tankTextureBuffer);
float[] glCoords = World.WorldCoordsToGLCoords(tank.GetPosition().PosX(), tank.GetPosition().PosY());
float[] glDims = {0.025f, 0.025f};
gl.glPushMatrix();
gl.glTranslatef(glCoords[0], glCoords[1]+glDims[1], -1.0f);
gl.glScalef(glDims[0], glDims[1], 1.0f);
float[] tc = tank.GetOwner().GetColor().asFloatArray();
gl.glColor4f(tc[0],tc[1],tc[2],tc[3]);
gl.glDrawArrays(GL11.GL_TRIANGLE_STRIP, 0, vertex.length / 3);
gl.glColor4f(1f, 1f, 1f, 1f);
gl.glPopMatrix();
Still, it took 1 millisecond to process all of the drawings to the screen and I have noticed that the FPS (I have measured the number of timer per second the surfaceView.drawFrame is called) drops from 60 to around 30 and it looks almost disgusting.
How do other games have such smooth animations even on the oldest devices?
And how do I achieve this with GLES11?
Related
Following code is drawing two different cube (red/green) on android studio with JNI using opengl-ES. On the virtual device, the result is correct. However, on real device, the result looks like strange.
The structure(means 3d model and its view on 2d) is correct. but the color is different to AVD. In addition it looks like the depth test is not working. What's the problem?
Simply speaking,
AVD : gives correct result (red, green cube with depth test on with specific camera pose)
real device : gives strange result (same camera pose to AVD. but color is different. two green cubes are there. also depth test not working)
float color1[] = {1.0f, 0.0f, 0.0f};
float color2[] = {0.0f, 1.0f, 0.0f};
int mColorHandle1;
int mColorHandle2;
glViewport(0,1280,720,1280);
glEnable(GL_DEPTH_TEST);
glUniform4f(mColorHandle1, color1[0], color1[1], color1[2], 1.0f);
glVertexAttribPointer(gvPositionHandle, 3, GL_FLOAT, GL_FALSE, 0, gTriangleVertices1);
glEnableVertexAttribArray(gvPositionHandle);
glDrawArrays(GL_TRIANGLES, 0, 36);
glUniform4f(mColorHandle2, color2[0], color2[1], color2[2], 1.0f);
glDrawArrays(GL_TRIANGLES, 36, 36);
glDisableVertexAttribArray(gvPositionHandle);
glDisable(GL_DEPTH_TEST);
I am writing a RTS Game for Android and I want the "Fog of War" effect on the player's units. This effect means that only a "circle" around each unit shows the background map while on places where no player unit is located, the screen should be black. I don't want to use shaders.
I have a first version of it working. What I am doing is to render the map to the default framebuffer, then I have a second Framebuffer (similar to light technics) which is completely black. Where the units of the players are, I then batch-draw a texture which is completely transparent and has a white circle with blurred edges in its middle.
Finally I draw the second (light) FrameBuffer's colorTexture over the first one using Gdx.gl.glBlendFunc(GL20.GL_DST_COLOR, GL20.GL_ZERO);
The visual effect now is that indeed the whole map is black and a circle around my units is visible - but a lot of white color is added.
The reason is pretty clear as I drew the light textures for the units like this:
lightBuffer.begin();
Gdx.gl.glBlendFunc(GL20.GL_SRC_ALPHA, GL20.GL_ONE);
Gdx.gl.glEnable(GL20.GL_BLEND);
Gdx.gl.glClearColor(0.1f, 0.1f, 0.1f, 1f);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
batch.setProjectionMatrix(camera.combined);
batch.begin();
batch.setColor(1f, 1f, 1f, 1f);
for (RTSTroopAction i : unitList) {
batch.draw(lightSprite, i.getX() + (i.getWidth() / 2) - 230, i.getY() + (i.getHeight() / 2) - 230, 460, 460); //, 0, 0, lightSprite.getWidth(), lightSprite.getHeight(), false, true);
}
batch.end();
lightBuffer.end();
However, I don't want the "white stuff" on the original texture, I just want the original background shine through. How can I achieve that ?
I think it's playing around with the blendFuncs, but I was not able to figure out which values to use yet.
Thanks to Tenfour04 pointing into the right direction, I was able to find the solution. First of all, the problem is not directly within batch.end();. The problem is, that indeed a sprite batch maintains its own blendFunc Settings. These get applied when flush(); is called. (end() calls it also ).
However the batch is also calling flush when it draws a TextureRegion that is bound to a different texture than the one used in the previous draw() call.
So in my original code: whatever blendFunc I had set was always overridden when I called batch.draw(lightBuffer,...). The solution is to use the spritebatch's blendFunc and not the Gdx.gl.blendFunc.
The total working code finally looks like this:
lightBuffer.begin();
Gdx.gl.glBlendFunc(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA);
Gdx.gl.glEnable(GL20.GL_BLEND);
// start rendering to the lightBuffer
// set the ambient color values, this is the "global" light of your scene
Gdx.gl.glClearColor(0.1f, 0.1f, 0.1f, 1f);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
// start rendering the lights
batch.setProjectionMatrix(camera.combined);
batch.begin();
// set the color of your light (red,green,blue,alpha values)
batch.setColor(1f, 1f, 1f, 1f);
for (RTSTroopAction i : unitList) {
if (i.getOwnerId() == game.getCallback().getPlayerId()) {
batch.draw(lightSprite, i.getX() + (i.getWidth() / 2) - 230, i.getY() + (i.getHeight() / 2) - 230, 460, 460); //, 0, 0, lightSprite.getWidth(), lightSprite.getHeight(), false, true);
}
}
batch.end();
lightBuffer.end();
// now we render the lightBuffer to the default "frame buffer"
// with the right blending !
Gdx.gl.glEnable(GL20.GL_BLEND);
Gdx.gl.glBlendFunc(GL20.GL_ZERO, GL20.GL_SRC_COLOR);
batch.setProjectionMatrix(getStage().getCamera().combined);
batch.enableBlending();
batch.setBlendFunction(GL20.GL_ZERO, GL20.GL_SRC_COLOR);
batch.begin();
Gdx.gl.glEnable(GL20.GL_BLEND);
Gdx.gl.glBlendFunc(GL20.GL_ZERO, GL20.GL_SRC_COLOR);
batch.draw(lightBufferRegion,0, 0, getStage().getWidth(), getStage().getHeight());
batch.end();
batch.setBlendFunction(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA);
I want to create a static starfield in libgdx.
My first way was: create a Decal and a DecalBatch over it.
When I draw the Decal I use a Billboarding technic on the Decal
star.decal.setRotation(camera.direction, camera.up);
next: I wanted to animate the alphas on the decals, so I created on a random way some time:
star.decal.setColor(1, 1, 1, 0.6f+((float) Math.random()*0.4f) );
It is working, but my FPS went down from 55 FPS to 25 FPS (because of my 500-1000 stars)
Can I use only one batch call in any way? Maybe a particleMaterial with only one Vertex list and with a GL_POINT mode that is always face to front of my camera?
How can I do this in libgdx?
The Batch is way to complex than what you need , on every frame it needs to copy all the vertices of the sprites in another array and do calculations on them to find the scale rotation etc..
As you suspect GL_POINT sprites will be way faster and in a medium range device it should be able to render in 60 fps like 2000 points that have different position and color
here is some old code of mine ,its in c and it uses opengl es 1.1 and propably there will be a more simple way to do it in libgdx
glDisableClientState(GL_COLOR_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
glEnable (GL_POINT_SPRITE_OES);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, TXTparticle);
glTexEnvi(GL_POINT_SPRITE_OES, GL_COORD_REPLACE_OES, GL_TRUE);
glPointSize(30);
glColorPointer(4, GL_FLOAT, 32, particlesC);//particlesC the vertices color
glVertexPointer(3, GL_FLOAT, 24, particlesV);//particlesV the vertices
glDrawArrays(GL_POINTS, 0, vertvitLenght/6);
glDisable( GL_POINT_SPRITE_OES );
glDisable(GL_TEXTURE_2D);
glDisableClientState(GL_COLOR_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
I am developing a game where similar objects are rendered on the screen. The problem is that every time a new object is added, more vertices needs to be handled, so FPS decrease.
Since I am a beginner with openGl, I have some general ideas about improving performance, I just don't know what openGl allows me to do.
Since most of my objects are identical(same number of vertices, same colors, same uvs and same normals), but only the position and rotation of the objects are different, is it possible to send the vertices for similar objects only once, on each frame render , and then call drawElements for all objects and modify only the objects positions?
Is it possible to go even further, and upload those similar vertices, only once when the scene is 1st created, and on each frame render just call drawElements for each object?
Currently, I am sending the vertices for each object like this:
.......
gl.glNormalPointer(GL10.GL_FLOAT, 0, normals);
gl.glColorPointer(4, GL10.GL_UNSIGNED_BYTE, 0, colors);
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertices);
gl.glPushMatrix();
gl.glTranslatef(position);
gl.glRotatef(rotation);
gl.glScalef(scale);
gl.glDrawElements(renderType,nr,GL10.GL_UNSIGNED_SHORT, faces);
gl.glPopMatrix();
........
I am thinking I should do someting like this:
.......
gl.glNormalPointer(GL10.GL_FLOAT, 0, normals);
gl.glColorPointer(4, GL10.GL_UNSIGNED_BYTE, 0, colors);
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertices);
for(eachObject)
{
gl.glPushMatrix();
gl.glTranslatef(position);
gl.glRotatef(rotation);
gl.glScalef(scale);
gl.glDrawElements(renderType,nr,GL10.GL_UNSIGNED_SHORT, faces);
gl.glPopMatrix();
........
}
Yes
Yes (even better)
Your second code example looks exactly like what you should be doing.
There's no reason you'd need to upload the vertices more than once, doing it on initialization should be sufficient. After that just set the pointers any time you want to render. You can call glDrawElements as many times as you want with different translations/rotations.
When I run my program on an emulator, it displays an ImageView splash screen, then a black screen for the remainder of the application, which uses GLSurfaceViews. The OGL runs well on my phone. I have tested the program on two computers (low and high performance) and neither displays the GLSurfaceViews. I have also tested the emulator using some of the OGL demos from the Google apidemos interweb site and the demos don't display on either computer. My program uses OGL es 1.1, however I have also tested using OGL es 1.0 to no avail. How might I display ogl on an emulator? Thanks.
Here's an example of some simple square rendering code that doesn't work on the emulator
public void onDrawFrame(GL10 gl) {
//This works
gl.glClearColor(_red, _green, _blue, 1.0f);
gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
//This doesn't
float vertices[] = { .5f, .5f, 0, .5f, -.5f, 0, -.5f, .5f, 0, -.5f, -.5f, 0 };
FloatBuffer vertexSquareBuffer = ByteBuffer.allocateDirect(4 * 3 * 4)
.order(ByteOrder.nativeOrder()).asFloatBuffer();
vertexSquareBuffer.put(vertices);
vertexSquareBuffer.position(0);
gl.glColor4f(1, 1, 0, 0.5f);
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexSquareBuffer);
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0,4);
}
Well there are some possibilities. Firstly lets see if something that should work, does work on your emulator. Can you please go here and try this tutorial and run it through your emulator: http://www.droidnova.com/android-3d-game-tutorial-part-i,312.html (Do this bit first)
That way we know if the problem is with your code or the emulator.
After that you might need to look to see if there are all the required shared object libraries present.
Let me know in the comments how you go.