Misunderstanding of multiplyMV - OpenGL ES 2.0 - java

I wrote a simple program using OpenGL ES 2.0 and Java for Android.
This program draw a point in a random position on the screen using an ortho projection matrix, view matrix and a model matrix. In the shader I put matrix * position.
All works well, but then I tried, for testing purpose, to calculate the position of the point by myself, so I used multiplyMV and as arguments I put my MVPmatrix (obtained by using multiplyMM before between projection and view and then between the result and the model matrix) and my point (for example 2f, 3.5f, 0f, 1f). The problem is that sometimes the result I get for x and/or y is greater than 1 or smaller than -1, despite the fact that the point is on the screen. But in normalized device coordinates the point must have a range between -1 and 1 in order to be "on screen".
I really don't understand where is my mistake.

The point has to be in normalized device space [-1, 1] (for x, y, z), after the projection matrix is applied.
But note the point may be a Homogeneous coordinate. This means you have to do a Perspective divide first.
The projection matrix describes the mapping from 3D points of a scene, to 2D points of the viewport. The projection matrix transforms from view space to the clip space. The coordinates in the clip space are transformed to the normalized device coordinates (NDC) in the range (-1, -1, -1) to (1, 1, 1) by dividing with the w component of the clip coordinates.
Clip space coordinates are Homogeneous coordinates. In clipspace the clipping of the scene is performed. A point is in clip space if the x, y and z components are in the range defined by the inverted w component and the w component of the homogeneous coordinates of the point:
-p.w <= p.x, p.y, p.z <= p.w.
The clip coordinates are transformed to the normalized evice coordiantes by doing the Perspective divide:
ndc.xyz = (p.x/p.w, p.y/p.w, p.z/p.w)

Related

What is the exact difference between the camera transformation matrix and the view matrix (using OpenGL and lwjgl)

I've read a lot online before asking this question, and I came to understand that the View matrix is just the inverse of the Camera Transformation matrix. For the sake of clarity, if we treat the Camera as an actual entity that is transformed like any other 3D object in the scene (so with a transformation matrix, that first translate, then rotate and then scale the object) we obtain the camera transformation matrix which contains the position of the camera. If we invert this matrix we should obtain the view matrix, but this is not what happens in my code. I have two static methods: one that creates the transformation matrix given the position, the rotation of the 3 axes and one value of scaling that is applied to all the axes (first translate, then rotate, then scale) and another one that creates the view matrix given the Camera which has a yaw (rotation of the y axis), a pitch (rotation around the x axis) and a Vec3 which represents the position (in here we first rotate the camera and then translate it with its negative position, because moving the camera is the same as moving the world around it). Here is the code for the transformation matrix:
public static Matrix4f createTransformationMatrix(Vector3f translation, float rx, float ry,
float rz, float scale) {
Matrix4f matrix = new Matrix4f();
matrix.setIdentity();
Matrix4f.translate(translation, matrix, matrix);
Matrix4f.rotate((float)Math.toRadians(rx), new Vector3f(1, 0, 0), matrix, matrix);
Matrix4f.rotate((float)Math.toRadians(ry), new Vector3f(0, 1, 0), matrix, matrix);
Matrix4f.rotate((float)Math.toRadians(rz), new Vector3f(0, 0, 1), matrix, matrix);
Matrix4f.scale(new Vector3f(scale, scale, scale), matrix, matrix);
return matrix;
}
Here is the code for the view Matrix:
public static Matrix4f createViewMatrix(Camera camera) {
Matrix4f viewMatrix = new Matrix4f();
viewMatrix.setIdentity();
Matrix4f.rotate((float) Math.toRadians(camera.getPitch()), new Vector3f(1, 0, 0), viewMatrix, viewMatrix);
Matrix4f.rotate((float) Math.toRadians(camera.getYaw()), new Vector3f(0, 1, 0), viewMatrix, viewMatrix);
Vector3f cameraPos = camera.getPosition();
Vector3f negativeCameraPos = new Vector3f(-cameraPos.x, -cameraPos.y, -cameraPos.z);
Matrix4f.translate(negativeCameraPos, viewMatrix, viewMatrix);
return viewMatrix;
}
Here comes the problem: since I followed a tutorial on Youtube on how to build these two matrices and I did not write this code myself, I don't understand how is the viewMatrix an inversion of the camera transformation matrix. I've noticed that in createViewMatrix() we first rotate and then translate (with the negative position) while in createTransformationMatrix() we first translate then rotate then scale. So if I understand things correctly, I can create a transformation matrix with the Camera data and then invert it to obtain the View matrix, but it doesn't work. I also tried, in createViewMatrix(), to first translate with the positive position (without computing the negativeCameraPos) then rotate and then invert the matrix. Same result: it doesn't work, weird things happen when I run the program (I don't know how to explain them, but they're just wrong). I tried a lot of other things, but it only works with the code I provided. Can you explain me how first rotating and then translating with the negative camera position provides the inverted camera transformation matrix please?
I'm so sorry for the prolixity, but I want you to understand my problem at the first shot so that you can answer me. Thank you.
Your basic understand of the camera and view matrix is correct. The camera is normally used to describe the position and orientation of the viewer/camera in the world while the view matrix would be used to transform from world space to view space so it should be the inverse of the camera matrix.
Note that in matrix math there is a difference in the order in which transformations are applied: rotating and then translating is different from translating and then rotating (we'll leave scaling out of the equation here since you normally don't scale a camera - zooming would be done via the projection matrix).
When building your camera matrix you'd first rotate to set camera orientation and then translate to set camera position, i.e. you treat the camera as sitting at 0/0/0, looking along the z axis (so the view vector would be 0/0/1). After rotating you get a different normalized view vector but the camera would still "sit" at 0/0/0. Then you translate to the actual camera position (you might need additional matrix operations to calculate that position but I'd do that in a separate step for starters - until you get things right).
Can you explain me how first rotating and then translating with the negative camera position provides the inverted camera transformation matrix please?
It shouldn't as the resulting view matrix would apply a different direction. The "negative" rotation (i.e. angle +/- 180 degrees) should work though. In that case you rotate a vector to point to the camera (so if the camera turns 45 degrees around the y-axis any object "pointing to the camera" would need to rotate by 225 or -135 degrees around the same axis).
Negative translation is ok since if you move the camera to 4/3/2 in world space a translation by -4/-3/-2 would move any coordinate in world space into view space.

