Opengl matrix without glm [duplicate] - matrix

This question already has an answer here:
How do I compose a rotation matrix with human readable angles from scratch?
(1 answer)
Closed 11 months ago.
Does anybody know what the order of a 4x4 GLfloat array matrix for transforming a 2D rectangle is? I don't want to use glm or cglm to make my life easy. I'm trying to use the least amount of libraries as possible.
Is the order something like this:
{ px, sx, rx, 0, py, sy, ry, 0, pz, sz, rz, 0, 0, 0, 0, 1 } ?
If not what is it?
Thanks!

4x4 matrix is for 3D.
Xx Yx Zx Tx
Xy Yy Zy Ty
Xz Yz Zz Tz
0 0 0 1
(Xx, Xy, Xz) - left (or right) vector
(Yx, Yy, Yz) - up vector
(Zx, Zy, Zz) - forward vector
(Tx, Ty, Tz) - translation (position) vector
indices:
m00 m01 m02 m03
m10 m11 m12 m13
m20 m21 m22 m23
m30 m31 m32 m33
Order: m00, m10, m20, m30, m01, m11, m21, m31, m02, m12, m22, m32, m03, m13, m23, m33
If you need only 2D transformations you can use a 3x3 matrix.
Xx Yx Tx
Xy Yy Ty
0 0 1
Order: m00, m10, m20, m01, m11, m21, m02, m12, m22
Or you want this?
Xx Yx 0 Tx
Xy Yy 0 Ty
0 0 1 0
0 0 0 1

Related

How to change quaternion to rotation matrix in world coordinate

I am trying to transform point cloud for register.
I have got quaternion, but when I do the transformation using point cloud library. It seem to transform point cloud in local coordinate instead of world coordinate. So, it can't do the register. I want to know the formula of Quaternion convert to rotation matrix
https://pointclouds.org/documentation/tutorials/matrix_transform.html#
Here is my transformation code
float qw, qx, qy, qz, tx, ty, tz;
qw = kinectmatrix4f[num][0];
qx = kinectmatrix4f[num][1];
qy = kinectmatrix4f[num][2];
qz = kinectmatrix4f[num][3];
tx= kinectmatrix4f[num][4];
ty = kinectmatrix4f[num][5];
tz = kinectmatrix4f[num][6];
//qx = -qx;
qy = -qy;
//init transformation matrix
Eigen::Matrix4f transform_matrix = Eigen::Matrix4f::Identity();
transform_matrix <<
1 - 2 * pow(qy, 2) - 2 * pow(qz, 2), 2 * qx*qy - 2 * qz*qw, 2 * qx*qz + 2 * qy*qw, 0,
2 * qx*qy + 2 * qz*qw, 1 - 2 * pow(qx, 2) - 2 * pow(qz, 2), 2 * qy*qz - 2 * qx*qw, 0,
2 * qx*qz - 2 * qy*qw, 2 * qy*qz + 2 * qx*qw, 1 - 2 * pow(qx, 2) - 2 * pow(qy, 2), 0,
0, 0, 0, 1;
printf("Transforming point cloud %i in rough\n", num);
std::cout << transform_matrix << std::endl;
pcl::transformPointCloud(*cloud_input, *cloud_input, transform_matrix);
However, I try transformation in unity by this code. It seems to change rotation well.
Quaternion rot=new Quaternion(0.4f,0.5f,0.9f,1);
transform.rotation=rot;
I'm not quite sure what you are asking for. But If you are using Eigen here.
You can simply convert quaternion rotation to rotation matrix using Eigen. Quaternionf.toRotationMatrix()
Eigen::Quaternionf quat(qw, qx, qy, qz);
//init transformation matrix
Eigen::Matrix4f transform_matrix = Eigen::Matrix4f::Identity();
// convert Quaternion to rotation matrix
Matrix3f rot_mat = quat.toRotationMatrix();
// assign the rotation part of transform matrix
transform_matrix.block(0, 0, 3, 3) = rot_mat;
// you should create another point cloud to acquire your result
pcl::PointCloud<pcl::PointXYZ>::Ptr transformed_cloud(new pcl::PointCloud <pcl::PointXYZ>)
pcl::PointCloud<pcl::PointXYZ>::Ptr transformed_cloud2(new pcl::PointCloud <pcl::PointXYZ>)
// do transform
pcl::transformPointCloud(*cloud_input, *transformed_cloud, transform_matrix);
// if above transformed_cloud is not what you expected, try inverse the transform matrix to check if your rotation is "back-ward"
pcl::transformPointCloud(*cloud_input, *transformed_cloud2, transform_matrix.inverse());

