Related
I'm trying to learn OpenGL ES 2.0 and I went to load 3d models on Android. I can now load properly with the model texture, but I have a problem on the display depth. When I place my model in perspective, and part of the model is hidden by another part of it, it happens to me that a triangle or two before another draw and this is what I see through some parts .
I try setEGLConfigChooser (8, 8, 8, 8, 16, 0); and (8, 8, 8, 8, 24, 0), but my problem remains the same, except that when I put (8, 8, 8, 8, 24, 0) and display a little better defined, but when the 3d object moves, the colors make a strobe effect that is disturbing to me.
I also try glDepthFunc function (GL_LEQUAL); with glEnable (GL_DEPTH_TEST), but this does not rule over my problem.
Here's the pictures of the probleme:
The probleme : Link is broken
The good : Link is broken
Sorry for my link picture, I do not have more than 10 reputation to post picture in the question.
Here my code
My GLSurfaceView
public MyGLSurfaceView(Context context) {
super(context);
this.context = context;
setEGLContextClientVersion(2);
setEGLConfigChooser(true);
//setZOrderOnTop(true);
//setEGLConfigChooser(8, 8, 8, 8, 16, 0);
//setEGLConfigChooser(8, 8, 8, 8, 24, 0);
//getHolder().setFormat(PixelFormat.RGBA_8888);
mRenderer = new Renderer(context);
setRenderer(mRenderer);
}
My renderer
#Override
public void onSurfaceCreated(GL10 glUnused, EGLConfig config) {
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);
glFrontFace(GL_CCW);
glEnable(GL_DEPTH_TEST);
mushroom = new Mushroom();
textureProgram = new TextureShaderProgram(context);
texture = TextureHelper.loadTexture(context, R.drawable.mushroom);
}
#Override
public void onSurfaceChanged(GL10 glUnused, int width, int height) {
glViewport(0, 0, width, height);
MatrixHelper.perspectiveM(projectionMatrix, 45, (float) width
/ (float) height, 0f, 10f);
setLookAtM(viewMatrix, 0, 0f, 1.2f, -10.2f, 0f, 0f, 0f, 0f, 1f, 0f);
}
#Override
public void onDrawFrame(GL10 glUnused) {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
multiplyMM(viewProjectionMatrix, 0, projectionMatrix, 0, viewMatrix, 0);
glDepthFunc(GL_LEQUAL);
//glDepthMask(true);
positionMushroomInScene();
textureProgram.useProgram();
textureProgram.setUniforms(modelViewProjectionMatrix, texture);
mushroom.bindData(textureProgram);
mushroom.draw();
//glDepthFunc(GL_LESS);
}
private void positionMushroomInScene() {
setIdentityM(modelMatrix, 0);
translateM(modelMatrix, 0, 0f, 0f, 5f);
rotateM(modelMatrix, 0, -yRotation, 1f, 0f, 0f);
rotateM(modelMatrix, 0, xRotation, 0f, 1f, 0f);
multiplyMM(modelViewProjectionMatrix, 0, viewProjectionMatrix,
0, modelMatrix, 0);
}
My matrix Helper
public static void perspectiveM(float[] m, float yFovInDegrees, float aspect, float n, float f) {
final float angleInRadians = (float) (yFovInDegrees * Math.PI / 180.0);
final float a = (float) (1.0 / Math.tan(angleInRadians / 2.0));
m[0] = a / aspect;
m[1] = 0f;
m[2] = 0f;
m[3] = 0f;
m[4] = 0f;
m[5] = a;
m[6] = 0f;
m[7] = 0f;
m[8] = 0f;
m[9] = 0f;
m[10] = -((f + n) / (f - n));
m[11] = -1f;
m[12] = 0f;
m[13] = 0f;
m[14] = -((2f * f * n) / (f - n));
m[15] = 0f;
}
The problem is most likely with the way you set up your projection matrix:
MatrixHelper.perspectiveM(projectionMatrix, 45, (float) width
/ (float) height, 0f, 10f);
The 4th argument in your definition of this function is the near plane. This value should never be 0.0. It should typically be a reasonable fraction of the far distance. Choosing the ideal value can be somewhat of a tradeoff. The larger far / near is, the less depth precision you get. On the other hand, if you set the near value too large, you risk clipping off close geometry that you actually wanted to see.
A ratio of maybe 100 or 1000 for far / near should normally give you reasonable depth precision, without undesirable front clipping. You'll need to be a little more conservative with the ratio if you use a 16-bit depth buffer than if you have a 24-bit depth buffer.
For your purpose, try changing near to 0.1, and see how that works for you:
MatrixHelper.perspectiveM(projectionMatrix, 45, (float) width
/ (float) height, 0.1f, 10f);
I'm trying to implement color picking using GLES20.glReadPixels function in android OpenGL ES. The problem is that this function is always returning 0,0,0,0 as color and not the correct color values. Any idea why? My code looks like this:
public boolean onTouchEvent(MotionEvent event)
{
if (event != null)
{
float x = event.getX();
float y = event.getY();
if (event.getAction() == MotionEvent.ACTION_UP)
{
int newX = (int)x;
int newY = (int)y;
ByteBuffer pixel = ByteBuffer.allocate(4);
pixel.order(ByteOrder.nativeOrder());
pixel.position(0);
GLES20.glReadPixels(newX, (int)mRenderer.viewport[3] - newY, 1, 1,
GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, pixel);
}
return true;
}
else
{
return super.onTouchEvent(event);
}
So as I said before... the result of the pixel array is always 0,0,0,0. Dont know why :/ What am I doing wrong? I was using the lighthouse tutorial as a reference:
http://www.lighthouse3d.com/opengl/picking/index.php3?color2
And I really can't see the mistake at this point :/
Oh I forgot to tell that my scene contains a 3D cube which is fully BLUE so the result should be something like 0,0,1,0 when I click on it but it isn't :(
EDIT:
The code from the Renderer where the Cube is drawn (it rotates arround its y-axis)
public void onDrawFrame(GL10 unused) {
float[] scratch = new float[16];
GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);
GLES20.glEnable(GLES20.GL_DEPTH_TEST);
Matrix.setLookAtM(mViewMatrix, 0, 0, 0, -3.0f, 0f, -0.3f, 0.0f, 0.0f, 1.0f, 0.0f);
Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mViewMatrix, 0);
float[] mModelMatrix = new float[16];
Matrix.setIdentityM(mModelMatrix, 0);
Matrix.translateM(mModelMatrix, 0, 0, 0, -0.5f);
Matrix.setIdentityM(mRotationMatrix, 0);
Matrix.rotateM(mRotationMatrix, 0, mDeltaX, 0, 1.0f, 0);
Matrix.rotateM(mRotationMatrix, 0, -mDeltaY, 1.0f, 0, 0);
mDeltaX = 0.2f;
mDeltaY = 0.2f;
float[] mTempMatrix = new float[16];
Matrix.multiplyMM(mTempMatrix, 0, mRotationMatrix, 0, mAccumulatedRotation, 0);
System.arraycopy(mTempMatrix, 0, mAccumulatedRotation, 0, 16);
float[] temp = new float[16];
Matrix.multiplyMM(temp, 0, mModelMatrix, 0 , mAccumulatedRotation, 0);
Matrix.multiplyMM(scratch, 0, mMVPMatrix, 0, temp, 0);
Matrix.setIdentityM(mModelMatrix, 0);
Matrix.translateM(mModelMatrix, 0, 0, 0, 0.5f);
float[] temp2 = new float[16];
Matrix.multiplyMM(temp2, 0, scratch, 0, mModelMatrix, 0);
mCube.drawCube(temp2);
}
Like all other OpenGL calls, glReadPixels() only works if there is a current OpenGL context.
In Android, OpenGL rendering is mostly done using a GLSurfaceView, which takes care of spawning a secondary thread for rendering, creating an OpenGL context, and making that context current in the secondary rendering thread while invoking the methods in your GLSurfaceView.Renderer implementation.
onTouchEvent() is invoked in the UI thread, so you won't have a current OpenGL context here. To use glReadPixels(), you can forward the request to your rendering thread using the GLSurfaceView.queueEvent() method, and then process it asynchronously the next time your Renderer.onDraw() method is invoked.
I have a project featuring a 3d object. The user can rotate and zoom it in/out with finger gestures. The problem I have is while rotating a cube (other solids also cause the same error, but I'll set an example on cube), the edges of it kind of disintegrate (as shown in screenshots). The faster the rotation is the more substantial the problem becomes.
I tried to mask it by disable face culling, so that the user sees the inside of the cube, which basically has the same colour. However the project manager isn't pleased with that solution (I admitt it doesn't work that well).
The cube has 6 faces, each containing about 242 polygons.
Here are the code snippets relevant to rendering the 3d sceene (I'm using VBO):
public void onDrawFrame(GL10 glUnused)
{
GLES20.glDisable(GLES20.GL_BLEND);
isBlendingEnabled = false;
Matrix.setLookAtM(mViewMatrix, 0, mEyeX, mEyeY, mEyeZ, mLookX, mLookY, mLookZ, mUpX, mUpY, mUpZ);
if(!mSolid.equals(((ActivityMain) mContext).getSolid())){
refreshSolid();
refreshTextureData();
}
if(mSolid.hasChanged){
refreshSolid();
}
if(mSolid.hasTexturesChanged){
refreshTextureData();
mSolid.hasTexturesChanged = false;
}
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
// Set per-vertex lighting program.
GLES20.glUseProgram(mProgramHandle);
// Set program handles for solid drawing.
mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgramHandle, "u_MVPMatrix");
mMVMatrixHandle = GLES20.glGetUniformLocation(mProgramHandle, "u_MVMatrix");
mLightPosHandle = GLES20.glGetUniformLocation(mProgramHandle, "u_LightPos");
mColorHandle = GLES20.glGetAttribLocation(mProgramHandle, "a_Color");
mTextureUniformHandle = GLES20.glGetUniformLocation(mProgramHandle, "u_Texture");
mPositionHandle = GLES20.glGetAttribLocation(mProgramHandle, "a_Position");
mNormalHandle = GLES20.glGetAttribLocation(mProgramHandle, "a_Normal");
mTextureCoordinateHandle = GLES20.glGetAttribLocation(mProgramHandle, "a_TexCoordinate");
// Calculate position of the light. Handle it's rotation
Matrix.setIdentityM(mLightModelMatrix, 0);
Matrix.translateM(mLightModelMatrix, 0, 0.0f, 0.0f, -2.0f);
Matrix.rotateM(mLightModelMatrix, 0, mAngleInDegrees, 0.0f, 1.0f, 0.0f);
Matrix.translateM(mLightModelMatrix, 0, 0.0f, 0.0f, 3.5f);
Matrix.multiplyMV(mLightPosInWorldSpace, 0, mLightModelMatrix, 0, mLightPosInModelSpace, 0);
Matrix.multiplyMV(mLightPosInEyeSpace, 0, mViewMatrix, 0, mLightPosInWorldSpace, 0);
//Doing that step for each of the faces
for(int i = 0; i < mSolid.get3dMesh().size(); i++){
// Draw a solid.
// Translate the solid into the screen.
Matrix.setIdentityM(mModelMatrix, 0);
Matrix.translateM(mModelMatrix, 0, 0.0f, 0.0f, -3.5f);
// Set a matrix that contains the current rotation.
Matrix.setIdentityM(mCurrentRotation, 0);
Matrix.rotateM(mCurrentRotation, 0, mDeltaX, 0.0f, 1.0f, 0.0f);
Matrix.rotateM(mCurrentRotation, 0, mDeltaY, 1.0f, 0.0f, 0.0f);
//handle inertia
if(Math.abs(mDeltaX) > 2f){
mDeltaX = Math.signum(mDeltaX);
mDeltaX = 0.5f*mDeltaX;
}else if(Math.abs(mDeltaX) > 0.05f){
mDeltaX = 0.99f*mDeltaX;
}else
mDeltaX = 0.0f;
if(Math.abs(mDeltaY) > 2f){
mDeltaY = Math.signum(mDeltaY);
mDeltaY = 0.5f*mDeltaY;
}else if(Math.abs(mDeltaY) > 0.05f){
mDeltaY = 0.99f*mDeltaY;
}else
mDeltaY = 0.0f;
// Multiply the current rotation by the accumulated rotation, and then set the accumulated rotation to the result.
Matrix.multiplyMM(mTemporaryMatrix, 0, mCurrentRotation, 0, mAccumulatedRotation, 0);
System.arraycopy(mTemporaryMatrix, 0, mAccumulatedRotation, 0, 16);
// Rotate the cube taking the overall rotation into account.
Matrix.multiplyMM(mTemporaryMatrix, 0, mModelMatrix, 0, mAccumulatedRotation, 0);
System.arraycopy(mTemporaryMatrix, 0, mModelMatrix, 0, 16);
// Set the active texture unit to texture unit 0.
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
// Bind the texture to this unit.
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureDataHandle[i]);
// Tell the texture uniform sampler to use this texture in the shader by binding to texture unit 0.
GLES20.glUniform1i(mTextureUniformHandle, 0);
drawCube(i);
}
if(ModelCorePrefs.getDrawMesh())
drawVertices();
GLES20.glEnable(GLES20.GL_BLEND);
GLES20.glBlendFunc (GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA);
isBlendingEnabled = true;
Matrix.setIdentityM(mModelMatrix, 0);
Matrix.translateM(mModelMatrix, 0, 0.0f, -3.5f, -3.5f);
Matrix.scaleM(mModelMatrix, 0, 2f, 1.0f, 2f);
// Set the active texture unit to texture unit 0.
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
// Bind the texture to this unit.
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureDataHandle[mSolid.get3dMesh().size()]);
// Tell the texture uniform sampler to use this texture in the shader by binding to texture unit 0.
GLES20.glUniform1i(mTextureUniformHandle, 0);
drawShadow();
}
The drawCube() method used above:
private void drawCube(int i)
{
// Pass in the position information
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mCubePositionsBufferIdx[i]);
GLES20.glEnableVertexAttribArray(mPositionHandle);
GLES20.glVertexAttribPointer(mPositionHandle, mPositionDataSize, GLES20.GL_FLOAT, false, 0, 0);
// Pass in the color information
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mCubeColorsBufferIdx[i]);
GLES20.glEnableVertexAttribArray(mColorHandle);
GLES20.glVertexAttribPointer(mColorHandle, mColorDataSize, GLES20.GL_FLOAT, false, 0, 0);
// Pass in the normal information
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mCubeNormalsBufferIdx[i]);
GLES20.glEnableVertexAttribArray(mNormalHandle);
GLES20.glVertexAttribPointer(mNormalHandle, mNormalDataSize, GLES20.GL_FLOAT, false, 0, 0);
// Pass in the texture coordinate information
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mCubeTexCoordsBufferIdx[i]);
GLES20.glEnableVertexAttribArray(mTextureCoordinateHandle);
GLES20.glVertexAttribPointer(mTextureCoordinateHandle, mTextureCoordinateDataSize, GLES20.GL_FLOAT, false, 0, 0);
// This multiplies the view matrix by the model matrix, and stores the result in the MVP matrix
// (which currently contains model * view).
Matrix.multiplyMM(mMVPMatrix, 0, mViewMatrix, 0, mModelMatrix, 0);
// Pass in the modelview matrix.
GLES20.glUniformMatrix4fv(mMVMatrixHandle, 1, false, mMVPMatrix, 0);
// This multiplies the modelview matrix by the projection matrix, and stores the result in the MVP matrix
// (which now contains model * view * projection).
Matrix.multiplyMM(mTemporaryMatrix, 0, mProjectionMatrix, 0, mMVPMatrix, 0);
System.arraycopy(mTemporaryMatrix, 0, mMVPMatrix, 0, 16);
// Pass in the combined matrix.
GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mMVPMatrix, 0);
// Pass in the light position in eye space.
GLES20.glUniform3f(mLightPosHandle, mLightPosInEyeSpace[0], mLightPosInEyeSpace[1], mLightPosInEyeSpace[2]);
// Draw the cube.
GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, mSolid.get3dMesh().get(i).length/3);
}
And the screenshot of the problem:
I would strongly suggest you remove the glGet*Location (...) calls from your onDrawFrame (...) method. Those things do not change after you link your program initially, and searching for those locations by string name each frame is going to hinder your performance. Likewise, you only have to set the sampler uniform once per-program since it is always using texture image unit 0.
As for your real issue:
I see no evidence that each face in your loop has its own unique model matrix, so you should move the logic that incrementally transforms the matrix out of the loop. Otherwise you are going to rotate each face in the cube individually, which is what your diagram is showing. To be honest, I do not know why you even have to draw the faces one at a time.
// Tell the texture uniform sampler to use this texture in the shader by binding to texture unit 0.
GLES20.glUniform1i(mTextureUniformHandle, 0);
// ^^^^^ ONLY DO THAT ONCE!
// Translate the solid into the screen.
Matrix.setIdentityM(mModelMatrix, 0);
Matrix.translateM(mModelMatrix, 0, 0.0f, 0.0f, -3.5f);
// Set a matrix that contains the current rotation.
Matrix.setIdentityM(mCurrentRotation, 0);
Matrix.rotateM(mCurrentRotation, 0, mDeltaX, 0.0f, 1.0f, 0.0f);
Matrix.rotateM(mCurrentRotation, 0, mDeltaY, 1.0f, 0.0f, 0.0f);
//handle inertia
if(Math.abs(mDeltaX) > 2f){
mDeltaX = Math.signum(mDeltaX);
mDeltaX = 0.5f*mDeltaX;
}else if(Math.abs(mDeltaX) > 0.05f){
mDeltaX = 0.99f*mDeltaX;
}else
mDeltaX = 0.0f;
if(Math.abs(mDeltaY) > 2f){
mDeltaY = Math.signum(mDeltaY);
mDeltaY = 0.5f*mDeltaY;
}else if(Math.abs(mDeltaY) > 0.05f){
mDeltaY = 0.99f*mDeltaY;
}else
mDeltaY = 0.0f;
// Multiply the current rotation by the accumulated rotation, and then set the accumulated rotation to the result.
Matrix.multiplyMM(mTemporaryMatrix, 0, mCurrentRotation, 0, mAccumulatedRotation, 0);
System.arraycopy(mTemporaryMatrix, 0, mAccumulatedRotation, 0, 16);
// Rotate the cube taking the overall rotation into account.
Matrix.multiplyMM(mTemporaryMatrix, 0, mModelMatrix, 0, mAccumulatedRotation, 0);
System.arraycopy(mTemporaryMatrix, 0, mModelMatrix, 0, 16);
//Doing that step for each of the faces
for(int i = 0; i < mSolid.get3dMesh().size(); i++){
// Draw a solid.
// Set the active texture unit to texture unit 0.
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
// Bind the texture to this unit.
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureDataHandle[i]);
drawCube(i);
}
I have cube that rotates around the center of the coordinates system. But the problem is it rotates very slowly. So in my case how to set the rotation speed?
The following three methods update the mCurrentModelMatrix with the given model transformation. These are stateful accumulative methods.
public void trnslate(float x, float y, float z)
{
float[] tempModelMatrix = new float[16];
Matrix.setIdentityM(tempModelMatrix, 0);
Matrix.translateM(tempModelMatrix,0,x,y,z);
Matrix.multiplyMM(this.mCurrentModelMatrix, 0,
tempModelMatrix, 0, this.mCurrentModelMatrix, 0);
}
public void rotate(float angle, float x, float y, float z)
{
float[] tempModelMatrix = new float[16];
Matrix.setIdentityM(tempModelMatrix, 0);
Matrix.rotateM(tempModelMatrix,0,angle,x,y,z);
Matrix.multiplyMM(this.mCurrentModelMatrix, 0,
tempModelMatrix, 0, this.mCurrentModelMatrix, 0);
}
public void scale(float xFactor, float yFactor, float zFactor)
{
float[] tempModelMatrix = new float[16];
Matrix.setIdentityM(tempModelMatrix, 0);
Matrix.scaleM(tempModelMatrix,0,xFactor,yFactor,zFactor);
Matrix.multiplyMM(this.mCurrentModelMatrix, 0,
tempModelMatrix, 0, this.mCurrentModelMatrix, 0);
}
/*
* Calculaute the final model view matrix
* 1. Order of matrix multiplication is important
* 2. MVPmatrix = proj * view * model;
* 3. Setup the MVP matrix in the vertex shader memory
*/
protected void setupMatrices()
{
float[] tempModelMatrix = new float[16];
Matrix.setIdentityM(tempModelMatrix, 0);
//translate the model combo next
Matrix.multiplyMM(mMVPMatrix, 0, //matrix and offset
mCurrentModelMatrix, 0,
tempModelMatrix, 0);
//translate eye coordinates first
Matrix.multiplyMM(mMVPMatrix, 0,
this.mVMatrix, 0,
mMVPMatrix, 0);
//Project it: screen coordinates
Matrix.multiplyMM(mMVPMatrix, 0,
mProjMatrix, 0,
mMVPMatrix, 0);
//Set the vertex uniform handler representing the MVP matrix
GLES20.glUniformMatrix4fv(muMVPMatrixHandle, //uniform handle
1, //number of uniforms. 1 if it is not an array
false, //transpose: must be false
mMVPMatrix, //client matrix memory pointer
0); //offset
}
draw method
// Drawing operation
#Override
protected void draw(GL10 gl, int positionHandle) {
// Hide the hidden surfaces using these APIs
GLES20.glEnable(GLES20.GL_DEPTH_TEST);
GLES20.glDepthFunc(GLES20.GL_LESS);
// Transfer vertices to the shader
transferVertexPoints(positionHandle);
// Transfer texture points to the shader
transferTexturePoints(getTextureHandle());
// Implement rotation from 0 to 360 degrees
// Stop when asked and restart when the stopFlag
// is set to false.
// Decide what the current angle to apply
// for rotation is.
if (stopFlag == true) {
// stop rotation
curAngle = stoppedAtAngle;
} else {
curAngle += 1.0f;
}
if (curAngle > 360) {
curAngle = 0;
}
// Tell the base class to start their
// matrices to unit matrices.
this.initializeMatrices();
// The order of these model transformations matter
// Each model transformation is specified with
// respect to the last one, and not the very first.
// Center the cube
this.trnslate(0, 0, -1);
// Rotate it around y axis
this.rotate(curAngle, 0, -1, 0);
// Decenter it to where ever you want
this.trnslate(0, -2, 2);
// Go ahead calculate the ModelViewMatrix as
// we are done with ALL of our model transformations
this.setupMatrices();
// Call glDrawArrays to use the vertices and draw
int vertexCount = mTriangleVerticesData.length / 3;
GLES20.glDrawArrays(GLES20.GL_TRIANGLES, // what primitives to use
0, // at what point to start
vertexCount); // Starting there how many points to use
// Check if there are errors
checkGlError("glDrawArrays");
}
Thanks in advance!
You are rotating at 1 degree per frame, so it will take 360 frames to do a complete rotation.
If you want it to rotate in 2 seconds, and you were running at 30 frames per second, you would want to rotate by 6 degrees per frame, by changing this section:
if (stopFlag == true) {
// stop rotation
curAngle = stoppedAtAngle;
} else {
curAngle += 6.0f;
}
if (curAngle > 360) {
curAngle = 0;
}
Because of performance I moved to OpenGL ES 2D from canvas.drawBitmap
This is sprite sheet 4x1:
Now to make it work I had followed class:
public Vulcan(ScreenObjectsView objectsView, int vulkanSpriteID, Context context) {
this.b = BitmapFactory.decodeResource(context.getResources(), vulkanSpriteID);
// 1x4
height = b.getHeight();
width = b.getWidth()/4;
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
x = display.getWidth()/2-width/2; // deprecated
y = display.getHeight()-height; // deprecated
}
public void update() {
frameFreq++;
if(frameFreq > 0){
currentFrame = ++currentFrame % 4;
frameFreq = 0;
}
}
#Override
public void draw(Canvas canvas) {
update();
int srcX = currentFrame * width;
Rect src = new Rect(srcX, 0, srcX+width, height);
Rect dst = new Rect(x, y, x+width, y+height);
canvas.drawBitmap(b, src, dst, null);
}
Each period of time I take Rect and shift from left to right (in loop):
currentFrame = ++currentFrame % 4;
So far so good.
How can I animate above mentioned sprite sheet in in OpenGL ES?
Today, I know how to draw and move objects in OpenGL ES (thanks to good demo)
but don't know to play with sprites.
Any ideas, links, snippets of code?
[Edit]
Ther is no mater to use sprite sheet or 4 images like:
, and so on.
Strange that still didn't get any answer or direction.
Thank you,
[Edit 2]
According to what Aert says I implemented the following code and it works.
But it seems messy
Too much code for OpenGL ES. For each texture (I have 4), I need create FloatBuffer:
Maybe someone have shorter way, or I did something wrong.
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import javax.microedition.khronos.opengles.GL10;
public class DevQuestSpriteBase {
private static final String LOG_TAG = "Fess";//DevQuestSpriteBase.class.getSimpleName();
protected int mFrame = 0;
protected int mSwitcher = 0;
private int textureCount = 1; // frame animation
protected int[] textures = new int[textureCount]; // frame animation
// texture and verts
protected FloatBuffer vertexBuffer,
textureBuffer1,
textureBuffer2,
textureBuffer3,
textureBuffer4;
ByteBuffer bb1;
protected float vertices[] = {
0f,0f,0.0f,
1f,0f,0.0f,
0f,1f,0.0f,
1f,1f,0.0f
};
/** 1 frame */
protected float texture1[] = {
0.0f, 1.0f,
0.0f, 0.0f,
0.25f, 1.0f,
0.25f, 0.0f
};
/** 2 frame */
protected float texture2[] = {
0.25f, 1.0f,
0.25f, 0.0f,
0.5f, 1.0f,
0.5f, 0.0f
};
/** 3 frame */
protected float texture3[] = {
0.5f, 1.0f,
0.5f, 0.0f,
0.75f, 1.0f,
0.75f, 0.0f
};
/** 4 frame */
protected float texture4[] = {
0.75f, 1.0f,
0.75f, 0.0f,
1.0f, 1.0f,
1.0f, 0.0f
};
public DevQuestSpriteBase(){
// vertices buffer
bb1 = ByteBuffer.allocateDirect(vertices.length * 4);
bb1.order(ByteOrder.nativeOrder());
vertexBuffer = bb1.asFloatBuffer();
vertexBuffer.put(vertices);
vertexBuffer.position(0);
// texture buffer
bb1 = ByteBuffer.allocateDirect(texture1.length * 4);
bb1.order(ByteOrder.nativeOrder());
textureBuffer1 = bb1.asFloatBuffer();
textureBuffer1.put(texture1);
textureBuffer1.position(0);
//#########################################################
// texture buffer
bb1 = ByteBuffer.allocateDirect(texture2.length * 4);
bb1.order(ByteOrder.nativeOrder());
textureBuffer2 = bb1.asFloatBuffer();
textureBuffer2.put(texture2);
textureBuffer2.position(0);
//#########################################################
// texture buffer
bb1 = ByteBuffer.allocateDirect(texture3.length * 4);
bb1.order(ByteOrder.nativeOrder());
textureBuffer3 = bb1.asFloatBuffer();
textureBuffer3.put(texture3);
textureBuffer3.position(0);
//#########################################################
// texture buffer
bb1 = ByteBuffer.allocateDirect(texture4.length * 4);
bb1.order(ByteOrder.nativeOrder());
textureBuffer4 = bb1.asFloatBuffer();
textureBuffer4.put(texture4);
textureBuffer4.position(0);
}
private void update() {
if(mSwitcher == 5){
mFrame = ++mFrame % 4;
mSwitcher = 0;
// Log.e(LOG_TAG, "DevQuestSpriteBase :: " + mFrame);
}
else{
mSwitcher++;
}
}
public void draw(GL10 gl){
gl.glBindTexture(GL10.GL_TEXTURE_2D, textures[0]);
gl.glBindTexture(GL10.GL_TEXTURE_2D, textures[0]);
if(mFrame == 0){
gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, textureBuffer1);
}
else if(mFrame == 1){
gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, textureBuffer2);
}
else if(mFrame == 2){
gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, textureBuffer3);
}
else if(mFrame == 3){
gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, textureBuffer4);
}
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 4);
//Log.e(LOG_TAG, "DevQuestSpriteBase :: draw");
update();
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer);
//gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, textureBuffer1);
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, vertices.length / 3);
gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
}
public int[] getTextures() {
return textures;
}
}
Without going into a lot of detail, you need to do the following (assuming you are already drawing a sprite using 4 vertices):
Define the texture coordinates corresponding to the vertices of the sprite for each animation frame, e.g.
texCoordsFrame1 = [0.0f, 0.0f, 0.25f, 0.0f, 0.0f, 1.0f, 0.25f, 1.0f];
Upload the spritesheet texture, e.g.
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, imageData);
Draw using the texture coordinates corresponding to the frame you want to show when required, e.g.
...
glBindTexture(GL_TEXTURE_2D, texture[0]);
glTexCoordPointer(2, GL_FLOAT, 0, texCoordsFrame1);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
Alternatively, you can upload the separate frames as individual textures, but that is undesirable from a performance point of view.
There are a few gotcha's
When using GLES1, you can only use power-of-two textures. In that case you'll have to scale the texture or increase its size to be power-of-two and adjust the texture coordinates.
The bitmap vs GL y-coordinate direction difference is a bit confusing, and you might end up with a vertically flipped sprite.