I have two objects that I want to parent together so that Tri is a child of Torus. When I do so and multiply the matricies together by adding the parents modelView to the childs, the child jumps in space initially, over to the right and up by a few units. Where do I insert an offset into this, and how do I calculate it?
obj = make_shared<Object>(*this);
obj->rename("tri");
obj->type->val_s = "tri";
obj->t->val_3 = glm::vec3(-4.f, 1.5f, 0.f);
allObj.push_back(obj);
obj = make_shared<Object>(*this);
obj->rename("torus");
obj->type->val_s = "obj";
obj->t->val_3 = glm::vec3(3.f, 2.f, 0.f);
allObj.push_back(obj);
//Matrix
scaleM = glm::scale(glm::mat4(), s->val_3);
rotationM = glm::toMat4(r_quat);
glm::vec3 usablePivot = t->val_3 - pivot->val_3;
glm::mat4 localAxis1M = glm::translate(glm::mat4(), usablePivot);
glm::mat4 localAxis2M = glm::translate(glm::mat4(), -usablePivot);
translationM = glm::translate(glm::mat4(), t->val_3);
modelM = translationM * localAxis2M * rotationM * scaleM * localAxis1M;
//View
usableView = myGL->ViewM;
//Projection
usableProjection = myGL->ProjectionM;
//MVP
if (parent == "world") { MVP = usableProjection * usableView * modelM; }
else { MVP = usableProjection * usableView * parentTo->modelM * modelM; }
Matrix order in mine OpenGL apps:
sub-object matrices
the lowest in ownership hierarchy are first
they are local to their parents
their have offset to point (0,0,0) of parent space
if not then and they are local to world then this is the jump reason
in that case the sub-objects are not really sub objects
and handle them as normal object without parent
object to world
is transform matrix of objects without parent
they are local to your scene world
then goes camera view
it is inverse of camera space
where Z-axis is view direction
last is projection
like gluPerspective(...)
this one is usually stored in GL_PROJECTION matrix on fixed pipeline
instead of GL_MODELVIEW
Clipping is done by OpenGL separately via glViewport
(your usable view I think)
Look for more info here
you can check the content of your matrices
look at positions 12,13,14 where the offset vector is stored
do not forget that this vector is local to parent coordinate system!!!
so it is more likely rotated by it ...
also a good idea is to draw axis lines for each tested matrix in its parent space
to see if they are correct
I use red,gree,blue lines for x,y,z - axis
just extract origin of coordinate system [12,13,14]
and draw line from it to the same point + a*axis vector
a is line length (big enough so you see a line not a point)
axis vectors are at positions x=[0,1,2], y=[4,5,6], z=[8,9,10]
do not forget to set matrices to parent coordinate system !!!
if you handle matrices your self via GLSL then do not forget that
direction vectors like Normals are transformed without offset
so get the whole transform matrix (all multilicated together without projection and sometimes also camera)
set the offset to zero [12,13,14]=(0.0,0.0,0.0)
and multiply by this matrix
Related
I'm currently working on a OpenGL FrameWork/Engine and as far as the OpenGL part goes, I'm quite satisfied with my results.
On the other hand I have a serious problem getting a Camera to work.
Moving along the Z-Axis works well, but as soon as I start to strafe (moving along the X-Axis), the whole Scene get screwed.
You can see the result of strafing in the image below.
The left part shows the actual scene, the right part shows the scene resulting from a strafe movement.
My code is the following.
In Constructor:
//Info is a Struct with Default values
m_projectionMatrix = glm::perspective(
info.fieldOfView, width / height, //info.fov = 90
info.nearPlane, info.farPlane // info.near = 0.1f, info.far = 1000
);
//m_pos = glm::vec3(0.0f,0.0f,0.0f), info.target = glm::vec3(0.0f, 0.0f, -1.0f)
m_viewMatrix = glm::lookAt(m_pos, m_pos + info.target, Camera::UP);
//combine projection and view
m_vpMatrix = m_projectionMatrix * m_viewMatrix;
In the "Update"-Method I'm currently doing the following:
glm::mat4x4 Camera::GetVPMatrix()
{
m_vpMatrix = glm::translate(m_vpMatrix, m_deltaPos);
return m_vpMatrix;
}
As far as i know:
The projection matrix achieves the actual perspective view. The view matrix, initially, translates and rotates the whole scene, that it is centered?
So why translating the VP-Matrix by any Z-Value works just fine, but by an X-Value doesn't?
I would like to achive a camera behaviour like this:
Initial Cam Pos is (0,0,0) and "Center" is e.g. (0,0,-1).
Then after Translation by X = 5: Cam Pos is (5,0,0) and Center is (5,0,-1).
Edit: Additional Question.
Why is the Z-Coordinate affekted by VP-Transformation?
Thanks for any help!
Best regards, Christoph.
Okay, I finally got the solution... As you can see, I am using GLM for my matrix math. GLM stores its matrices values in column major order. Open GL wants column major ordered matrices, too. C/C++ native 2d Array layout is row major, so most of the available OpenGL/C++ tutorials state, that one should use
glUniformMatrix4fv(location, 1, GL_TRUE, &mat[0][0]);
With GL_TRUE meaning, that the matrix should be converted (transposed?) from row major to column major order. Because of my matrices already beeing in column major format, that makes absolutely no sense...
Changing the above to
glUniformMatrix4fv(location, 1, GL_FALSE, &mat[0][0]);
fixed my problem...
Matrix math is not my strong point so I can't explain why your current approach doesn't work, though I have my suspicions (translating the projection matrix doesn't seem right). The following should allow you to move the camera though:
// update your camera position
m_pos = new_pos;
// update the view matrix
m_viewMatrix = glm::lookAt(m_pos, m_pos + info.target, Camera::UP);
// update the view-projection matrix, projection matrix never changes
m_vpMatrix = m_projectionMatrix * m_viewMatrix;
For picking objects, I've implemented a ray casting algorithm similar to what's described here. After converting the mouse click to a ray (with origin and direction) the next task is to intersect this ray with all triangles in the scene to determine hit points for each mesh.
I have also implemented the triangle intersection test algorithm based on the one described here. My question is, how should we account for the objects' transforms when performing the intersection? Obviously, I don't want to apply the transformation matrix to all vertices and then do the intersection test (too slow).
EDIT:
Here is the UnProject implementation I'm using (I'm using OpenTK by the way). I compared the results, they match what GluUnProject gives me:
private Vector3d UnProject(Vector3d screen)
{
int[] viewport = new int[4];
OpenTK.Graphics.OpenGL.GL.GetInteger(OpenTK.Graphics.OpenGL.GetPName.Viewport, viewport);
Vector4d pos = new Vector4d();
// Map x and y from window coordinates, map to range -1 to 1
pos.X = (screen.X - (float)viewport[0]) / (float)viewport[2] * 2.0f - 1.0f;
pos.Y = 1 - (screen.Y - (float)viewport[1]) / (float)viewport[3] * 2.0f;
pos.Z = screen.Z * 2.0f - 1.0f;
pos.W = 1.0f;
Vector4d pos2 = Vector4d.Transform(pos, Matrix4d.Invert(GetModelViewMatrix() * GetProjectionMatrix()));
Vector3d pos_out = new Vector3d(pos2.X, pos2.Y, pos2.Z);
return pos_out / pos2.W;
}
Then I'm using this function to create a ray (with origin and direction):
private Ray ScreenPointToRay(Point mouseLocation)
{
Vector3d near = UnProject(new Vector3d(mouseLocation.X, mouseLocation.Y, 0));
Vector3d far = UnProject(new Vector3d(mouseLocation.X, mouseLocation.Y, 1));
Vector3d origin = near;
Vector3d direction = (far - near).Normalized();
return new Ray(origin, direction);
}
You can apply the reverse transformation of each object to the ray instead.
I don't know if this is the best/most efficient approach, but I recently implemented something similar like this:
In world space, the origin of the ray is the camera position. In order to get the direction of the ray, I assumed the user had clicked on the near plane of the camera and thus applied the 'reverse transformation' - from screen space to world space - to the screen space position
( mouseClick.x, viewportHeight - mouseClick.y, 0 )
and then subtracted the origin of the ray, i.e. the camera position, from
the now transformed mouse click position.
In my case, there was no object-specific transformation, meaning I was done once I had my ray in world space. However, transforming origin & direction with the inverse model matrix would have been easy enough after that.
You mentioned that you tried to apply the reverse transformation, but that it didn't work - maybe there's a bug in there? I used a GLM - i.e. glm::unProject - for this.
New to OpenGl I am trying to make sure i did this part right, im told to build the world matrix from the position, scaling and rotation information.
From the material i found online my understanding is
p^world = p^worldâp^Model
P^Model = Scaling * Rotation * Translation
Therefore i coded the following:
glm::mat4 Model::GetWorldMatrix() const
{
// #TODO 2, you must build the world matrix from the position, scaling and rotation informations
glm::mat4 pModel = GetScaling() * GetRotationAngle() * GetPosition();
glm::mat4 worldMatrix(1.0f);
worldMatrix = worldMatrix* pModel;
// #TODO 4 - Maybe you should use the parent world transform when you do hierarchical modeling
return worldMatrix;
}
void Model::SetPosition(glm::vec3 position)
{
mPosition = position;
}
void Model::SetScaling(glm::vec3 scaling)
{
mScaling = scaling;
}
void Model::SetRotation(glm::vec3 axis, float angleDegrees)
{
mRotationAxis = axis;
mRotationAngleInDegrees = angleDegrees;
}
Is this correct?? Thank you for your time and help.
The way to do it is to save one 4x4 Matrix for every Model. This matrix is although called the ModelMatrix.
All important informations (position,rotation,scale) of the object are saved in this matrix. If you want to translate,rotate or scale your object you generate a transformation matrix and multiply it from the left side to your model matrix. model = trafo * model; You can generate these transformation matrices with GLM http://glm.g-truc.net/0.9.2/api/a00245.html .
I needed to implement 'choosing an object' in a 3D environment. So instead of going with robust, accurate approach, such as raycasting, I decided to take the easy way out. First, I transform the objects world position onto screen coordinates:
glm::mat4 modelView, projection, accum;
glGetFloatv(GL_PROJECTION_MATRIX, (GLfloat*)&projection);
glGetFloatv(GL_MODELVIEW_MATRIX, (GLfloat*)&modelView);
accum = projection * modelView;
glm::mat4 transformed = accum * glm::vec4(objectLocation, 1);
Followed by some trivial code to transform the opengl coordinate system to normal window coordinates, and do a simple distance from the mouse check. BUT that doesn't quite work. In order to translate from world space to screen space, I need one more calculation added on to the end of the function shown above:
transformed.x /= transformed.z;
transformed.y /= transformed.z;
I don't understand why I have to do this. I was under the impression that, once one multiplied your vertex by the accumulated modelViewProjection matrix, you had your screen coordinates. But I have to divide by Z to get it to work properly. In my openGL 3.3 shaders, I never have to divide by Z. Why is this?
EDIT: The code to transform from from opengl coordinate system to screen coordinates is this:
int screenX = (int)((trans.x + 1.f)*640.f); //640 = 1280/2
int screenY = (int)((-trans.y + 1.f)*360.f); //360 = 720/2
And then I test if the mouse is near that point by doing:
float length = glm::distance(glm::vec2(screenX, screenY), glm::vec2(mouseX, mouseY));
if(length < 50) {//you can guess the rest
EDIT #2
This method is called upon a mouse click event:
glm::mat4 modelView;
glm::mat4 projection;
glm::mat4 accum;
glGetFloatv(GL_PROJECTION_MATRIX, (GLfloat*)&projection);
glGetFloatv(GL_MODELVIEW_MATRIX, (GLfloat*)&modelView);
accum = projection * modelView;
float nearestDistance = 1000.f;
gameObject* nearest = NULL;
for(uint i = 0; i < objects.size(); i++) {
gameObject* o = objects[i];
o->selected = false;
glm::vec4 trans = accum * glm::vec4(o->location,1);
trans.x /= trans.z;
trans.y /= trans.z;
int clipX = (int)((trans.x+1.f)*640.f);
int clipY = (int)((-trans.y+1.f)*360.f);
float length = glm::distance(glm::vec2(clipX,clipY), glm::vec2(mouseX, mouseY));
if(length<50) {
nearestDistance = trans.z;
nearest = o;
}
}
if(nearest) {
nearest->selected = true;
}
mouseRightPressed = true;
The code as a whole is incomplete, but the parts relevant to my question works fine. The 'objects' vector contains only one element for my tests, so the loop doesn't get in the way at all.
I've figured it out. As Mr David Lively pointed out,
Typically in this case you'd divide by .w instead of .z to get something useful, though.
My .w values were very close to my .z values, so in my code I change the statement:
transformed.x /= transformed.z;
transformed.y /= transformed.z;
to:
transformed.x /= transformed.w;
transformed.y /= transformed.w;
And it still worked just as before.
https://stackoverflow.com/a/10354368/2159051 explains that division by w will be done later in the pipeline. Obviously, because my code simply multiplies the matrices together, there is no 'later pipeline'. I was just getting lucky in a sense, because my .z value was so close to my .w value, there was the illusion that it was working.
The divide-by-Z step effectively applies the perspective transformation. Without it, you'd have an iso view. Imagine two view-space vertices: A(-1,0,1) and B(-1,0,100).
Without the divide by Z step, the screen coordinates are equal (-1,0).
With the divide-by-Z, they are different: A(-1,0) and B(-0.01,0). So, things farther away from the view-space origin (camera) are smaller in screen space than things that are closer. IE, perspective.
That said: if your projection matrix (and matrix multiplication code) is correct, this should already be happening, as the projection matrix will contain 1/Z scaling components which do this. So, some questions:
Are you really using the output of a projection transform, or just the view transform?
Are you doing this in a pixel/fragment shader? Screen coordinates there are normalized (-1,-1) to (+1,+1), not pixel coordinates, with the origin at the middle of the viewport. Typically in this case you'd divide by .w instead of .z to get something useful, though.
If you're doing this on the CPU, how are you getting this information back to the host?
I guess it is because you are going from 3 dimensions to 2 dimensions, so you are normalizing the 3 dimension world to a 2 dimensional coordinates.
P = (X,Y,Z) in 3D will be q = (x,y) in 2D where x=X/Z and y = Y/Z
So a circle in 3D will not be circle in 2D.
You can check this video out:
https://www.youtube.com/watch?v=fVJeJMWZcq8
I hope I understand your question correctly.
I'm trying to implement skeletal animation in a small program I'm writing. The idea is to calculate the transformation matrix on the CPU every frame by interpolating keyframe data, then feeding this data to my vertex shader which multiplies my vertices by this matrix like this:
vec4 v = animationMatrices[int(boneIndices.x)] * gl_Vertex * boneWeights.x;
Where boneWeights and boneIndices are attributes and animationMatrices is a uniform array of transformation matrices updated every frame before drawing. (The idea is to have multiple bones affecting one vertex eventually, but right now I'm testing with one bone per vertex so just taking the weight.x and indices.x is enough).
Now the problem is calculating the transformation matrix for each bone. My transformation matrix for the single joint is good, the problem is that it always takes (0,0,0) as pivot instead of the pivot. I took the joint matrices from the COLLADA which correctly shows my skeleton when I draw them like this:
public void Draw()
{
GL.PushMatrix();
drawBone(Root);
GL.PopMatrix();
}
private void drawBone(Bone b)
{
GL.PointSize(50);
GL.MultMatrix(ref b.restMatrix);
GL.Begin(BeginMode.Points);
GL.Color3((byte)0, (byte)50, (byte)0);
if (b.Name == "Blades")
{
GL.Vertex3(0, 0, 0);
}
GL.End();
foreach (Bone bc in b.Children)
{
GL.PushMatrix();
drawBone(bc);
GL.PopMatrix();
}
}
So now to calculate the actual matrix I've tried:
Matrix4 jointMatrix = b.restMatrixInv * boneTransform * b.restMatrix;
or according to the collada documentation (this doesn't really make sense to me):
Matrix4 jointMatrix = b.restMatrix * b.restMatrixInv * boneTransform;
And I know I also have to put the parent matrix in here somewhere, I'm guessing something like this:
Matrix4 jointMatrix = b.restMatrixInv * boneTransform * b.restMatrix * b.Parent.jointMatrix;
But at the moment I'm mostly just confused and any push in the right direction would help. I really need to get this order right...