How do you rectify a 3D planar polygon?

I have a 3D planar (all vertices lie in some plane) polygon with vertices: [(x1, y1, z1) ... (x1, y1, z1)].
I would like to transform this polygon so that I'm viewing it orthographically (as if I'm looking at it straight on).
How can this be done in Python?
I assume you have no information except for vertex coordinates.
Take three non-collinear (perhaps consequent) vertices C, A, B. Calculate normalized vector (divide by vector length)
b = (B - A) / |B - A|
then normal vector (using vector/cross multiplication)
N = b.cross.(A-C) and normalize it
un = N / |N|
and another unit vector in polygon plane
v = b.cross.n
Now we want find such matrix of affine transformations, that transforms vertex A into point (0,0,0), edge AB will be collinear with OX axis, normal will be collinear with OZ axis, vector q will be collinear with OY axis. This all means that rotated polygon will lie in OXY plane.
Mathematically: points A, u=A+b, v=A+q, n=A+un should be transformed in quadruplet (0,0,0), (1,0,0), (0,1,0), (0,0,1). In matrix form
[Ax ux vx nx] [0 1 0 0]
M * [Ay uy vy ny] = [0 0 1 0]
[Az uz vz nz] [0 0 0 1]
[1 1 1 1 ] [1 1 1 1]
or
M * S = D
Using matrix inverse
M * S * Sinv = D * Sinv
and finally
M = D * Sinv
So calculate matrix M and multiply it with every vertex coordinates. New coordinates should have zero Z-component (or very small due to numerical errors).
You can perform all described operations with numpy library with a little code
Example with specific data
Quick-made implementation in plain Python for reference
import math
def calcMatrix(ax, bx, cx, ay, by, cy, az, bz, cz):
ux, uy, uz = bx - ax, by - ay, bz - az
mag = math.sqrt(ux*ux+uy*uy +uz*uz)
ux, uy, uz = ux / mag, uy / mag, uz / mag
Cx, Cy, Cz = ax - cx, ay - cy, az - cz
nx, ny, nz = uy * Cz - uz * Cy, uz * Cx - ux * Cz, ux * Cy - uy * Cx
mag = math.sqrt(nx*nx+ny*ny+nz*nz)
nx, ny, nz = nx / mag, ny / mag, nz / mag
vx, vy, vz = uy * nz - uz * ny, uz * nx - ux * nz, ux * ny - uy * nx
denom = 1.0 / (ux*ux+uy*uy + uz*uz)
M = [[0.0]*4 for _ in range(4)]
M[3][3] = 1.0
M[0][0] = denom*(ux)
M[0][1] = denom*(uy)
M[0][2] = denom*(uz)
M[0][3] = denom*(-ax*ux-ay*uy+az*uz)
M[1][0] = denom*(vx)
M[1][1] = denom*(vy)
M[1][2] = denom*(vz)
M[1][3] = -denom*(ax*vx-ay*vy+az*vz)
M[2][0] = denom*(nx)
M[2][1] = denom*(ny)
M[2][2] = denom*(nz)
M[2][3] = denom*(-ax*nx-ay*ny+az*nz)
return M
def mult(M, vec):
res = [0]*4
for k in range(4):
for i in range(4):
res[k] += M[k][i] * vec[i]
return res
#test corners and middle point
M = calcMatrix(1, 0, 0, 0, 1, 0, 0, 0, 1)
#print(M)
p = [1, 0, 0, 1]
print(mult(M, p))
p = [0, 1, 0, 1]
print(mult(M, p))
p = [0, 0, 1, 1]
print(mult(M, p))
p = [1/3, 1/3, 1/3, 1]
print(mult(M, p))
test results:
[0.0, 0.0, 0.0, 1.0]
[1.4142135623730951, 0.0, 0.0, 1.0]
[0.7071067811865476, 1.2247448713915892, 0.0, 1.0]
[0.7071067811865476, 0.4082482904638631, 1.1102230246251565e-16, 1.0]
Find a normal n to the polygon, by means of a cross-product between two non-parallel sides. Take the cross-product of n with a vertical vector, to get an horizontal vector u. Then take the cross product of n and u to get v, and normalize the vectors. u and v are parallel to the plane of the polygon and orthogonal to each other.
Finally, for every vertex p compute the 2D coordinates (p.u, p.v) which show you the polygon in its plane.
numpy supplies the cross and dot vector functions. Also linalg.norm (or sqrt(dot(v, v))).
Here's a robust approach using NumPy (project(); the rest is test code).
import numpy
import scipy.spatial
def project(x):
# Center the plane on the origin
x = x - numpy.mean(x, axis=0)
# Compute the Singular Value Decomposition
u, s, v = numpy.linalg.svd(x)
# Return the top two principal components
return u[:, :2] # numpy.diag(s[:2])
def test():
n = 10
x = (numpy.random.rand(n, 2) # numpy.random.rand(2, 3)) + numpy.random.rand(3)
y = project(x)
print(x.shape, y.shape)
print(
numpy.max(
numpy.abs(
scipy.spatial.distance_matrix(x, x)
- scipy.spatial.distance_matrix(y, y)
)
)
)
if __name__ == "__main__":
test()
Sample output:
(10, 3) (10, 2)
5.551115123125783e-16

Webgl: Rotating object becomes distorted along axes

I'm having trouble trying to display three cartesian planes in 3D, when they rotate the planes get stretched in one direction (z-axis, blue) and compressed in another (x-axis, red) like these images showing rotation around the y-axis:
45 degrees:
95 degrees:
135 degrees:
I calculate the perspective matrix, with mat4.perspective from the gl-matrix library:
mat4.perspective(this.pMatrix_, this.vFieldOfView_, this.aspect_, this.near_, this.far_);
with values of:
private near_ = 0.1;
private far_ = 1000.0;
private vFieldOfView_ = 60.0 * Math.PI / 180;
Vertex Shader:
void main(void) {
gl_Position = uProjection * uView * uTransform * vec4(aVertexPosition, 1.0);
}
The view matrix translates the object 2.0 units away from the camera.
let t = new Mat4();
t.array[12] = v.x;
t.array[13] = v.y;
t.array[14] = v.z;
I rotate the planes around the y-axis using the matrix generated from this code:
// identity matrix already set
let rad = angle * Math.PI / 180;
r.array[0] = Math.cos(rad);
r.array[2] = Math.sin(rad);
r.array[8] = -1.0 * Math.sin(rad);
r.array[10] = Math.cos(rad);
And I multiply the three transform matrices of the object in this order:
rotation * translation * scale. Was using quaternions to handle rotations but they were similarly distorted so went back to using rotation matrices and kept the rotation simple, in one axis. It looks like I'm doing some multiplication step in the wrong order or not using the perspective matrix correctly or got a sign wrong.
Update:
Just to clarify on the value of some of the matrices in vertex shader:
uProjection = pMatrix_ = value obtained from mat.perspective(...).
uView = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, -2.0, 0, 0, 0, 1] i.e. matrix translated 2 units away in z-axis.
uTransform should be identity matrix in this example.
Update2:
uView was actually [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, -2.0, 1]
You have transposed your view matrix. You have
1 0 0 0
0 1 0 0
0 0 1 0
0 0 -2 1
You want:
1 0 0 0
0 1 0 0
0 0 1 -2
0 0 0 1
This mistake never would have happened if you just stuck with gl-matrix and used mat4.translate(). This is why I don't use direct array access for creating a matrix, it's too easy to screw up.
Remember that OpenGL matrixes are stored like an array of column vectors. So the indexes go like this:
0 4 8 12
1 5 9 13
2 6 10 14
3 7 11 15
I found out where I was going wrong with my implementation, it was with the matrix multiplication. The correct code is this:
static multiply(a: Mat4, b: Mat4, out: Mat4) {
let a11 = a.array[0], a12 = a.array[1], a13 = a.array[2], a14 = a.array[3],
a21 = a.array[4], a22 = a.array[5], a23 = a.array[6], a24 = a.array[7],
a31 = a.array[8], a32 = a.array[9], a33 = a.array[10], a34 = a.array[11],
a41 = a.array[12], a42 = a.array[13], a43 = a.array[14], a44 = a.array[15];
for (let i = 0; i < 16; i += 4) {
let b1 = b.array[i], b2 = b.array[i + 1], b3 = b.array[i + 2], b4 = b.array[i + 3];
out.array[i] = b1 * a11 + b2 * a21 + b3 * a31 + b4 * a41;
out.array[i + 1] = b1 * a12 + b2 * a22 + b3 * a32 + b4 * a42;
out.array[i + 2] = b1 * a13 + b2 * a23 + b3 * a33 + b4 * a43;
out.array[i + 3] = b1 * a14 + b2 * a24 + b3 * a34 + b4 * a44;
}
};

