How to draw a HUD using shaders on opengl es 2.0?
I have a shader which draws a textured quad on screen, it uses MVP matrix. The quad has it own vertices which are independent of view position and so on (cause of MVP matrix)
Matrix.frustumM(mProjMatrix, 0, -ratio, ratio, -1, 1, 3f, 17);
Matrix.setLookAtM(mVMatrix, 0, 0, 0, -5, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
I'd like to show the same quad on the top right corner (like a button or something else, HUD).
As I understand, i need create an ortho matrix instead of "frustumM", but what should i do later? How vertex shader should use vertices of quad?
Ok, You have your ortho matrix and quad, so whats the problem, translate modelview matrix of your quad to desired position (x,y,z=0), multiply it by ortho matrix , pass multiplied matrix to vertex shader, multiply vert position by your matrix and done :), i am not using any lookat function in my code to do this, but i have own code for matrices computation its partially code from some bada tutorial, for projection matrix i have other function.
void
Letter::Ortho(Matrix* result, float fovy, float aspect, float nearZ, float farZ)
{
GLfloat frustumW, frustumH;
frustumH = tanf(fovy / 360.0f * PI) * nearZ;
frustumW = frustumH * aspect;
Frustum(result, -frustumW, frustumW, -frustumH, frustumH, nearZ, farZ);
}
void
Letter::LoadIdentity(Matrix* result)
{
memset(result, 0x0, sizeof(Matrix));
result->m[0][0] = 1.0f;
result->m[1][1] = 1.0f;
result->m[2][2] = 1.0f;
result->m[3][3] = 1.0f;
}
void
Letter::Frustum(Matrix *result, float left, float right, float bottom, float top, float nearZ, float farZ)
{
float deltaX = right - left;
float deltaY = top - bottom;
float deltaZ = farZ - nearZ;
Matrix frustum;
if ((nearZ <= 0.0f) || (farZ <= 0.0f) ||
(deltaX <= 0.0f) || (deltaY <= 0.0f) || (deltaZ <= 0.0f))
{
return;
}
frustum.m[0][0] = 2.0f * nearZ / deltaX;
frustum.m[0][1] = frustum.m[0][2] = frustum.m[0][3] = 0.0f;
frustum.m[1][1] = 2.0f * nearZ / deltaY;
frustum.m[1][0] = frustum.m[1][2] = frustum.m[1][3] = 0.0f;
frustum.m[2][0] = (right + left) / deltaX;
frustum.m[2][1] = (top + bottom) / deltaY;
frustum.m[2][2] = -(nearZ + farZ) / deltaZ;
frustum.m[2][3] = -1.0f;
frustum.m[3][2] = -2.0f * nearZ * farZ / deltaZ;
frustum.m[3][0] = frustum.m[3][1] = frustum.m[3][3] = 0.0f;
Multiply(result, &frustum, result);
}
So, with this code :
LoadIdentity(&matPerspective);
Ortho(&matPerspective, 60.0f, TEXMANAGER.aspect, -1.0f, 20.0f);
LoadIdentity(&matModelview);
Translate(&matModelview, x ,y ,z);
Scale(&matModelview,size);
//Rotate(&matModelview, 0.0f, 1.0f, 0.0f, 1.0f);
Multiply(&posMatrix, &matModelview, &matPerspective);
And pass posMatrix to shader :)
Related
To rotate the camera around an object along the abscissa axis (X), I use the following code:
private float k = 0f;
...
#Override
public void onDrawFrame(GL10 gl) {
// Math.PI * 2 - full rotation
k = (k >= Math.PI * 2) ? 0.0f : k + 0.01f; // gradually rotate the camera
float radius = 2.6f;
float x = (float) (radius * Math.cos(k));
float z = (float) (radius * Math.sin(k));
Matrix.setLookAtM(viewMatrix, 0, x, 0, z, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
...
}
To rotate the camera around an object along the ordinate axis (Y):
...
float y = (float) (radius * Math.cos(k));
float z = (float) (radius * Math.sin(k));
Matrix.setLookAtM(viewMatrix, 0, 0, y, z, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
...
But I don't understand how to rotate the camera around an object along an inclined axis. What is the ratio between x, y, z should be in this case?
Thank you for any comment / response!
Note: The center of the object coincides with the point where the camera is looking (0,0,0).
Found this solution:
#Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
...
Matrix.setLookAtM(viewMatrix, 0, 0f, 0f, 5f,
0f, 0f, 0f, 0f, 1.0f, 0.0f);
}
#Override
public void onDrawFrame(GL10 gl) {
Matrix.rotateM(viewMatrix, 0, angle1, x:1, y:0, z:0);
Matrix.rotateM(viewMatrix, 0, angle2, x:0, y:1, z:0);
...
}
This is an Android application. I want to rotate a sphere with touch event using Matrix in OpenGLES 2.0. However the angle is not right. It seems like the rotate is not stable. I want to rotate only x when I touch horizontally, and rotate only y when I touch vertically. But in fact, when I touch horizontally it rotate x and y, so did when I touch vertically... Something wrong with the calculation of rotate anglex and angley or the Matrix set up?
Here is the touch event:
public boolean onTouchEvent(MotionEvent e) {
float x = e.getX();
float y = e.getY();
switch (e.getAction()) {
case MotionEvent.ACTION_MOVE:
float dx = x - mPreviousX;
float dy = y - mPreviousY;
mRenderer.setAngleX(mRenderer.getAngleX() + (dx * TOUCH_SCALE_FACTOR));
mRenderer.setAngleY(mRenderer.getAngleY() - (dy * TOUCH_SCALE_FACTOR));
requestRender();
}
mPreviousX = x;
mPreviousY = y;
return true;
}
This is the Matrix set up:
public void onDrawFrame(GL10 unused) {
// Draw background color
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
//set up the matrix
float [] mTempMatrix = new float[16];
Matrix.setIdentityM(mModelMatrix, 0); // initialize to model matrix
Matrix.setIdentityM(mTranslateMatrix,0);
Matrix.translateM(mTranslateMatrix, 0, 0.0f, 0.0f, 0.0f);
mTempMatrix = mModelMatrix.clone();
Matrix.multiplyMM(mModelMatrix,0,mTempMatrix,0,mTranslateMatrix,0);
Matrix.setIdentityM(mRotationMatrix,0);
Matrix.rotateM(mRotationMatrix, 0, mAngleX, 0,1,0);
Matrix.rotateM(mRotationMatrix, 0, mAngleY, 1,0,0);
mTempMatrix = mModelMatrix.clone();
Matrix.multiplyMM(mModelMatrix,0,mTempMatrix,0,mRotationMatrix,0);
Matrix.setIdentityM(mScaleMatrix,0);
Matrix.scaleM(mScaleMatrix,0, 1f, 1f, 1f);
mTempMatrix = mModelMatrix.clone();
Matrix.multiplyMM(mModelMatrix,0,mTempMatrix,0,mScaleMatrix,0);
Matrix.setLookAtM(mViewMatrix, 0,
mLocation.x,mLocation.y,mLocation.z,
mTarget.x,mTarget.y,mTarget.z,
mUp.x,mUp.y,mUp.z);//camera
mTempMatrix = mViewMatrix.clone();
Matrix.multiplyMM(mViewMatrix, 0, mTempMatrix,0,mModelMatrix,0);
Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mViewMatrix, 0);
mSphere.draw(mMVPMatrix);
}
It seems to me that since you use
mRenderer.setAngleX(mRenderer.getAngleX() + (dx * TOUCH_SCALE_FACTOR));
Even if dx is 0, any previous angle (through mRenderer.getAngleX() ) will be re-applied in the next iteration.
Let dx alone modify mAngleX instead (and do the same for mAngleY of course):
mRenderer.mAngleX += dx * TOUCH_SCALE_FACTOR;
I'm having a hard time implementing the rotation and movement of an on-screen OpenGL shape. Basically, what I want to achieve is being able to control the shape using a touch screen. Wherever I touch, it should rotate to that direction and start moving towards until it gets there.
Here's the code that sets up the frustum and the camera in onSurfaceChanged():
glViewport(0, 0, width, height);
float sizeRatio = (float) width / height;
Matrix.frustumM(
projectionMatrix, 0, -sizeRatio, sizeRatio, -1.0f, 1.0f, 1.0f, 20.0f
);
Matrix.setLookAtM(
viewMatrix, 0, 0, 0, -60, 0f, 0f, 0f, 0.0f, 1.0f, 0.0f
);
Matrix.multiplyMM(globalMvpMatrix, 0, projectionMatrix, 0, viewMatrix, 0);
and here's how the touch input is handled in onTouchEvent() (shape is an object that stores position and rotation and then draws the shape on the screen):
lastTouchX = event.getX();
lastTouchY = event.getY();
float shapePosX = shape.getPositionX();
float shapePosY = shape.getPositionY();
int[] viewport = new int[4];
glGetIntegerv(GL_VIEWPORT, viewport, 0);
float[] unprojectedTouchCoords = new float[4];
GLU.gluUnProject(
lastTouchX, lastTouchY, 0,
viewMatrix, 0,
projectionMatrix, 0,
viewport, 0,
unprojectedTouchCoords, 0
);
float unprojectedX = unprojectedTouchCoords[0] / unprojectedTouchCoords[3];
float unprojectedY = unprojectedTouchCoords[1] / unprojectedTouchCoords[3];
float rotation = 90 - (float) Math.toDegrees(Math.atan2(
shapePosY - unprojectedY, shapePosX - unprojectedX
));
shape.setRotation(rotation);
float moveX = 2 * (float) Math.cos(Math.toRadians(rotation));
float moveY = 2 * (float) Math.sin(Math.toRadians(rotation));
shape.move(moveX, moveY);
However, it doesn't seem to work well. The shape is moving in the wrong direction, and the rotation is only correct if the position of the shape is (0, 0). In any other case, it breaks.
I guess this problem involves distance between the camera and the shape in the OpenGL space, but I have no idea where and how to fix that. I tried bringing the camera closer to the shape but with no apparent improvement.
Can you please help me with this one? I'm getting really frustrated.
I haven't looked deeply at your code but I really want to give you a suggestion: the order of transformations matters in OpenGL, because they are cumulative.
This means that rotate and translate is different than translate and rotate.
Here is a good starting point to understand what I'm saying.
Seems like you have co-ordinates calculation problem. You need to #Override the onTouchEvent method of GLSurfaceView, rest is below
private final float TOUCH_SCALE_FACTOR = 180.0f / 320;
private float mPreviousX;
private float mPreviousY;
#Override
public boolean onTouchEvent(MotionEvent e) {
// MotionEvent reports input details from the touch screen
// and other input controls. In this case, you are only
// interested in events where the touch position changed.
float x = e.getX();
float y = e.getY();
switch (e.getAction()) {
case MotionEvent.ACTION_MOVE:
float dx = x - mPreviousX;
float dy = y - mPreviousY;
// reverse direction of rotation above the mid-line
if (y > getHeight() / 2) {
dx = dx * -1 ;
}
// reverse direction of rotation to left of the mid-line
if (x < getWidth() / 2) {
dy = dy * -1 ;
}
mRenderer.setAngle(
mRenderer.getAngle() +
((dx + dy) * TOUCH_SCALE_FACTOR)); // = 180.0f / 320
requestRender();
}
mPreviousX = x;
mPreviousY = y;
return true;
}
I set my GL10 object in this way in my 2D Android game:
public void onSurfaceChanged(GL10 gl, int width, int height) {
gl.glViewport(0, 0, width, height);
gl.glMatrixMode(GL10.GL_PROJECTION);
gl.glLoadIdentity();
gl.glOrthof(0, width, height, 0, -1f, 1f);
gl.glMatrixMode(GL10.GL_MODELVIEW);
gl.glLoadIdentity();
}
When I scale my GL10 object in this way (for example):
gl.glScalef(50, 50, 0);
Then a call to a translate transformation such as:
gl.glTranslatef(1, 1, 0f);
Causes a translate transformation of 50 pixel instead of 1 pixel. The same goes for a rotate transformation.
How can I do a post translate transformation without knowing previous transformations? Is it possible?
I'm not OpenGL guru but what I used is changing vertices like:
Lets say we had:
vertices = new float[12];
vertices[0] = -0.25f;
vertices[1] = -0.25f;
vertices[2] = 0.0f;
vertices[3] = -0.25f;
vertices[4] = 0.25f;
vertices[5] = 0.0f;
vertices[6] = 0.25f;
vertices[7] = -0.25f;
vertices[8] = 0.0f;
vertices[9] = 0.25f;
vertices[10] =0.25f;
vertices[11] =0.0f;
So to change zoom in I used:
gl.glPushMatrix();
...
vertices[0] = -0.25f - 1.75f *(mGameRange - distance)/mGameRange;// dummy increase index
vertices[1] = -0.25f - 1.75f *(mGameRange - distance)/mGameRange;
vertices[3] = -0.25f - 1.75f *(mGameRange - distance)/mGameRange;
vertices[4] = 0.25f + 1.75f *(mGameRange - distance)/mGameRange;
vertices[6] = 0.25f + 1.75f *(mGameRange - distance)/mGameRange;
vertices[7] = -0.25f - 1.75f *(mGameRange - distance)/mGameRange;
vertices[9] = 0.25f + 1.75f *(mGameRange - distance)/mGameRange;
vertices[10] =0.25f + 1.75f *(mGameRange - distance)/mGameRange;
vertexBuffer.clear();
vertexBuffer.put(vertices);
vertexBuffer.position(0);
...
gl.glPopMatrix();
try putting your scale call inside of a glPushMatrix() and glPopMatrix() and having your translate call after the pop.
good discussion here
What i've found is that if you want the translations to be separate you have to translate in the opposite direction for example after finishing a translation.
gl.glTranslatef(position.X, position.Y, 0);
gl.glDrawArrays(...); //first triangle
gl.glTranslatef(-position.X, -position.Y, 0); // note how they are negative instead of positive
gl.glDrawArrays(...); //second triangle
in this code the second triangle will be drawn at 0,0,0 and the first will be drawn at position.X, position.Y , 0. this tends to work for me but i still recommend pushing and popping the matrix before and after doing these translations.
Actually i have an application that creates a square (a polygon, not a cube, a square) and the user can rotate it touching the screen, but something is going bad, because the rotation is not fair, it only can rotate it horizontally and vertically, but not circularly. I need that the user can rotate it circularly too.
I use this code to do the rotation (horizontal/vertical)
public void onDrawFrame(GL10 gl) {
//Clear Screen And Depth Buffer
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
gl.glLoadIdentity(); //Reset The Current Modelview Matrix
//Drawing
gl.glTranslatef(0.0f, 0.0f, z); //Move z units into the screen
gl.glScalef(0.8f, 0.8f, 0.8f); //Scale the Cube to 80 percent, otherwise it would be too large for the screen
//Rotate around the axis based on the rotation matrix (rotation, x, y, z)
gl.glRotatef(xrot, 1.0f, 0.0f, 0.0f); //X
gl.glRotatef(yrot, 0.0f, 1.0f, 0.0f); //Y
square.draw(gl); //Draw the Cube
//Change rotation factors
xrot += xspeed;
yrot += yspeed;
}
public boolean onTouchEvent(MotionEvent event) {
//
float x = event.getX();
float y = event.getY();
//If a touch is moved on the screen
if(event.getAction() == MotionEvent.ACTION_MOVE) {
//Calculate the change
float dx = x - oldX;
float dy = y - oldY;
//Define an upper area of 10% on the screen
int upperArea = this.getHeight() / 10;
//Zoom in/out if the touch move has been made in the upper
if(y < upperArea) {
z -= dx * TOUCH_SCALE / 2;
//Rotate around the axis otherwise
} else {
xrot += dy * TOUCH_SCALE;
yrot += dx * TOUCH_SCALE;
}
//A press on the screen
} else if(event.getAction() == MotionEvent.ACTION_UP) {
//Define an upper area of 10% to define a lower area
int upperArea = this.getHeight() / 10;
int lowerArea = this.getHeight() - upperArea;
//Change the light setting if the lower area has been pressed
if(y > lowerArea) {
}
}
//Remember the values
oldX = x;
oldY = y;
//We handled the event
return true;
}
You should try this:
yourRootLayout.setOnClickListener(yourListener);
yourAnimation = new RotateAnimation(0.0f, 360.0f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
yourAnimation.setDuration(duration in milliseconds - int);
#Override
protected void onClick(View view){
if (view.getId() == R.id.yourRootLayoutId){
yourRootLayout.startAnimation(yourAnimation);
}
}