I have a renderer implementing GLSurfaceView.Renderer interface; a subclass of GLSurfaceView and some classes representing my objects I want to draw. I have the code from http://developer.android.com/training/graphics/opengl/motion.html I want to expand this and add some moving along axes and can't manage it. The object is only rotated. and here is my code:
public class NotMyCoolRenderer implements GLSurfaceView.Renderer {
public GLShip mTriangle;
private GLBackgroundStar mSquare;
private final float[] mMVPMatrix = new float[16];
private final float[] mProjMatrix = new float[16];
private final float[] mVMatrix = new float[16];
private final float[] mModelMatrix = new float[16];
private final float[] tempMatrix = new float[16];
public void onDrawFrame(GL10 unused) {
// Draw background color
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
// Set the camera position (View matrix)
Matrix.setLookAtM(mVMatrix, 0, 0, 0, -3, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
// Calculate the projection and view transformation
Matrix.multiplyMM(mMVPMatrix, 0, mProjMatrix, 0, mVMatrix, 0);
// Draw square
mSquare.draw(mMVPMatrix);
// Now moving on to triangle aka ship
Matrix.setIdentityM(mModelMatrix, 0);
Matrix.translateM(mModelMatrix, 0, 0.1f, 0f, 0);
Matrix.rotateM(mModelMatrix, 0, mTriangle.mAngle, 0, 0, -1.0f);
Matrix.multiplyMM(tempMatrix, 0, mVMatrix, 0, mProjMatrix, 0);
Matrix.multiplyMM(mMVPMatrix, 0, mModelMatrix , 0, tempMatrix , 0);
// Draw triangle
mTriangle.draw(mMVPMatrix);
}
public void onSurfaceChanged(GL10 unused, int width, int height) {
// Adjust the viewport based on geometry changes,
// such as screen rotation
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);
}
public class GLShip {
public volatile float mAngle;
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
" gl_Position = uMVPMatrix * vPosition;" +
"}";
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");
NotMyCoolRenderer.checkGlError("glGetUniformLocation");
// Apply the projection and view transformation
GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mvpMatrix, 0);
NotMyCoolRenderer.checkGlError("glUniformMatrix4fv");
// Draw the triangle
GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount);
// Disable vertex array
GLES20.glDisableVertexAttribArray(mPositionHandle);
}
}
My expectations are that on each redrawing the object would be rotated by mAngle and traslated along Y-Axis by 1f. I can see only rotating (a bit projected too) though. I actually got some questions regarding that: how do I apply my translation matrix and what is the best practice of dividing the opengl functionality? Should not the modelMatrix be stored in the object itself rather than in renderer? Should the matrix operations be performed in the renderer-class? I have grouped them together since I guess that they are all related.
I´ve been working with the example from the Android Training, the following approach finally works for me. (Based on Android Training > Displaying Graphics with OpenGL ES > Adding Motion)
Use the correct vertex shader:
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
" gl_Position = uMVPMatrix * vPosition;" +
"}";
In the renderer class:
public class MyGL20Renderer implements GLSurfaceView.Renderer {
[...]
// create a model matrix for the triangle
private final float[] mModelMatrix = new float[16];
// create a temporary matrix for calculation purposes,
// to avoid the same matrix on the right and left side of multiplyMM later
// see https://stackoverflow.com/questions/13480043/opengl-es-android-matrix-transformations#comment18443759_13480364
private float[] mTempMatrix = new float[16];
[...]
Apply transformations in onDrawFrame, start with translation:
public void onDrawFrame(GL10 unused) {
[...]
Matrix.setIdentityM(mModelMatrix, 0); // initialize to identity matrix
Matrix.translateM(mModelMatrix, 0, -0.5f, 0, 0); // translation to the left
Then rotation:
// Create a rotation transformation for the triangle
long time = SystemClock.uptimeMillis() % 4000L;
float mAngle = 0.090f * ((int) time);
Matrix.setRotateM(mRotationMatrix, 0, mAngle, 0, 0, -1.0f);
Combine rotation and translation, avoid using mModelMatrix
"as the same matrix on the right and left side of multiplyMM" (see 2)
// Combine Rotation and Translation matrices
mTempMatrix = mModelMatrix.clone();
Matrix.multiplyMM(mModelMatrix, 0, mTempMatrix, 0, mRotationMatrix, 0);
Combine the model matrix with the projection and camera view; again avoid using mModelMatrix
"as the same matrix on the right and left side of multiplyMM" (see 2)
// Combine the model matrix with the projection and camera view
mTempMatrix = mMVPMatrix.clone();
Matrix.multiplyMM(mMVPMatrix, 0, mTempMatrix, 0, mModelMatrix, 0);
Draw the shape
// Draw shape
mTriangle.draw(mMVPMatrix);
Thank you all, for all the useful input I could draw from this thread.
If you want to see movement, you should update mTriangle.mAngle each frame (preferably as a function of time to combat speed differences or delays caused by other processes...).
Note that Matrix.setIdentityM(mModelMatrix, 0); restores all the accumulated rotations and translations to "zero" or actually to Identity Matrix... The same convention applies btw to all set functions. To accumulate all the transformations, one has to
Also one should keep the values of object translation vector [ox,oy,oz] between each call and feed them to Matrix.translateM(mModelMatrix, ox, oy, oz, 0);
Typically one concatenates all 'translate, rotate, scale', etc. matrices as early as possible and cache them per object, or hierarchically per a complex container having multiple objects and having a bounding box, so that multiple objects can be culled when on behind the camera (or generally outside the viewing frustum).
Also typically one keeps a moving camera in one matrix and per frame multiplies it with the projection matrix.
You can start with something like:
float Time = System.currentTimeMillis() * 0.01f; // 10 radians / second == fast!
Matrix.translateM(mModelMatrix, Math.sin(Time)*2.0f, 0, 1f, 0);
...
As Tim noticed, there is no projection matrix involved, meaning that all z-values behave in this code exactly, even though changing x & y values would make a difference.
I'd be tempted to say, that MVP matrix would mean multiplying in order M * V * P = (M*V) * P = M * (V*P).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With