Computer Graphics Coordinates with glu (OpenGL)

I am having difficulty figuring out how the coordinate system in glu works, several problems to solve.
GLJPanel canvas = new GLJPanel();
frame.setSize(400,600); // Size in pixels of the frame we draw on
frame.getContentPane().add(canvas);
glu.gluOrtho2D(-100.0, 100.0, -200.0, 200.0);
gl.glViewport(100,100,200,300);
If a point has world coordinates (-50,-75), what are its coordinates in the viewport coordinate system?
and another one (not really specific code):
gluOrtho2D(-1.0, 0.0, -1.5, 0.0) and glViewport(0,300,200,300)
gluOrtho2D(0.0, 1.0, 0.0, 1.5) and glViewport(200,0,200,300)
Where would the two truncated genie curves be positioned?
Now I think I would be able to solve these, but am lost on how the coordinate system works.
The world coordinates are arbitrary, and you get to choose them. In this case, (-50, -75).
The MVP matrix and projection transformation convert these to clip space coordinates which vary from (-1, -1, -1) to (+1, +1, +1). In this case, (-0.5, -0.375). This conversion is affected by your use of gluOrtho2D(), or in more modern programs, the output of the vertex shader.
The viewport coordinates are pixels, from (100, 100) to (300, 400) in this case. You just scale the clip space coordinates to convert. The pixel centers are located at half-integer coordinates, so the lower-left pixel of the window is at (0.5, 0.5). Your point is located at (200, 193.75). This conversion is affected by the use of glViewport().
I have no idea what a "genie curve" is.

Math Vector Matrix multiplication

