I want to rotate an element around a specific point defined by me and dynamically changed.
I am orientating myself at the guidelines from the google developers site.
My first approach is this:
scratch = new float[16];
Matrix.setIdentityM(mRotationMatrix, 0);
Matrix.setRotateM(mRotationMatrix, 0, angle, 0, 0, 1f);
Matrix.multiplyMM(scratch, 0, mMVPMatrix, 0, mRotationMatrix, 0);
element.draw(scratch);
This rotates the object around the center of the screen.
What do I have to add/change to make the object rotate around some other point?
Add a translation operation.
Grafika's Sprite2d class provides an example:
/**
* Re-computes mModelViewMatrix, based on the current values for rotation, scale, and
* translation.
*/
private void recomputeMatrix() {
float[] modelView = mModelViewMatrix;
Matrix.setIdentityM(modelView, 0);
Matrix.translateM(modelView, 0, mPosX, mPosY, 0.0f);
if (mAngle != 0.0f) {
Matrix.rotateM(modelView, 0, mAngle, 0.0f, 0.0f, 1.0f);
}
Matrix.scaleM(modelView, 0, mScaleX, mScaleY, 1.0f);
mMatrixReady = true;
}
This positions the object, then rotates it around the center of the object.
You need to translate the matrix in the reverse direction of the point first, then rotate and then translate it back. Look at it as if the rotation is always rotating around the center of the world, and the translation moves the center of the world.
Something like this (untested):
scratch = new float[16];
Matrix.setIdentityM(mRotationMatrix, 0);
Matrix.translateM(mRotationMatrix, 0, -x, -y, -z);
Matrix.rotateM(mRotationMatrix, 0, angle, 0, 0, 1f);
Matrix.translateM(mRotationMatrix, 0, x, y, z);
Matrix.multiplyMM(scratch, 0, mMVPMatrix, 0, mRotationMatrix, 0);
element.draw(scratch);
The x, y and z values need to be calculated as the delta between the current position of the object in the world and the position of the point you want to rotate around. You need to do that calculation yourself, but that's pretty trivial.
Related
Hi I am working on a AR android app. I am using ARToolkit6. In this app I want to view my 3D object( A Cube) on left half of the screen. With this eventually I want to display 3 cubes on the screen each on 1/3 of the screen area.
I was able to scale the 3D object by tweaking ModelView Matrix. What I read so far, I think I need to tweak projection matrix to achieve my goal. I tried looking solutions online. But Couldn't get it to work. Can anyone direct me to right path?
for (int trackableUID : trackableUIDs) {
// If the marker is visible, apply its transformation, and render a cube
if (ARToolKit.getInstance().queryMarkerVisible(trackableUID)) {
float[] projectionMatrix = ARToolKit.getInstance().getProjectionMatrix();
float[] modelViewMatrix = ARToolKit.getInstance().queryMarkerTransformation(trackableUID);
float[] scalingMat = {1, 0, 0, 0, 0, 3.0f, 0, 0, 0, 0, 1.0f, 0, 0.0f, 0, 0, 1};
float[] newModelView = modelViewMatrix;
multiplyMM(newModelView, 0, modelViewMatrix, 0, scalingMat, 0);
cube.draw(projectionMatrix, newModelView);
}
I followed the this link Set origin to top-left corner of screen in OpenGL ES 2 and (OpenGL ES) Objects away from view centre are stretched. So I translated the modelView Matrix but it doesn't solve the problem, the 3D object appears at the center of the screen. Can you explain how should I approach this problem? Thanks
#Override
public void draw() {
super.draw();
GLES20.glEnable(GLES20.GL_CULL_FACE);
GLES20.glEnable(GLES20.GL_DEPTH_TEST);
GLES20.glFrontFace(GLES20.GL_CCW);
// Look for trackables, and draw on each found one.
for (int trackableUID : trackableUIDs) {
// If the marker is visible, apply its transformation, and render a cube
if (ARToolKit.getInstance().queryMarkerVisible(trackableUID)) {
float[] projectionMatrix = ARToolKit.getInstance().getProjectionMatrix();
float[] modelViewMatrix = ARToolKit.getInstance().queryMarkerTransformation(trackableUID);
float[] scalingMat = {1, 0, 0, 0, 0, 3.0f, 0, 0, 0, 0, 1.0f, 0, 0.0f, 0, 0, 1};
multiplyMM(modelViewMatrix, 0, scalingMat, 0, modelViewMatrix, 0);
float[] rightModelMatrix = new float[16];
Matrix.setIdentityM(rightModelMatrix, 0);
// Translate outer sphere by 5 in x.
Matrix.translateM(rightModelMatrix, 0, 5.0f, 0.0f, 0.0f);
Matrix.multiplyMM(modelViewMatrix, 0, rightModelMatrix, 0, modelViewMatrix, 0);
cube.draw(projectionMatrix, modelViewMatrix);
}
}
Also tried this but the object gets displayed at the center of the screen.
glMatrixMode(GL_PROJECTION);
glTranslatef(5f, 0f, 0f);
I am having some problems with proper scene manipulations with OpenGL ES 2.0. On my screen I want to draw a rectangle and a cube. Rectangle should be moved to the bottom and scaled while cube should be moved to the top and scaled. What's more, I want to rotate this cube in all 3 axis.
This is what I've created so far:
#Override
public void onDrawFrame(GL10 gl) {
float[] scratch = new float[16];
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
Matrix.setLookAtM(viewMatrix, 0, 0, 0, -6, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
Matrix.multiplyMM(MVPMatrix, 0, projectionMatrix, 0, viewMatrix, 0);
Matrix.translateM(scratchMatrix1, 0, 0, -1.1f, 0);
Matrix.scaleM(scratchMatrix3, 0, 0.5f, 0.5f, 0.5f);
Matrix.multiplyMM(scratchMatrix2, 0, scratchMatrix1, 0, scratchMatrix3, 0);
Matrix.multiplyMM(scratch, 0, scratchMatrix2, 0, MVPMatrix, 0);
rectangle.draw(scratch);
Matrix.setRotateM(scratchMatrix1, 0, angleXVal, 1.0f, 0, 0);
Matrix.setRotateM(rotationMatrix, 0, angleYVal, 0, 1.0f, 0);
Matrix.multiplyMM(scratchMatrix2, 0, rotationMatrix, 0, scratchMatrix1, 0);
Matrix.setRotateM(rotationMatrix, 0, angleZVal, 0, 0, 1.0f);
Matrix.multiplyMM(scratchMatrix3, 0, rotationMatrix, 0, scratchMatrix2, 0);
Matrix.translateM(scratchMatrix1, 0, 0, 0.3f, 0);
Matrix.multiplyMM(scratchMatrix2, 0, scratchMatrix3, 0, scratchMatrix1, 0);
Matrix.scaleM(scratchMatrix1, 0, 0.8f, 0.8f, 0.8f);
Matrix.multiplyMM(scratchMatrix3, 0, scratchMatrix2, 0, scratchMatrix1, 0);
Matrix.multiplyMM(scratch, 0, scratchMatrix2, 0, MVPMatrix, 0);
cube.draw(scratch);
}
The problem is that when I am rotating the cube also the rectangle is moving. It is this same with scale and translating of cube - it is pulling the rectangle.
For me it looks like the matrices are connected somewhere but I don't see where, because the rectangle is drawn first and scratch matrix is overwritten later.
Assuming that is your complete onDrawFrame() code, scratchMatrix1 etc are class member variables that persist from frame to frame.
That means that translations from one frame will carry over to the next, because Matrix.translateM() applies a translation to an existing matrix transform by adding to it, rather than setting the matrix to a given translation matrix and overwriting the previous values. So the translations will accumulate over several frames.
That means that when you translate the cube, the translation will still be sitting in scratchMatrix1 the next time onDrawFrame is called, and the rectangle translation will be added to it rather than be set afresh.
The same goes for Matrix.scaleM().
You can rectify this by initializing all the scratch matrices to identity at the start of the function, and again before beginning the calculation of the cube transform matrix:
Matrix.setIdentityM(scratchMatrix1, 0);
Matrix.setIdentityM(scratchMatrix2, 0);
Matrix.setIdentityM(scratchMatrix3, 0);
Matrix.setIdentityM(scratch, 0);
I am trying to rotate a Triangle along Z axis from centre of triangle. But here i am getting triangle rotation from centre of an edge of triangle and not from its centre.
Renderer code:
#Override
public void onDrawFrame(GL10 gl) {
float scratch[] = new float[16];
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
Matrix.setLookAtM(mViewMatrix, 0, 0, 0, -3, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mViewMatrix, 0);
int time = (int) (SystemClock.uptimeMillis() % 4000l);
float angle = 0.090f * time;
Matrix.setRotateM(mRotationMatrix, 0, angle, 0.0f, 0.0f, -1.0f);
Matrix.multiplyMM(scratch, 0, mMVPMatrix, 0, mRotationMatrix, 0);
triangle.draw(scratch);
}
The problem is more likely due to your vertices, which compose the triangle.
Solution 1: Before rotating the triangle, translate it so that its center aligns with the scene's center.
Solution 2: Provide vertices, that are around the center. For example:
glVertex(0,0,0);
glVertex(1,0,0);
glVertex(0,1,0); // will produce rotation around the first vertex
... so offset them with a half:
glVertex(0-0.5,0-0.5,0-0.5);
glVertex(1-0.5,0-0.5,0-0.5);
glVertex(0-0.5,1-0.5,0-0.5); // will produce rotation around the approximate center
Best way is to calculate the center and translate before rotation.
Good luck!
I'm trying to work on some OpenGL stuff. What I've got up to now is a viewport, in which I'm drawing some imaginary "borders" by using GL_LINES. It looks like this, with setLookAt set as follows:
Matrix.setLookAtM(mViewMatrix, 0, 0, 0, 5, 0, 0, 0, 0, 1, 0);
My frustum is set: Matrix.frustumM(mProjectionMatrix, 0, -2, 2, -2, 2, 1, 11); so I'm positioned somewhere inside the "cube".
Now what I'm trying to achieve is let the user look around. I'm capturing onTouchEvents, passing any movement in x/y direction to the renderer. What I'm doing next is rotating all lines drawn by the specific angle I received from the touch listener.
It then looks like this:
So the cube is not rotated around the viewer or the eye-center, but instead around some point that I don't know where it's coming from.
My problem is: how can I rotate the object around the viewer's center/position? Do I have to rotate the mViewMatrix which comes from setLookAtM? If yes, simply by using Matrix.setRotateM(mViewMatrix, ...)?
The Line's drawing method looks like this:
public void draw(float[] mViewMatrix, float[] mProjectionMatrix) {
Matrix.multiplyMM(mViewProjectionMatrix, 0, mProjectionMatrix, 0, mViewMatrix, 0);
GLES20.glUseProgram(iProgId);
lineBuffer.position(0);
GLES20.glVertexAttribPointer(iPosition, 3, GLES20.GL_FLOAT, false, 0, lineBuffer);
GLES20.glEnableVertexAttribArray(iPosition);
colorBuffer.position(0);
GLES20.glVertexAttribPointer(iColor, 3, GLES20.GL_FLOAT, false, 0, colorBuffer);
GLES20.glEnableVertexAttribArray(iColor);
Matrix.setIdentityM(mModelMatrix, 0);
Matrix.rotateM(mModelMatrix, 0, rotX, 1, 0, 0);
Matrix.rotateM(mModelMatrix, 0, -rotY, 0, 1, 0);
Matrix.setIdentityM(mViewProjectionMatrix, 0);
Matrix.multiplyMM(mViewProjectionMatrix, 0, mViewMatrix, 0, mModelMatrix, 0);
//GLES20.glUniformMatrix4fv(iVPMatrix, 1, false, mMVPMatrix, 0);
Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mViewProjectionMatrix, 0);
GLES20.glUniformMatrix4fv(iVPMatrix, 1, false, mMVPMatrix, 0);
//GLES20.glDrawElements(GLES20.GL_LINES, mVertices.length/2, GLES20.GL_UNSIGNED_SHORT, indexBuffer);
GLES20.glDrawArrays(GLES20.GL_LINES, 0, mVertices.length / 2);
}
look at function takes 3 vertices, eye position, target position and up vector. Basically it generates a matrix that moves scene around to render scene like you are looking from eye position to target position. In your example your is at 0,0,0 and looks at 5, 0, 0 (so you are looking at +x direction and up is defined as 0, 1, 0 (higher y value means object will be at top of window.)
Instead of using those constants, first define a camera position.
float cameraX, cameraY, cameraY;
it is harder to work with target vectors, so instead use an angle that defines which direction you are looking at
float angle;
and to calculate targetVector use this angle.
float targetX = cameraX + cos(angle);
float targetY = cameraY;
float targetZ = cameraZ + sin(angle);
Now to move camera around, you have to modify cameraX-Y-Z. If you want to move forward you should move your camera towards to target vector. For example to move 10 unit forward.
targetX += cos(angle)*10;
targetY += sin(angle)*10;
You also need to recalculate target vector since target position also should move.
If you want to move backwards, use -= operator instead. If you want to move sides then you have to add or remove angle PI/2 in those calculations.
To rotate camera around just increase/decrease angle and recalculate target vector.
This is a very basic camera and you won't be able to look up or down. You have to use pitch/yaw camera to be able to look up and down.
I am trying to write an Android application that can translate a triangle a have used the
http://developer.android.com/training/graphics/opengl/motion.html code from Google but when I replaced the
Matrix.setRotateM(mRotationMatrix, 0, mAngle, 0, 0, -1.0f);
Matrix.multiplyMM(mMVPMatrix, 0, mRotationMatrix, 0, mMVPMatrix, 0);
with
Matrix.translate(mMVPMatrix,0,dx,dy,0);
the triangle was moving also on the Z axis and it did not look like a translation at all
What can i do ?
The following code:
Matrix.setRotateM(mRotationMatrix, 0, mAngle, 0, 0, -1.0f);
Matrix.multiplyMM(mMVPMatrix, 0, mRotationMatrix, 0, mMVPMatrix, 0);
means:
mRotationMatrix <-- Create a rotation matrix of mAngle degrees around axis -Z
mMVPMatrix <-- The product of mRotationMatrix and mMVPMatrix
While the following:
Matrix.translate(mMVPMatrix,0,dx,dy,0);
means:
mMVPMatrix <-- Translate mMVPMatrix of dx along X axis and dy along Y axis
I assume that mMVPMatrix is a perspective projection (and the P of MVP usually suggests that). Usually you don't translate stuff that has already been projected. Try the following instead:
Matrix.setIdentityM(mTranslationMatrix, 0);
Matrix.translateM(mTranslationMatrix, 0, dx, dy, 0);
Matrix.multiplyMM(mMVPMatrix, 0, mTranslationMatrix, 0, mMVPMatrix, 0);
I dont think you want to translate your MVP matrix, you want to create an identity matrix, translate it, then multiply it with the mMVPMatrix.