LookAt matrix distorts when looking up or down - java

I am using a lookAt matrix calculated in an open source math library I found for LWJGL called JOML for free cam in my game. It works well when rotating left and right, but looking up and down seems to cause major distortions issues similar to heavily increasing the FOV.
Looking straight forward:
But when looking up:
And when looking down:
I haven't been able to find someone with a similar error, and no one using JOML has reported this. I'm not the best at matrix math, so all my tries at calculating my own lookAt matrix were fails.
If someone could make a lookAt matrix using JOML, or say any one of my (most likely) possible errors, that would be much appreciated, thank you.

Well, the lookAt code provided by that library is just this (I'm leaving the actual source code out and only keep the comments, as they nicely explain the steps which are done):
public final static void lookAt(Vector3f position, Vector3f centre, Vector3f up, Matrix4f dest) {
// Compute direction from position to lookAt
// Normalize direction
// Normalize up
// right = direction x up
// up = right x direction
// Set matrix elements
}
And this code is just wrong. Interestingly, I've seen this mistake before. It is actually the same error that that the "official" gluLookAt() manpage still contains (the actual glu implementations do not have the error, just that documentation is wrong).
What this code does is building an orthonormal basis. And the problem is that the up vector is normalized before the cross product for calculating right. The assumption seems to be that when building the cross product of two unit length vectors, the result will also be a unit lenght vector. But that is a common misconception. What's actually holding true is just:
length( cross( a, b) ) == lenght(a) * length(b) * sin(alpha)
where alpha is the angle between a and b. So the unit lenght assumption only holds if the vectors are already orthogonal. As the vectors are never re-normalized after the cross product, the resulting basis is not orthonormal, but will introduce some non-uniform scaling. The lookAt assumes that the inverse rotation can be calculated by the transposed matrix, which will completely fail in this case.
The distortion you see will get more severe when the angle between the viewing direction and your up vector will move away from 90 degrees.
The correct way to deal with this is just doing the normalization at a different point. Don't normalize the up-vector before the cross-product, but normalize it's result instead. Then, you have two unit-lenght vectors orthogonal to each other, and the second cross-product will also work as expected. So the actual lookAt function should be:
// Compute direction from position to lookAt
// Normalize direction
// right = direction x up
// Normalize right
// up = right x direction
// Set matrix elements

Related

How to get the position of the point in the view after rotate?

I have a View that has the moving, rotating and scaling capability, I have all the four-corner positions and I want to get the position of some imaginary points in it so that can be constant with scaling and moving
Please take a look at the picture below to understand it well.
Edit:
After re-reading I see that question is not clear
If you need to transform points when you know initial and ending corner coordinates, then calculate matrix of affine transform using three pairs of coordinates as described here and apply that matrix to all needed points.
But you also mentioned points .. that can be constant - as far as I understand - point that preserve their coordinates after transform.
You should know matrix of affine transform that is applied to your coordinates.
For this matrix calculate eigenvalues and corresponding eigenvectors (there are up to three of them)
If some eigenvalue is 1, then static point does exist, and corresponding eigenvector being normalized to the form (x,y,1) represents coordinates of your static point.

add length to a line that passes through 2 points and get the new end point

i have been working with lwjgl and also j3d for the geometry part. i am still working on the collision. what i have so far witht he collision is working decently but there are 2 problems. to sum up my current way of colliding, it tests if the previous coordinate and current coordinate go through a triangle(what things are rendered as) and then it finds the point on the triangle that it just intersected that is closest to your current coordinate and makes you go there. it also makes your y coordinate go up by .001.
this workd descent but going up .001 is bad becuase if you go to a triangle that is at a 90* angle going updards you can go left to rigth but you cant back up out of it, almost as if you are stuck in it.
here is a drawing of how it works on imgur
http://i.imgur.com/1gMhRut.png
from here i want to add say .001 to the length between the current coordinate and the closest point (i already know these points) and get the new current point.
btw prev is where the person was at before they moved to the cur point and then it tests to see if those 2 points intersect a triangle and then i get the closest point to the prev if it does which is defined as closest in the picture. i can already calculate for all of those points
If I understand you correctly you want to add .001 to move away from the triangle. If that is the case then you need a vector of length 0.001 perpendicular to the triangle. In case of a triangle this is usually called the "normal". If you already have a normal for the triangle, then multiply that by .001 and add that. If you don't have a normal yet you can calculate it using cross product (you can Google the details of what a cross product is), something like this, from the vertices of the triangle:
Vector3 perpendicular = crossProduct(vertex3.pos - vertex1.pos, vertex2.pos - vertex1.pos);
Vector3 normal = perpendicular / length(normal);
Vector3 offset = normal * 0.001f;

Convert Latitude and Longitude values to a custom sized grid

I am making a java program that classifies a set of lat/lng coordinates to a specific rectangle of a custom size, so in effect, map the surface of the earth into a custom grid and be able to identify what rectangle/ polygon a point lies in.
The way to do this I am looking into is by using a map projection (possibly Mercator).
For example, assuming I want to classify a long/lat into 'squares' of 100m x 100m,
44.727549, 10.419704 and 44.727572, 10.420460 would classify to area X
and
44.732496, 10.528092 and 44.732999, 10.529465 would classify to area Y as they are within 100m apart.
(this assumes they lie within the same boundary of course)
Im not too worried about distortion as I will not need to display the map, but I do need to be able to tell what polygon a set of coordinates belong to.
Is this possible? Any suggestions welcome. Thanks.
Edit
Omitting projection of the poles is also an acceptable loss
Here is my final solution (in PHP), creates a bin for every square 100m :
function get_static_pointer_table_id($lat, $lng)
{
$earth_circumference = 40000; // km
$lat_bin = round($lat / 0.0009);
$lng_length = $earth_circumference * cos(deg2rad($lat));
$number_of_bins_on_lng = $lng_length * 10;
$lng_bin = round($number_of_bins_on_lng * $lng / 360);
//the 'bin' unique identifier
return $lat_bin . "_" . $lng_bin;
}
If I understand correctly, you are looking for
a way to divide the surface of the earth into approximately 100m x 100m squares
a way to find the square in which a point lies
Question 1 is mission impossible with squares but much less so with polygons. A very simple way to create the polygons would to use the coordinates themselves. If each polygon is 0.0009° in latitude and longitude, you will have approximately square 100m x 100m grid on the equator, put the slices will become very thin close to the poles.
Question 2 depends on the approximation used to solve the challenge outlined above. If you use the very simple method above, then placing each coordinate into a bin is just a division by 0.0009 (and rounding down to the closest integer).
So, first you will have to decide what you can compromise. Is it important to have equal area in the polygons, equal longitudinal distance, equal latitude distance, etc.? Is it important to have four corners in the polygon? Is it important to have similar or almost similar polygons close to the poles and close to the equator? Once you know the limitations set by your application, choosing the projection becomes easier.
What you are trying to do here is a projection onto a flat surface of an ellipsoid. So as long as your points are close together, and, well, you don't mind getting the answer slightly wrong you can assume that your projection plane intersects in the centre of your collection of points, and, each degree of lat and lon are a constant number of metres. Then the problem is a simple planar calculation.
This is wrong, of course. I would actually recommend that you look into map projections, pick one that makes sense, and go for that. Remember that you can move the centre of the projection to the centre to your set of points which will reduce distortion.
I suspect that PROJ.4 might help you in terms of libraries. There also must be a good Java one but that is not my speciality.
Finally you can could assume that the earth is a sphere and do your calculations on the sphere. Or, if you really want to get it right you can pick a standard earth ellipsoid and do the calculations on that.

LWJGL: Applying a "curve" to a landscape

I am interested in creating a shader effect similar to that of the game (don't shoot me for using this example) Animal Crossing. As you move forward and backward along the terrain, the world "curves" giving the sense of being on a round surface. The thing is, I want to apply this kind of effect to a 2D side-scroller.
Imagine a game like Terraria where both ends of the screen (left and right sides) are slightly bent downward to give the illusion of curvature.
I have tried to find an article explaining how to achieve such an effect, but I haven't much in the way of helpful direction pointing. I know this isn't the most organized or well-put question, but any help would be appreciated.
Although I am not a fan of answering my own questions, I think I have found a way to achieve this effect and would like to share. By setting up a basic vertex shader, I was able to manipulate the location of the vertex along the y-axis depending on how far away it was from the center of the screen (the origin in my case). I originally used a linear absolute value equation to see how it worked, and I got something like this:
This is obviously a strange effect, but it is getting very close to what I want to achieve. I figured I would also try leveling the effect out by dividing the absolute value of the vertices' distance from the origin by some scalar. I started with 32 and the result was much more reasonable:
As you can see, there is only a slight bend in the terrain.
This is all nice and all, but it isn't a "curve" yet. It is just an upside down 'V' with a bit of squashing done. So from here, it was easy to apply a nice curve by using a parabola and just flattening it out in the same fashion. The result was this:
The result was a very nice curve that I could modify to be any intensity I wanted. I also tried applying the graph of a 3rd degree equation, but it gave more of a try-hard 3D feel. I suppose I could apply the graph of a circle so I can accurately get the proper curve when I am on a planet with a specified radius, but I am satisfied for now.
The code turned out to be only a few lines long. Here is the GLSL code for the vertex shader:
#version 150
varying vec4 vertColor; //Just to send the color data to the fragment shader
uniform float tx; //Passed by the Java program to ensure the terrain curvature
//is sinked by with player's movement, this value is usually
//in the "lookThroughCamera" method where you handle the game
//world's translation when the player moves
void main(void) {
float v0 = gl_Vertex[1] - pow((gl_Vertex[0] + tx) / 64, 2); //this will be the new y-value for the vertex.
//It takes the original y-value and subtracts
//the absolute value of the x-value plus the offset
//on the x-axis of the player divided by 64 all to
//the power of 2. The division by 64 levels the
//curve out enough so it looks acceptable
vec4 pos = vec4(gl_Vertex[0], v0, gl_Vertex[2], gl_Vertex[3]); //create the new position with the changed y-value
gl_Position = gl_ModelViewProjectionMatrix * pos; //multiply it by your projection and modelview matrices
vertColor = gl_Color.argb; //more color stuff that has nothing to do with the rest
}
EDIT: This approach does have a serious issue though. All vertical lines will remain vertical due to the fact they are not shifted along the x-axis properly. This is shown by the following image:
This gives an extremely unnatural look, and I have yet to come up with a proper solution to this.

Calculating a point in 3D space

I am trying to locate a point in 3D space relative to the origin (0,0,0). I have 3 values to calculate this point with: a rotation in degrees about both the x and y axis as well as a "view distance". Using these values, how can I locate a point in 3D space relative to the origin? I have tried using basic trigonometric functions, but the results seem to be random. The image below gives a visual as to what needs to be done.
'vd' being the "view distance"
'c' being a value holder
'(x,y,z)' being the coordinate I am trying to find
What I am trying to do is find the point a player is looking at a certain distance away (find a point in the direct line of sight of the player out a certain distance). Keep in mind, the rotations about the x and y axis are constantly changing, but the view distance remains the same. If anyone has any suggestions, methods of how to do this, or needs clarification, please comment/answer below.
I am doing this in LWJGL, and the code I am using is as follows:
float c = (float)(Math.cos(Math.toRadians(A00.rot.y)) * view_distance);
locate.y = (float)(Math.sin(Math.toRadians(rot.y)) * view_distance);
locate.x = (float)(Math.cos(Math.toRadians(rot.x)) * c);
locate.z = (float)(Math.sin(Math.toRadians(rot.x)) * c);
EDIT:
My issue is that this current setup does NOT work for some reason. The math seems legitimate to me, but I must have something wrong somewhere in the actual setup of the graph..
I suggest looking up quaternions. No need to fully understand how they work. You can find ready made classes for java available on the internet as well. Quaternions allow you to represent a rotation in 3D space.
What I would then do, is to start with a vector representing the direction pointing forwards from the origin, and apply the same rotation that the player currently has to it. Now it is pointing in the same direction as the player. Now if you take the player's current point, and the direction vector we now have a ray describing where the player is looking at.
I suggest this link for further information on quaternions. They may look complex but, as I said, you don't need to fully understand how and why they work to be able to use them. Just copy the formulae and learn how they are used. Once you figure out how to use them, they make 3d rotations really easy.
http://content.gpwiki.org/index.php/OpenGL:Tutorials:Using_Quaternions_to_represent_rotation

Categories