OpenGL : How to rotate (roll) the camera? - java

The pitch and the heading work perfectly :
fun onDrawFrame(pitch: Float, roll: Float, heading: Float) {
val lookAtX = cos(pitch) * cos(heading)
val lookAtY = -sin(pitch)
val lookAtZ = cos(pitch) * sin(heading)
val upX = 0.0F
val upY = 1.0F
val upZ = 0.0F
Matrix.setLookAtM(cameraPositionMatrix, 0, 0.0F, 0.0F, 0.0F, lookAtX.toFloat(), lookAtY.toFloat(), lookAtZ.toFloat(), upX.toFloat(), upY.toFloat(), upZ.toFloat())
...
}
But how can I rotate (roll) the camera? I guess I need to rotate the UP vector based on the roll angle around the "line of sight", but it's more complex than I thought.
Does anyone know the formula to calculate the cartesian coordinates of a point A(Xa, Ya, Za) after a rotation of an angle δ (in radians) around an axis B(Xb, Yb, Zb)C(Xc, Yc, Zc)?
This question concerns both setLookAtM (OpenGL ES) and gluLookAt (OpenGL)

What you want can easily be achieved with Rx * Ry * Rz * T where Rx/y/z are the respective rotation matrices to rotate around the x, y or z axis, respectively, followed by the (negative) camera translation, which in your case is just (0, 0, 0).
You could compute it manually by doing the trigonometry by hand and computing the direction and up vectors for lookat by hand, but there's also the route via rotateM, effectively achieving the same:
fun onDrawFrame(pitch: Float, roll: Float, heading: Float) {
Matrix.setIdentityM(cameraPositionMatrix, 0)
Matrix.rotateM(cameraPositionMatrix, 0, pitch, 1, 0, 0);
Matrix.rotateM(cameraPositionMatrix, 0, heading, 0, 1, 0);
Matrix.rotateM(cameraPositionMatrix, 0, roll, 0, 0, 1);
}

Related

Java OpenGL - Mouse position from window to world space

I try to transform the window mouse coordinates (0/0 is the upper left corner) into world space coordinates. I just tried to solve it by this description. Here is my code:
public void showMousePosition(float mx, float my){
Matrix4f projectionMatrix = camera.getProjectionMatrix();
Matrix4f viewMatrix = camera.getViewMatrix();
Matrix4f projMulView = projectionMatrix.mul(viewMatrix);
projMulView.invert();
float px = ((2*mx)/650)-1;
float py = ((2*my)/650)-1;
Vector4f vec4 = new Vector4f(px, py*(-1), 0.0f, 1.0f);
vec4.mul(projMulView);
vec4.w = 1.0f / vec4.w;
vec4.x *= vec4.w;
vec4.y *= vec4.w;
vec4.z *= vec4.w;
System.out.println(vec4.x + ", " + vec4.y);
}
But thats not 100% correct. I have an Object on 0/-11 on world space and when I move my mouse to this point, my function say 0/9,8. And when I go to the left side of my window the x value is 5,6 but it should be something like 28.
Someone know what is wrong on my code?
First of all, your code says that your windows size is always width=650, height=650.
Then you are getting the position when z=0. But this z is in screen space and therefore it changes as you change the camera position and orientation. Normally, you get this information from the depth buffer, using glReadPixel. You should do it in this case.
However, there is another way to do this also. In the code I will share, I am looking for the intersection between a ray (generated from the mouse position) and the plane (0,0,0) with normal (0,1,0), I hope this helps.
/*Given the inverse PV (projection*view) matrix, the position of the mouse on screen and the size of the screen, transforms the screen coordinates to world coordinates*/
glm::vec3 Picking::OnWorld(glm::mat4 const& m_inv, glm::vec2 const & spos,size_t width, size_t height) {
float x = spos.x;
float y = spos.y;
y = height - y;
//InputOrigin, start of the ray for intersection with plane
glm::vec4 inputO = glm::vec4(x / width*2.0f - 1.0f, y / height*2.0f - 1.0f, -1.0f, 1.0f); //transforms screen position to the unit cube range
glm::vec4 resO = m_inv*inputO; //transforms to world space
if (resO.w == 0.0f)
return glm::vec3(-1); //return an invalid value to show a problem during a calculation, normally this means that the m_inv matrix was incorrect
resO /= resO.w; //homogeneous division
glm::vec4 inputE = inputO; //inputEnd, the end of the ray
inputE.z = 1.0;
//End of ray to world space
glm::vec4 resE = m_inv*inputE;
//checks that the coordinates are correct
if (resE.w == 0.0f)
return glm::vec3(-1); //return an invalid value to show a problem during a calculation, normally this means that the m_inv matrix was incorrect
resE /= resE.w;
//ray for intersection
glm::vec3 ray = glm::vec3(resE - resO); //vector between z=-1 and z=1
glm::vec3 normalRay = glm::normalize(ray);
glm::vec3 normalPlane = glm::vec3(0, 1, 0); //detects collision with plane 0, normal 1
float denominator = glm::dot(normalRay, normalPlane);
if (denominator == 0)
return glm::vec3(-1); //return an invalid value to show a problem during a calculation, normally this means that the m_inv matrix was incorrect
float numerator = glm::dot(glm::vec3(resO), normalPlane);
//intersection between ray and plane
glm::vec3 result = glm::vec3(resO) - normalRay*(numerator / denominator);
return result;
}
The math for the intersection can be read from this link:
https://www.cs.princeton.edu/courses/archive/fall00/cs426/lectures/raycast/sld017.htm

