OpenGL : how to get the matrix that mirror the Scene? - c++

Context:
I try to make a mirror in OpenGL.
So what I need to define a mirror is a plane, defined by a point p and two normal vectors n1 and n2. n3=n1^n2 defines a vector oriented to the viewer of the mirror (the sign is important to discard fragments that are behind the mirror and also would be reflected in front of the mirror). n1, n2 and n3 define a basis in the coordinates of the mirror (with origin p an arbitrary point located on the mirror plane).
Now, to draw the reflection of the scene I need the matrix reflectionMatrix that transform any point into its mirrored. I don't even know if it's possible, but what I tried looks ok but there is problems when the mirror is very close to the point.
I tried to use transition matrix used in linear algebra, but I don't even know if I can use them with transformation matrix. I am also a bit confused about the transformation order.
The code is that :
/// #brief Compute change-of-basis matrix.
/// From basis {e1, e2, e3} to canonical basis of R^3.
mat4 transition(const glm::vec3& e1, const glm::vec3& e2, const glm::vec3& e3)
{
// GLM is column major: mat4(col1, col2, col3, col4)
return {
vec4(e1, 0),
vec4(e2, 0),
vec4(e3, 0),
vec4(vec3(0), 1)
};
}
glm::mat4 getReflectionMatrix() {
const glm::vec3 mirrorPos{scene.mirrorPos};
const glm::vec3 n1{1, 0, 0};
const glm::vec3 n2{0, 1, 0};
const glm::vec3 n3{glm::cross(n1, n2)};
// n1, n2 are orthogonal vectors of the mirror
// n3 is to complete the basis
// the vertices should be in trigonometric order to work
glm::mat4 reflectionMatrix{1};
// Let p be the point we mirror
// Compute p1 = p relative to the mirror [Translation in global coordinate system ~= premultiply]
reflectionMatrix = glm::translate(reflectionMatrix, -mirrorPos);
// Compute p2 = same as p1 but in coordinate system {n1, n2, n3}
reflectionMatrix *= glm::inverse(transition(n1, n2, n3));
// Compute p3 = (p2.x, p2.y, -p2.z) to go into the mirror (z is orthogonal distance from mirror)
reflectionMatrix = glm::scale(reflectionMatrix, {1, 1, -1});
// Compute p4 = same as p3 but in canonical coordinate system (again relative to the mirror)
reflectionMatrix *= transition(n1, n2, n3);
// Compute p5 = p4 from relative to the mirror to global space [Translation in global coordinate system]
reflectionMatrix = glm::translate(reflectionMatrix, mirrorPos);
return reflectionMatrix;
}
What is wrong with the reflection matrix? Or is there a simpler way

Related

Determining rotation matrix about an axis for a given angle