Calculating a location on a circle given an angle of rotation

Okay algebra and trig are not my strong suit by any means so here is what I need to do.
I have a circle which is measured in degrees from +180 to -180 (360 total)
Given the center point of the circle stays the same, Cx , Cy.
The angle varies from -180 to +180
I need to locate a point that regardless the given angle is + 3 units away that is at the 90 degree position and the 270 degree position (from the given degrees)
So like...
Angle = 0
Point 1 -> x = 0, y -3
Point 2 -> x = 0, y + 3
And if the angle was say 90 (provided its measured Clockwise)
Point 1 -> x = -3, y = 0
Point 2 -> x = 3, y = 0
What I need is a forumla that will accept Angle, then tell me what my x/y should be 3 units away from the origin.
I have tried: EDIT Updated to double precision using Java.
`double x = Cx + 3 * Math.cos((d + 90) * Math.PI / 180);'
'double y = Cy + 3 * Math.sin((d + 90) * Math.PI / 180);`
this gives me mixed results, I mean sometimes it's where I think it should be and other times its quite wrong.
Assuming Cx = 0.500, Cy = 0.500
Sample Data: Result:
Deg = 0 x = 2 / y = 5
Deg = 90 x = -1 / y = 2
Deg = 125 x = -0.457 / y = 0.297
Deg = 159 x = 0.924 / y = -0.800
I realize I am only calculating one point at this point but do you have any suggestions on how to get the first point working? at say 90 degrees from whatever degree I start with?
x = Cx + r * Math.cos( (d+90) * Math.PI / 180 );
y = Cy + r * Math.sin( (d+90) * Math.PI / 180 );
Seems that this is the correct formula for what I was trying to accomplish. This will take any value for Cx/Cy's origin add the Radius r, then calculate the degrees + 90 and convert to radians.. Once all that magic takes place, you're left with an x/y coord that is 90 degrees of where you started.