Sphere collision test reacts too quickly

I'm writing a game for Android using Java and OpenGL. I can render everything perfectly to screen, but when I try to check whether two objects collide or not, my algorithm detects a collision before it occurs on the screen.
Here's how I test for collision:
for(int i=0; i<enemies.size(); i++) {
float enemyRadius = enemies.elementAt(i).worldSpaceBoundingSphereRadius();
float[] enemyPosition = enemies.elementAt(i).getWorldSpaceCoordinates();
for(int j=0; j<qubieBullets.size(); j++) {
float bulletRadius = bullets.elementAt(j).worldSpaceBoundingSphereRadius();
float[] bulletPosition = bullets.elementAt(j).getWorldSpaceCoordinates();
float[] distanceVector = Vector3f.subtract(enemyPosition, bulletPosition);
float distance = Vector3f.length(distanceVector);
if(distance < (enemyRadius + bulletRadius)) {
enemies.remove(i);
qubieBullets.remove(j);
i--;
j--;
// Reset enemy position
}
}
}
When the enemy cube (represented by a sphere for collision detection) closes in on the player, the player shoots a bullet (also a cube represented by a sphere) toward the enemy. My expectations are that the enemy gets reset when the bullet hits him on screen, but it happens way earlier than that.
The methods for calculation world space position and radius:
public float[] getWorldSpaceCoordinates() {
float[] modelSpaceCenter = {0.0f, 0.0f, 0.0f, 1.0f};
float[] worldSpaceCenter = new float[4];
Matrix.multiplyMV(worldSpaceCenter, 0, getModelMatrix(), 0, modelSpaceCenter, 0);
return new float[] {worldSpaceCenter[0]/worldSpaceCenter[3], worldSpaceCenter[1]/worldSpaceCenter[3], worldSpaceCenter[2]/worldSpaceCenter[3]};
}
public float worldSpaceBoundingSphereRadius() {
float[] arbitraryVertex = new float[] {1.0f, 1.0f, 1.0f, 1.0f};
float[] worldSpaceVector = new float[4];
Matrix.multiplyMV(worldSpaceVector, 0, getModelMatrix(), 0, arbitraryVertex, 0);
float[] xyz = new float[] {worldSpaceVector[0]/worldSpaceVector[3], worldSpaceVector[1]/worldSpaceVector[3], worldSpaceVector[2]/worldSpaceVector[3]};
return Vector3f.length(xyz);
}
Is it my code or math that's wrong? I can't think of anything more to try, and would be helpful if someone could point me in the right direction.
Your worldSpaceBoundingSphereRadius() is most likely the culprit. arbitraryVertex is a Vector of (1,1,1) so your math will only work if the cube model has edges of length 2 * sqrt(1/3). What you want to do is find the exact length of your cube's model's edge, use the formula from my comment (rad = sqrt( 3 * (x/2) * (x/2) )) and use that radius for your arbitraryVertex (rad,rad,rad,1).
Also, your dividing the results of your multiplication by the homogenous coordinate (worldSpaceVector[0]/worldSpaceVector[3]). With a proper rotation, translation, or scale, the homogenous coordinate should always be exactly 1 (if it started as one). If it isn't, you might have a projection matrix in there or something else that isn't a basic transformation.
EDIT:
Since you're using worldSpaceBoundingSphereRadius() to get only the radius, you want only the scaling component of getModelMatrix(). If that returns scaling and translation, this translation will apply to your radius and make it much larger than it actually is.

Doing a matrix multiplication in reverse