As far as I know the opengl Viewport's Coordinate System is 2Dimensional and ranges between -1 and 1 in x and y direction.To map a 3DVector's position from "World Space" to Viewport Coordinates you write f.e
"gl_position=uModelViewProjectionMatrix * vPosition " in your fragment shader.
My Question is how you can multiply a 3DVector by a 4D Matrix and get a 2DVector as a result and - more important - is there a funktion to do this on CPU side (especially a libary/ class for java in android)
Just clarifying a few terms:
The viewport is the region within the window you're drawing to. It's specified in pixels.
The rasterizer needs coordinates in normalized device coordinates which is -1 to 1. It then maps these to the viewport area.
gl_Position must take a 4D vector in clip space. This is the space triangles are clipped in (for example and particularly if they intersect the near plane). This is separate to normalized device coordinates because the perspective divide hasn't happened yet. Doing this yourself would be pos /= pos.w, but that loses some information OpenGL needs for clipping, depth and interpolation.
This brings me to the answer. You're correct, you can't multiply a 3D vector by a 4x4 matrix. It's actually using homogeneous coordinates and the vector is 4D with a 1 at the end. The 4D result is for clip space. The rasterizer creates fragments with just the 2D position, but w is used for perspective correct interpolation and z is interpolated for depth testing.
Finally, the ModelViewProjection matrix implies the introduction of three more spaces. These are purely convention but with good reasons to exist. Mesh vertices are given in object space. You can place objects in the world with a model transformation matrix. You provide a camera position and rotation in the world with the view matrix. The projection matrix then defines the viewing volume by scaling everything for clip space. A reason to separate the view and projection matrices is for operations in eye space such as lighting calculations.
I won't go into any more detail, but hopefully this sets you on the right track.
As far as I know the opengl Viewport's Coordinate System is 2Dimensional and ranges between -1 and 1 in x and y direction.
Sort of, but not exactly like that.
From a mathematical point of view what matters is the dimension of the kernel. When it comes to the framebuffer things don't end at the viewport coordinates. From there things get "split", the (x,y) coordinate is used to determine which fragment to touch and the (z,w) coordinates usually are used for calculations that ultimately end in the depth buffer.
My Question is how you can multiply a 3DVector by a 4D Matrix and get a 2DVector as a result
By padding the 3d vector to 4d elements; in terms of homogenous coordinates pad it with zeros except for the last element which is set to 1. This allows you to multiply with a nĂ—4 matrix. And to get back to 2d you project it down into the lower dimension vector space; this is just like a 3d object projects a 2d shadow onto a surface. The simplemost projection is simply omitting the dimensions you're not interested in, like dropping z and w when going to the viewport.
is there a function to do this on CPU side
There are several linear algebra libraries. Just pad the vectors accordingly to transform with higher dimension matrices and project them to get to lower dimensions.

Implementing trapezoidal sprites in LibGDX

I'm trying to create a procedural animation engine for a simple 2D game, that would let me create nice looking animations out of a small number of images (similar to this approach, but for 2D: http://www.gdcvault.com/play/1020583/Animation-Bootcamp-An-Indie-Approach)
At the moment I have keyframes which hold data for different animation objects, the keyframes are arrays of floats representing the following:
translateX, translateY, scaleX, scaleY, rotation (degrees)
I'd like to add skewX, skewY, taperTop, and taperBottom to this list, but I'm having trouble properly rendering them.
This was my attempt at implementing a taper to the top of the sprite to give it a trapezoid shape:
float[] vert = sprite.getVertices();
vert[5] += 20; // top-left vertex x co-ordinate
vert[10] -= 20; // top-right vertex x co-ordinate
batch.draw(texture, vert, 0, vert.length);
Unfortunately this is producing some weird texture morphing.
I had a bit of a Google and a look around StackOverflow and found this, which appears to be the problem I'm having:
http://www.xyzw.us/~cass/qcoord/
However I don't understand the maths behind it (what are s, t, r and q?).
Can someone explain it a bit simpler?
Basically, the less a quad resembles a rectangle, the worse the appearance due to the effect of linearly interpolating the texture coordinates across the shape. The two triangles that make up the quad are stretched to different sizes, so linear interpolation make the seam very noticeable.
The texture coordinates of each vertex are linearly interpolated for each fragment that the fragment shader processes. Texture coordinates typically are stored with the size of the object already divided out, so the coordinates are in the range of 0-1, corresponding with the edges of the texture (and values outside this range are clamped or wrapped around). This is also typically how any 3D modeling program exports meshes.
With a trapezoid, we can limit the distortion by pre-multiplying the texture coordinates by the width and then post-dividing the width out of the texture coordinates after linear interpolation. This is like curving the diagonal between the two triangles such that its slope is more horizontal at the corner that is on the wider side of the trapezoid. Here's an image that helps illustrate it.
Texture coordinates are usually expressed as a 2D vector with components U and V, also known as S and T. But if you want to divide the size out of the components, you need one more component that you are going to divide by after interpolation, and this is called the Q component. (The P component would be used as the third position in the texture if you were looking up something in a 3D texture instead of a 2D texture).
Now here comes the hard part... libgdx's SpriteBatch doesn't support the extra vertex attribute necessary for the Q component. So you can either clone SpriteBatch and carefully go through and modify it to have an extra component in the texCoord attribute, or you can try to re-purpose the existing color attribute, although it's stored as an unsigned byte.
Regardless, you will need pre-width-divided texture coordinates. One way to simplify this is to, instead of using the actual size of the quad for the four vertices, get the ratio of the top and bottom widths of the trapezoid, so we can treat the top parts as width of 1 and therefore leave them alone.
float bottomWidth = taperBottom / taperTop;
Then you need to modify the TextureRegion's existing texture coordinates to pre-multiply them by the widths. We can leave the vertices on the top side of the trapezoid alone because of the above simplification, but the U and V coordinates of the two narrow-side vertices need to be multiplied by bottomWidth. You would need to recalculate them and put them into your vertex array every time you change the TextureRegion or one of the taper values.
In the vertex shader, you would need to pass the extra Q component to the fragment shader. In the fragment shader, we normally look up our texture color using the size-divided texture coordinates like this:
vec4 textureColor = texture2D(u_texture, v_texCoords);
but in our case we still need to divide by that Q component:
vec4 textureColor = texture2D(u_texture, v_texCoords.st / v_texCoords.q);
However, this causes a dependent texture read because we are modifying a vector before it is passed into the texture function. GLSL provides a function that automatically does the above (and I assume does not cause a dependent texture read):
vec4 textureColor = texture2DProj(u_texture, v_texCoords); //first two components automatically divided by last component