Calculating an AABB for a transformed ellipse

I am looking to compute the axis-aligned bounding box (AABB) of a 2D ellipse on which a tranformation matrix was applied (rotation, scale, translation, etc.)
Something similar to this solution : Calculating an AABB for a transformed sphere
So far, it doesn't seem to work for 2D ellipses.
This is what I got (in pseudo-code) :
Matrix M; // Transformation matrix (already existing)
Matrix C = new Matrix( // Conic matrix
radiusX, 0, 0,
0, radiusY, 0,
0, 0, -1
);
Matrix MT = M.transpose();
Matrix CI = C.inverse();
Matrix R = M*CI*MT;
int minX = (R13 + sqrt(R13^2 - (R11 * R33))) / R33;
int minY = (R23 + sqrt(R23^2 - (R22 * R33))) / R33;
// maxX etc...
// Build AABB Rectangle out of min & max...
Simple demo of the current behavior
radiusX = 2
radiusY = 2 // To keep it simple, M is identity
// (no transformation on the ellipse)
M = /1 0 0\ // /M11 M21 M31\
|0 1 0| // |M12 M22 M32| Transform matrix format
\0 0 1/ // \0 0 1 /
C = /2 0 0\ // C as conic
|0 2 0|
\0 0 -1/
CI =/0.5 0 0\ // CI as dual conic
|0 0.5 0|
\0 0 -1/
R = /1 0 0\ * /0.5 0 0\ * /1 0 0\ // R = M*CI*MT
|0 1 0| |0 0.5 0| |0 1 0|
\0 0 1/ \0 0 -1/ \0 0 1/
= /0.5 0 0\ // /R11 R12 R13\
|0 0.5 0| // |R12 R22 R23| (R is symmetric)
\0 0 -1/ // \R13 R23 R33/
minX = (0 + sqrt(0^2 - (0.5 * -1))) / -1
= -0.7071 // Should be -2
// Also, using R = MIT*C*MI
// leads to -1.4142
Solution (using dual conic matrix)
Matrix M;
Matrix C = new Matrix(
1/radiusX^2, 0, 0,
0, 1/radiusY^2, 0,
0, 0, -1
);
Matrix MT = M.transpose();
Matrix CI = C.inverse();
Matrix R = M*CI*MT;
int minX = (R13 + sqrt(R13^2 - (R11 * R33))) / R33;
int minY = (R23 + sqrt(R23^2 - (R22 * R33))) / R33;
Final solution (no direct use of conic matrix)
Here's a simplified version.
Matrix M;
int xOffset = sqrt((M11^2 * radiusX^2) + (M21^2 * radiusY^2));
int yOffset = sqrt((M12^2 * radiusX^2) + (M22^2 * radiusY^2));
int centerX = (M11 * ellipse.x + M21 * ellipse.y) + M31; // Transform center of
int centerY = (M12 * ellipse.x + M22 * ellipse.y) + M32; // ellipse using M
// Most probably, ellipse.x = 0 for you, but my implementation has an actual (x,y) AND a translation
int xMin = centerX - xOffset;
int xMax = centerX + xOffset;
int yMin = centerY - yOffset;
int yMax = centerY + yOffset;
From dual conic
So you state that M is a transformation matrix. But what does it transform, is it points or lines? I assume points. How do you represent points, as a row vector so that the point is on the left and the matrix on the right, or as a column vector so that the matrix is on the left and the point on the right of a multiplication? I'll assume column vectors. So a transformation would be p' = M*p for some point p.
Next is C. The way you write it, that's an ellipse but not with the radii you are using. A point lies on the ellipse if it satisfies (x/radiusX)^2 + (y/radiusY)^2 = 1 so the values on the main diagonal have to be (1/radiusX^2, 1/radiusY^2, -1). I repeatedly missed this mistake in pervious revisions of my answer.
Next you combine these things. Suppose CP were the primal conic, i.e. the conic as a set of points. Then you'd obtain the transformed version by doing MT.inverse()*CP*M.inverse(). The reason is because you apply M.inverse() to every point and then check whether it lies on the original conic. But you are not using M.inverse(), you are using M. This indicates that you try to transform a dual conic. If M transforms points, then MT.inverse() transforms lines, so M*CD*MT is the correct transformation if CD is a dual conic.
And if R is a dual conic, then your formulas are correct. So perhaps the main problem with your code is the fact that you forgot to use inverse radii in the matrix C.
From primal conic
When I read your post for the first time, I assumed R would describe a set of points, i.e. that a point (x,y) lies on that ellipse if (x,y,1)*R*(x,y,1).transpose()=0. Based on this, I did come up with formulas for the AABB without using the dual conic. I'm not saying that this is simpler, particularly not if you have matrix inversion available as a building block. But I'll still leave it here for reference. Keep in mind that the R in this paragraph is a different one from the one used in your code example.
For my approach, consider that R*(1,0,0) (which is simply the first column of R) is some vector (a,b,c) which you can interpret as a definition of a line ax+by+c=0. Intersect that line with the conic and you get the points where the tangents are horizontal, which are the extrema in y direction. Do the same for R*(0,1,0) (i.e. the seond column) to find extrema in the x direction.
The key idea here is that R*p computes the polar line for some point p, so we are constructing the polar line for the point at infinity in x resp. y direction. That polar line will intersect the conic in those points where the tangents through p touch the conic, which in this case would be horizontal resp. vertical tangents since parallel lines intersect at infinity.
If I do the above computation symbolically, I get the following formulas:
xmin, xmax = (R13*R22^2 - R12*R22*R23 ± sqrt(R13^2*R22^4 - 2*R12*R13*R22^3*R23 + R11*R22^3*R23^2 + (R12^2*R22^3 - R11*R22^4)*R33))/(R12^2*R22 - R11*R22^2)
ymin, ymax = (R11*R12*R13 - R11^2*R23 ± sqrt(R11^3*R13^2*R22 - 2*R11^3*R12*R13*R23 + R11^4*R23^2 + (R11^3*R12^2 - R11^4*R22)*R33))/(R11^2*R22 - R11*R12^2)
These expressions can certainly be simplified, but it should get you started. Feel free to edit this post if you reformulate this to something simpler, or easier to read, or whatever.

Resources