Doing a matrix multiplication in reverse - java

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.

Related

OpenGL : How to rotate (roll) the camera?

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);
}

How to rotate a normalized vector towards a point with joml

So this is my understanding of how to rotate a vector towards a point:
Vector A = (0, 0, -1)
Vector B = (15, 164, 16)
Step 1: Normalize B
Step 2: Calculate angle between A and normalized B
Step 3: Calculate the cross product of A and normalized B
Then, rotating A around the axis we calculated in step 3 by the angle calculated in step 2 (in radians) should give me the normalized vector of B.
However, trying to do it with joml i don't get the right result.
Here is my code:
Vector3f vecA = new Vector3f(0.0f, 0.0f, -1.0f);
System.out.println("Vector A: " + vecA.toString(FORMAT));
Vector3f vecB = new Vector3f(0, 0.5f, 1.0f).normalize();
System.out.println("Vector B: " + vecB.toString(FORMAT));
float angle = (float) Math.acos(vecA.dot(vecB));
System.out.println("Angle between the two: " + angle + "(" + Math.toDegrees(angle) + "°)");
Vector3f rotationAxis = new Vector3f();
vecA.cross(vecB, rotationAxis);
Vector3f rotatedVector = new Vector3f();
vecA.rotateAxis(angle, rotationAxis.x, rotationAxis.y, rotationAxis.z, rotatedVector).normalize();
System.out.println("Rotated Vector: " + rotatedVector.toString(FORMAT));
This results in the following output:
Vector A: (0 0 -1)
Vector B: (0 0.44721 0.89443)
Angle between the two: 2.6779451(153.43495411905388°)
Rotated Vector: (0 0.82566 0.56416)
From the calculation above, shouldn't the rotated vector be equal to the output of Vector B?
You have to normalize your rotationAxis-vector since an axis is represented by a unit vector (see e.g. https://en.wikipedia.org/wiki/Axis%E2%80%93angle_representation).
Thus, simply replace
vecA.cross(vecB, rotationAxis);
with
vecA.cross(vecB, rotationAxis).normalize();
and it works:
Vector A: (0 0 -1)
Vector B: (0 0.44721 0.89443)
Angle between the two: 2.6779451(153.43495411905388°)
Rotated Vector: (0 0.44721 0.89443)

Moving an object in a direction with opengl java

I am trying to move a triangle in the direction of the top vertex.
Depending on the rotation angle.
This is my code:
private static void render() {
// Clear the pixels on the screen and clear the contents of the depth buffer (3D contents of the scene)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Reset any translations the camera made last frame update
glLoadIdentity();
// Apply the camera position and orientation to the scene
//camera.applyTranslations();
glTranslated(0,0,-5);
glPushMatrix();
glRotated(f.get_direction(),0,0,1);
glTranslated(x,y,0);
f.draw();
glPopMatrix();
x+=(f.get_speed()/30)*cos(f.get_direction()+90);
y+=(f.get_speed()/30)*sin(f.get_direction()+90);
}
The point is that no matter what is the rotation angle that is the direction,
i want to move the triangle according to it.
Did you try to translate according to direction vector but there is simple problem cos and sin arguments is in radians glRotate in degrees:
so we must create static function
static double degToRad(double x)
{
return (x / 180.0) * Math.PI;
}
than use it
glLoadIdenity();
x += Math.cos(degToRad(getDirection() + 90)) * getSpeed();
y += Math.sin(degToRad(getDirection() + 90)) * getSpeed();
glTranslatef(x, y, 0);
glRotatef(getDirection(), 0, 0, 1);
drawObject();
you can also invert direction of movement just subsctacting current angle from 360:
glLoadIdenity();
x += Math.cos(degToRad(360 - getDirection() + 90)) * getSpeed();
y += Math.sin(degToRad(360 - getDirection() + 90)) * getSpeed();
glTranslatef(x, y, 0);
glRotatef(getDirection(), 0, 0, 1);
drawObject();

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

Billboard facing the camera has wrong rotation near 180 degrees

I've implemented a particle system. I'm drawing their textures on billboards that should be rotated towards the camera.
This works fine except for the case when the angle between particle->camera and the normal comes near to 180 degrees. Then the particle starts rotating around itself many times.
The angle is calculated using cos(angle) = dot(a, b) / (length(a) * length(b), the length are both 1 cause the Vectors are normalized.
The axis is calculated using the cross product of those two vectors.
glDisable(GL_CULL_FACE);
//calculate rotation
Vector3f normal = new Vector3f(0, 0, 1);
Vector3f dir = Vector3f.sub(new Vector3f(GraphicsData.camera.x, GraphicsData.camera.y, GraphicsData.camera.z), new Vector3f(x, y, z), null);
if(dir.length() == 0)
{
glEnable(GL_CULL_FACE);
return;
}
dir = (Vector3f) dir.normalise();
float angle = (float) Math.toDegrees(Math.acos(Vector3f.dot(normal, dir)));
Vector3f rotationAxis = Vector3f.cross(normal, dir, null);
rotationAxis = (Vector3f) rotationAxis.normalise();
System.out.println("Angle: + " + angle + " Axis: " + rotationAxis);
glBindTexture(GL_TEXTURE_2D, ParticleEngine.particleTextures.get(typeId).texture.getTextureID());
glColor4f(1f,1f,1f, time >= lifeTime - decayTime ? ((float)lifeTime - (float)time) / ((float)lifeTime - (float)decayTime) : 1f);
shaderEngine.createModelMatrix(new Vector3f(x, y, z), new Vector3f(angle * rotationAxis.x, angle * rotationAxis.y, angle * rotationAxis.z), new Vector3f(sx, sy, sz));
shaderEngine.loadModelMatrix(shaderEngine.particle);
glCallList(ParticleEngine.particleTextures.get(typeId).displayListId + textureIndex);
glEnable(GL_CULL_FACE);
What am i doing wrong calculating the rotation?
public static void createModelMatrix(Vector3f pos, Vector3f rot, Vector3f scale)
{
GraphicsData.camera.modelMatrix = new Matrix4f();
GraphicsData.camera.modelMatrix.setIdentity();
GraphicsData.camera.modelMatrix.translate(pos);
GraphicsData.camera.modelMatrix.rotate((float) Math.toRadians(rot.x), new Vector3f(1,0,0));
GraphicsData.camera.modelMatrix.rotate((float) Math.toRadians(rot.y), new Vector3f(0,1,0));
GraphicsData.camera.modelMatrix.rotate((float) Math.toRadians(rot.z), new Vector3f(0,0,1));
GraphicsData.camera.modelMatrix.scale(scale);
}
More a long comment or perhaps a partial answer to the problem:
If you are computing the cross product anyway, then use that
norm( a × b ) = sin(angle) * norm(a)*norm(b)
dot(a,b) = cos(angle) * norm(a)*norm(b)
to determine
angle = atan2( norm(a×b), dot(a,b) )

Categories

Resources