Drawing close range GPS coordinates on a JPanel

I'm facing issue for a class where I need to convert and draw GPS coordinates dynamically into a JPanel. I have this local library data in a file that I parse. The structure is ID, County, Library Name, Latitude, Longitude.
R1,Ramsey,Maplewood,45.0327,-93.0262
R2,Ramsey,Mounds View,45.1059,-93.2104
R3,Ramsey,New Brighton,45.06604,-93.19125
R4,Ramsey,North St. Paul,45.0105,-92.9968
R5,Ramsey,Roseville,45.0072,-93.1558
R6,Ramsey,Shoreview,45.0805,-93.1345
R7,Ramsey,White Bear Lake,45.0831,-93.0092
As you can see, the locations are very close together (7 locations in 170 sq miles, Max Distance apart: 15.5 miles). My code currently can draw and link the nodes properly from hardcoded coordinates instead of the GPS data. I have found algorithms for converting the GPS lat and long to XY coordinates, but once the calculations happen, all the objects print on top of each other since the calculation leads to the same XY because of it being so close. The process needs to be dynamic because I anticipate that the test files to be use will be using 42 locations. What can I do for an equation that will give me XY coordinates that have enough variety to make a decent graphic rendering instead of using random points?
What can I do for an equation that will give me XY coordinates that have enough variety to make a decent graphic rendering instead of using random points?
Find the bounding box. In other words, find the smallest x and smallest y coordinate in your list. Find the largest x and largest y coordinate in your list. Those two points define the bounding box.
Now, translate these location x, y coordinates to drawing x, y coordinates. Your smallest location x, y coordinate becomes drawing coordinate 0, 0. Your largest location x, y coordinate becomes drawing coordinate width, height.
In order to keep the scaling from distorting the image, we have to first calculate the scaling factor for the x coordinate and the y coordinate.
scaling factor x = drawing width / (location maximum x - location minimum x)
scaling factor y = drawing height / (location maximum y - location minimum y)
Then we use the smaller of the two scaling factors.
scaling factor = Math.min(scaling factor x, scaling factor y)
The equations for converting location coordinates to drawing coordinates are:
drawing x = location x * scaling factor
drawing y = location y * scaling factor
Location and scaling factor are doubles, so you don't drop any precision. You have to convert drawing x and drawing y to integers so you can plot them.
Drawing x increases from west to east. Location x probably increases from west to east.
Drawing y increases from north to south. If location y increases from south to north, then you have to take that into account when doing the conversion.
drawing y = drawing height - drawing y
You'll probably will want to add a margin to your drawing area so that none of the locations are drawn on the edge of the drawing area.
Let's say you want a margin of 10 pixels. That would make the actual drawing area 20 pixels larger in width and 20 pixels larger in height.
You then add 10 pixels to the drawing x and the drawing y before you plot your location.
The ideal way to go about this is to find the min-longitude and min-latitude , max-longitude and max-latitude and map them to the [0,0] and [JPanels.width , Jpanels.Height].
this map can be done just
Point map(cure_location){
int X = (curr_location.longitude-min_longitude)*(scalelog);
int Y = (curr_location.latitude-min_latitude)*(scalelon);
return new Point(X,Y);
}
and scalelog and scalelon are
scalelog = (JPanels.width)/(max_longitude-min_longitude)
scalelat = (JPanels.height)/(max_latitude-min_latitude)

Categories

Resources