I've been trying to understand matrices and vectors and implemented Rodrigue's rotation formula to determine the rotation matrix about an axis for a given angle. I've got function Transform which calls out to function Rotate.
// initial values of eye ={0,0,7}
//initial values of up={0,1,0}
void Transform(float degrees, vec3& eye, vec3& up) {
vec3 axis = glm::cross(glm::normalize(eye), glm::normalize(up));
glm::normalize(axis);
mat3 resultRotate = rotate(degrees, axis);
eye = eye * resultRotate;
glm::normalize(eye);
up = up * resultRotate;`enter code here`
glm::normalize(up);
}
mat3 rotate(const float degrees, const vec3& axis) {
//Implement Rodrigue's axis-angle rotation formula
float radDegree = glm::radians(degrees);
float cosValue = cosf(radDegree);
float minusCos = 1 - cosValue;
float sinValue = sinf(radDegree);
float cartesianX = axis.x;
float cartesianY = axis.y;
float cartesianZ = axis.z;
mat3 myFinalResult = mat3(cosValue +(cartesianX*cartesianX*minusCos), ((cartesianX*cartesianY*minusCos)-(cartesianZ*sinValue)),((cartesianX*cartesianZ*minusCos)+(cartesianY*sinValue)),
((cartesianX*cartesianY*minusCos)+(cartesianZ*sinValue)), (cosValue+(cartesianY*cartesianY*minusCos)), ((cartesianY*cartesianZ*minusCos) - (cartesianX*sinValue)),
((cartesianX*cartesianZ*minusCos)-(cartesianY*sinValue)), ((cartesianY*cartesianZ*minusCos) + (cartesianX*sinValue)), ((cartesianZ*cartesianZ*minusCos) + cosValue));
return myFinalResult;
}
All the values, resultant rotation matrix and the changed vectors are as expected for +angle of rotation, but wrong for negative angles and from then on, has cascading effect until the all the vectors are re-initialised. Can someone please help me figure out the problem? I cannot use any inbuilt functions like glm::rotate.
I do not use Rodrigues_rotation_formula because it needs to compute a system of equation on runtime and gets very complicated in higher dimensions.
Instead I am using axis aligned incremental rotations along with 4x4 homogenous transform matrices which are really easily portable to higher dimensions like 4D rotors.
Now there are local and global rotations. Local rotations will rotate around your matrix coordiante system local axises and global ones will rotate around world (or main coordinate system)
What you want is create a transform matrix around some point,axis and angle. To do that just:
create a transform matrix A
that has one axis aligned to axis of rotation and origin is center of rotation. To construct such matrix you need 2 perpendicular vectors which are easily obtainable from cross product.
rotate A around its local axis aligned to axis of rotation by angle
by simple multiplication of A by axis aligned incremental rotation R so
A*R;
revert the original transform of A before rotation
by simply multiplying inverse of A to the result so
A*R*Inverse(A);
apply this on matrix M you want to rotate
also by simply multiplying this to M so:
M*=A*R*Inverse(A);
And that is it... Here 3D OBB approximation you can find function :
template <class T> _mat4<T> rotate(_mat4<T> &m,T ang,_vec3<T> p0,_vec3<T> dp)
{
int i;
T c=cos(ang),s=sin(ang);
_vec3<T> x,y,z;
_mat4<T> a,_a,r=mat4(
1, 0, 0, 0,
0, c, s, 0,
0,-s, c, 0,
0, 0, 0, 1);
// basis vectors
x=normalize(dp); // axis of rotation
y=_vec3<T>(1,0,0); // any vector non parallel to x
if (fabs(dot(x,y))>0.75) y=_vec3<T>(0,1,0);
z=cross(x,y); // z is perpendicular to x,y
y=cross(z,x); // y is perpendicular to x,z
y=normalize(y);
z=normalize(z);
// feed the matrix
for (i=0;i<3;i++)
{
a[0][i]= x[i];
a[1][i]= y[i];
a[2][i]= z[i];
a[3][i]=p0[i];
a[i][3]=0;
} a[3][3]=1;
_a=inverse(a);
r=m*a*r*_a;
return r;
};
That does exactly that. Where m is original matrix to transform (and returns the rotated one), ang is signed angle in [rad], p0 is center of rotation and dp is axis of rotation direction vector.
This approach does not have any singularities nor problems to rotate by negative angles ...
If you want to use this with glm or any other GLSL like math just change the templates to what you use so float,vec3,mat4 instead of T,_vec3<T>,mat4<T>.

Rotate a std::vector<Eigen::Vector3d> as a rigid transformation?

I have a few 3d points, stored in a std::vector<Eigen::Vector3d>. I need to rigidly rotate and translate these points, without changing their relationship to one another. As if moving the cloud as a whole.
Based on this question:
https://stackoverflow.com/questions/50507665/eigen-rotate-a-vector3d-with-a-quaternion
I have this code:
std::vector<Eigen::Vector3d> pts, ptsMoved;
Eigen::Quaterniond rotateBy = Eigen::Quaterniond(0.1,0.5,0.08,0.02);
Eigen::Vector3d translateBy(1, 2.5, 1.5);
for (int i = 0; i < pts.size(); i++)
{
//transform point
Vector3d rot = rotateBy * (pts[i] + translateBy);
ptsMoved.push_back(rot);
}
When i view the points and compare them to the original points, however, I get this: (White are the original, green are the transformed).
What i expect, is the cloud as a whole to look the same, just in a different position and orientation. What i get, is a moved and rotated and scaled cloud, that looks different to the original. What am i doing wrong?
EDIT:
If i apply the inverse transform to the adjusted points, using:
std::vector<Eigen::Vector3d> pntsBack;
for (int i = 0; i < ptsMoved.size(); i++)
{
//transform point
Vector3d rot = rotateBy.inverse() * (ptsMoved[i] - translateBy);
pntsBack.push_back(rot);
}
It gives me an even worse result. (dark green = original points, white = transformed, light green = transformed inverse)
Your Quaternion is not a unit-Quaternion, therefore you will get unspecified results.
If you are not sure your quaternion is normalized, just write
rotateBy.normalize();
before using it. Additionally, if you want to rotate more than one vector it is more efficient to convert the Quaternion to a rotation matrix:
Eigen::Matrix3d rotMat = rotateBy.toRotationMatrix();
// ...
// inside for loop:
Vector3d rot = rotMat * (ptsMoved[i] - translateBy);
Also, instead of .inverse() you can use .conjugate() for unit quaternions and .adjoint() or .transpose() for orthogonal Matrices.