Follow-up for: Calculating world coordinates from camera coordinates
I'm multiplying a 2D vector with a transformation matrix (OpenGL's model-view matrix) to get world coordinates from my camera coordinates.
I do this calculation like this:
private Vector2f toWorldCoordinates(Vector2f position) {
glPushMatrix();
glScalef(this.zoom, this.zoom, 1);
glTranslatef(this.position.x, this.position.y, 0);
glRotatef(ROTATION, 0, 0, 1);
ByteBuffer m = ByteBuffer.allocateDirect(64);
m.order(ByteOrder.nativeOrder());
glGetFloatv(GL_MODELVIEW_MATRIX, m);
float x = (position.x * m.getFloat(0)) + (position.y * m.getFloat(4)) + m.getFloat(12);
float y = (position.x * m.getFloat(16)) + (position.y * m.getFloat(20)) + m.getFloat(28);
glPopMatrix();
return new Vector2f(x, y);
}
Now I also want to do this vice-versa: calculate the camera coordinates for a position in the world. How can I reverse this calculation?
To create a matrix representing the inverse transform to the one above, apply the transforms in reverse, with negative quantities for the rotation and translation and an inverse quantity for the zoom:
glRotatef(-ROTATION, 0, 0, 1);
glTranslatef(-this.position.x, -this.position.y, 0);
glScalef(1.0f / this.zoom, 1.0f / this.zoom, 1);
Then multiply by the position vector as before.
The alternative is to compute the inverse matrix, but this way is much simpler.

OpenGL ES 2.0 - Mesh is getting distorted by Y and Z axes

I'm trying to display simple mesh, but I've got the following problem:
when I'm trying to translate mesh, or specifying most of perspective matrix parameters (using the Matrix.frustumM method), my mesh is getting distorted. It shrinks by the Z axis, and stretches by the Y axis.
My camera matrix code:
Matrix.setLookAtM(mViewMatrix, 0, 0, 0,-3, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f);
My projection matrix code (copy-pasted from here: source )
float near = 1.0f;
float far = 10.0f;
float ratio = (float) width/height;
float fov = 60;
float top = (float)Math.tan(fov * Math.PI / 360.0f) * near;
float bottom = -top;
float left = ratio * bottom;
float right = ratio * top;
Matrix.frustumM(mProjMatrix, 0, left, right, bottom, top, near, far);
My vertex shader code:
private static String vertexShaderCode =
"uniform mat4 uMVPMatrix;" +
"attribute vec4 aPosition;" +
"attribute vec2 aTextureCoordIn;"+
"varying vec2 vTextureCoordOut;"+
"void main() {"+
" vTextureCoordOut = aTextureCoordIn;"+
" gl_Position = aPosition * uMVPMatrix;" +
"}";
Mesh is scaled using the 0.2 multiplier, using the following code:
mModelMatrix = new float[16];
Matrix.setIdentityM(mModelMatrix, 0);
Matrix.rotateM(mModelMatrix, 0, rotateX, 1, 0, 0);
Matrix.rotateM(mModelMatrix, 0, rotateY, 0, 1, 0);
Matrix.rotateM(mModelMatrix, 0, rotateZ, 0, 0, 1);
Matrix.scaleM(mModelMatrix, 0, scaleX, scaleY, scaleZ);
Matrix.translateM(mModelMatrix, 0, translateX, translateY, translateZ);
And mvp matrix multiplied using this:
float[] mMVPMatrix = new float[16];
Matrix.multiplyMM(mMVPMatrix, 0, mViewMatrix, 0, mModelMatrix, 0);
Matrix.multiplyMM(mMVPMatrix, 0, mProjMatrix, 0, mMVPMatrix, 0);
I have tried solutions from the following topics:first,second, but without success.
Screenshots:
I have also found that when I'm using the following projection matrix, and dont translate mesh, it looks correct:
Matrix.frustumM(mProjMatrix, 0, -ratio, ratio, -1, 1, 2, 6);
Screen:
I've tried to find solution for several hours, but without any effect. I'm sure that the problem with my matrices, but I dont know how to resolve it.
Any ideas are highly appreciated.
Well, there is a huge confusion with android tutorials.. The way you multiply your matrices dictates to multiply the position in the vertex shader main function the same way, thus:
instead of:
"gl_Position = aPosition * uMVPMatrix;" +
do:
"gl_Position = uMVPMatrix * aPosition;" +

opengl camera rotation around object

I'm fighting with the code, to make it rotate camera around the object. The code that I'm using is:
eyeX = (float) (obj.x + 500*Math.cos(Math.toRadians(angle))*Math.sin(Math.toRadians(angle)));
eyeY = (float) (obj.y + 500*Math.sin(Math.toRadians(angle))*Math.sin(Math.toRadians(angle)));
eyeZ = (float) (obj.z + 500*Math.cos(Math.toRadians(angle)));
Matrix.setLookAtM(mViewMatrix, 0, eyeX, eyeY, eyeZ, obj.x, obj.y, obj.z, 0, 1, 0);
Where obj is the model I want to rotate the camera around, and angle is incremented by 1 with every draw. Can somebody shed a light to this problem, what I'm doing wrong?
What exact camera motion are you trying to achieve?
If you want to turn around obj on the XZ plane while remaining at a constant height, you should do:
eyeX = (float)(obj.x + 500*Math.cos(Math.toRadians(angle));
eyeY = (float)(y0); //constant height
eyeZ = (float)(obj.z + 500*Math.sin(Math.toRadians(angle));
Also, if y0 != 0, then (0, 1, 0) is not a proper "up" vector and will distort you view.

Categories

Resources