Rotate object with keys [duplicate]

i am trying to implement functions, where i can rotate/ translate an object in local or global orientation, like in 3D modeling software, using glm. Something like this:
void Rotate(float x, float y, float z, bool localOrientation);
but I dont know how to get it working. Local rotation rotation should just be something like this(?):
m_Orientation *= glm::rotate(x, glm::vec3(1,0,0);
m_Orientation *= glm::rotate(y, glm::vec3(0,1,0);
m_Orientation *= glm::rotate(z, glm::vec3(0,0,1);
// (m_Orientation is glm::mat4)
But how to combine this with local orientation? Actually i need to rotate the rotation matrix in world orientation, right?
I hope you know what i mean with local and global oriented rotation/translation, like it is in 3D modeling programs. In most of them you have a button to switch between local and global.
And how would i calculating the forward/right/up vector then?
normally it should be something like this, right?:
forward = m_Orientation * glm::vec4(0,0,-1,0);
I tried global rotation with this:
m_GlobalOrientation = glm::rotate(m_GlobalRotation.x, glm::vec(1,0,0);
m_GlobalOrientation *= glm::rotate(m_GlobalRotation.y, glm::vec(0,1,0);
m_GlobalOrientation *= glm::rotate(m_GlobalRotation.z, glm::vec(0,0,1);
but then only x rotation is in global orientation, y and z rotation is in local orientation, since it is already rotated around x axis. So I need to rotate all 3 angles at once(?)
Translating local should just be adding translation values to current translation, and local translation should be glm::inverse(m_Orientation) * translationVector right?
Before I come to your question, let me explain some core concepts of matrices.
Assume that we have the following matrix:
wher T is a translation and R is a rotation matrix.
When we use this matrix to transform a vertex (or even mesh), there is one unique result. However, we can get to this result with the help of two interpretations:
Interpretation 1: Evaluate from right to left
If we evaluate the matrix from right to left, all transformations are performed in the global coordinate system. So if we transform a triangle that sits at the origin, we get the following result:
Interpretation 2: Evaluate from left to right
In the other case, all transformations are performed in the local coordinate system:
Of course, we get the same result.
So coming back to your question. If you store the position and orientation of the object as a matrix T. You can rotate this object in its local coordinate system by multiplying a rotation matrix to the right side of the current matrix. And in the global system by multiplying it at the left side. The same applies for translation:
void Rotate(float x, float y, float z, bool localOrientation)
{
auto rotationMatrix = glm::rotate(x, glm::vec3(1,0,0));
rotationMatrix *= glm::rotate(y, glm::vec3(0,1,0));
rotationMatrix *= glm::rotate(z, glm::vec3(0,0,1));
if(localOrientation)
this->T = this->T * rotationMatrix;
else
this->T = rotationMatrix * this->T;
}
The right / forward / up vectors are the column vectors of the matrix T. You can either read them directly or get them by multiplying the matrix with (1, 0, 0, 0) (right), (0, 1, 0, 0) (up), (0, 0, 1, 0) (for/backward) or (0, 0, 0, 1) (position).
If you want to read more about this, take a look at my blog article about matrices in DirectX. But it's for DirectX, which uses transposed matrices. Therefore the matrix order is reversed. Watch out for that when reading the article.

Rotate and translate object in local and global orientation using glm

i am trying to implement functions, where i can rotate/ translate an object in local or global orientation, like in 3D modeling software, using glm. Something like this:
void Rotate(float x, float y, float z, bool localOrientation);
but I dont know how to get it working. Local rotation rotation should just be something like this(?):
m_Orientation *= glm::rotate(x, glm::vec3(1,0,0);
m_Orientation *= glm::rotate(y, glm::vec3(0,1,0);
m_Orientation *= glm::rotate(z, glm::vec3(0,0,1);
// (m_Orientation is glm::mat4)
But how to combine this with local orientation? Actually i need to rotate the rotation matrix in world orientation, right?
I hope you know what i mean with local and global oriented rotation/translation, like it is in 3D modeling programs. In most of them you have a button to switch between local and global.
And how would i calculating the forward/right/up vector then?
normally it should be something like this, right?:
forward = m_Orientation * glm::vec4(0,0,-1,0);
I tried global rotation with this:
m_GlobalOrientation = glm::rotate(m_GlobalRotation.x, glm::vec(1,0,0);
m_GlobalOrientation *= glm::rotate(m_GlobalRotation.y, glm::vec(0,1,0);
m_GlobalOrientation *= glm::rotate(m_GlobalRotation.z, glm::vec(0,0,1);
but then only x rotation is in global orientation, y and z rotation is in local orientation, since it is already rotated around x axis. So I need to rotate all 3 angles at once(?)
Translating local should just be adding translation values to current translation, and local translation should be glm::inverse(m_Orientation) * translationVector right?
Before I come to your question, let me explain some core concepts of matrices.
Assume that we have the following matrix:
wher T is a translation and R is a rotation matrix.
When we use this matrix to transform a vertex (or even mesh), there is one unique result. However, we can get to this result with the help of two interpretations:
Interpretation 1: Evaluate from right to left
If we evaluate the matrix from right to left, all transformations are performed in the global coordinate system. So if we transform a triangle that sits at the origin, we get the following result:
Interpretation 2: Evaluate from left to right
In the other case, all transformations are performed in the local coordinate system:
Of course, we get the same result.
So coming back to your question. If you store the position and orientation of the object as a matrix T. You can rotate this object in its local coordinate system by multiplying a rotation matrix to the right side of the current matrix. And in the global system by multiplying it at the left side. The same applies for translation:
void Rotate(float x, float y, float z, bool localOrientation)
{
auto rotationMatrix = glm::rotate(x, glm::vec3(1,0,0));
rotationMatrix *= glm::rotate(y, glm::vec3(0,1,0));
rotationMatrix *= glm::rotate(z, glm::vec3(0,0,1));
if(localOrientation)
this->T = this->T * rotationMatrix;
else
this->T = rotationMatrix * this->T;
}
The right / forward / up vectors are the column vectors of the matrix T. You can either read them directly or get them by multiplying the matrix with (1, 0, 0, 0) (right), (0, 1, 0, 0) (up), (0, 0, 1, 0) (for/backward) or (0, 0, 0, 1) (position).
If you want to read more about this, take a look at my blog article about matrices in DirectX. But it's for DirectX, which uses transposed matrices. Therefore the matrix order is reversed. Watch out for that when reading the article.

Compute mesh vertices from a Plane

I would like to draw my Plane with OpenGL to debug my program but I don't know how I can do that (I'm not very good in math).
I've got a Plane with 2 attributs:
A constant
A normal
Here is what i've got:
////////////////////////////////////////////////////////////
Plane::Plane( const glm::vec3& a, const glm::vec3& b, const glm::vec3& c )
{
glm::vec3 edge1 = b - a;
glm::vec3 edge2 = c - a;
this->normal = glm::cross(edge1, edge2);
this->constant = -glm::dot( this->normal, a );
this->normalize();
}
////////////////////////////////////////////////////////////
Plane::Plane( const glm::vec4& values )
{
this->normal = glm::vec3( values.x, values.y, values.z );
this->constant = values.w;
}
////////////////////////////////////////////////////////////
Plane::Plane( const glm::vec3& normal, const float constant ) :
constant (constant),
normal (normal)
{
}
////////////////////////////////////////////////////////////
Plane::Plane( const glm::vec3& normal, const glm::vec3& point )
{
this->normal = normal;
this->constant = -glm::dot(normal, point);
this->normalize();
}
I would like to draw it to see if everything is ok. How I can do that?
(I need to compute vertices and indices to draw it)
When you want to draw, you need to find two vectors that are perpendecular to normal and a point on the plane. That's not so hard. First, let's get a vector that is not normal. Call it some_vect. For example:
if normal == [0, 0, 1]
some_vect = [0, 1, 0]
else
some_vect = [0, 0, 1]
Then, calculating vect1 = cross(normal, some_vect) would give you a vector perpendecular to normal. Calculating vect2 = cross(normal, vect1) would give you another vector that is perpendecular to normal.
Having two perpendecular vectors vect1 and vect2 and one point on the plane, drawing the plane becomes trivial. For example the sqaure with the following four points (remember to normalize the vectors):
point + vect1 * SIZE
point + vect2 * SIZE
point - vect1 * SIZE
point - vect2 * SIZE
where point is a point on the plane. If your constant is distance from the origin, then one point would be constant * normal.
The difficulty with drawing a plane is that it's an infinite surface; i.e. by definition it has no edges or vertices. If you want to show the plane in a typical polygonal fashion then you'll have to crop it to a particular area, such as a square.
A fairly easy approach is this:
Pick an arbitrary unit vector which is perpendicular to the normal. Store it as v1.
Use the cross product of v1 and the plane normal to get v2.
Negate v1 to get v3.
Negate v2 to get v4.
The points v1-4 now express the four corners of a square which has the same orientation as your plane. All you need to do is multiply them up to whatever size you want, and draw it relative to any point on your plane.