Webgl: Rotating object becomes distorted along axes - matrix

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;
}
};

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());

parametrize the bounceOut ease effect in D3

In the image below, the black line describes the bounceOut function, as shown in the D3-ease documentation. Is there any way to make the bounces smaller, as in the red line?
There is no native way to change that specific easing method.
However, if you look at the source code, you'll see that the bounce steps are hardcoded:
var b1 = 4 / 11,
b2 = 6 / 11,
b3 = 8 / 11,
b4 = 3 / 4,
b5 = 9 / 11,
b6 = 10 / 11,
b7 = 15 / 16,
b8 = 21 / 22,
b9 = 63 / 64,
b0 = 1 / b1 / b1;
So, what you can easily do is creating a custom easing with a different set of values.
Just for comparison, here is the D3 easing:
d3.select("svg")
.append("circle")
.attr("r", 20)
.attr("cx", 20)
.attr("cy", 50)
.transition()
.ease(d3.easeBounceOut)
.duration(2000)
.attr("cx", 280);
<script src="https://d3js.org/d3.v5.min.js"></script>
<svg></svg>
And here the same code, with our custom easing:
d3.select("svg")
.append("circle")
.attr("r", 20)
.attr("cx", 20)
.attr("cy", 50)
.transition()
.ease(customBounce)
.duration(2000)
.attr("cx", 280);
function customBounce(t) {
var b1 = 50 / 64,
b2 = 54 / 64,
b3 = 58 / 64,
b4 = 60 / 64,
b5 = 62 / 64,
b6 = 63 / 64,
b0 = 1 / b1 / b1;
return (t = +t) < b1 ? b0 * t * t : t < b3 ? b0 * (t -= b2) * t + b4 : b0 * (t -= b5) * t + b6;
}
<script src="https://d3js.org/d3.v5.min.js"></script>
<svg></svg>
I didn't choose the best math according to your desired outcome, that's just a general explanation, so you can do it by yourself.
The following code, that I've found on a Custom Easing page by Mike Bostock on bl.ocks.org, makes exactly what I want, describing a parameterized bounce:
It defines the function:
function bounce(h) {
if (!arguments.length) h = 0.25;
var b0 = 1 - h,
b1 = b0 * (1 - b0) + b0,
b2 = b0 * (1 - b1) + b1,
x0 = 2 * Math.sqrt(h),
x1 = x0 * Math.sqrt(h),
x2 = x1 * Math.sqrt(h),
t0 = 1 / (1 + x0 + x1 + x2),
t1 = t0 + t0 * x0,
t2 = t1 + t0 * x1,
m0 = t0 + t0 * x0 / 2,
m1 = t1 + t0 * x1 / 2,
m2 = t2 + t0 * x2 / 2,
a = 1 / (t0 * t0);
return function(t) {
return t >= 1 ? 1
: t < t0 ? a * t * t
: t < t1 ? a * (t -= m0) * t + b0
: t < t2 ? a * (t -= m1) * t + b1
: a * (t -= m2) * t + b2;
};
}
and then specify it as ease argument in the transition:
d3element.transition()
.duration(1500)
.ease(bounce(0.2))
...omissis...

Controlling a virtual character's joints rotation with OpenNI + Kinect

I'm starting a project where I need to control a virtual character. The character is being rendered in multiple 3D engines, such as Three.JS and iOS SceneKit.
I'm getting the Quaternions of every joint of the skeleton with OpenNI, and it looks kind of like this:
The code that saves and pass the quaternion, looks like this:
float confidence = context.getJointOrientationSkeleton(userId, jointName,
joint);
joints[jointIndex]=joint.m00;
joints[jointIndex+1]=joint.m01;
joints[jointIndex+2]=joint.m02;
joints[jointIndex+3]=joint.m10;
joints[jointIndex+4]=joint.m11;
joints[jointIndex+5]=joint.m12;
joints[jointIndex+6]=joint.m20;
joints[jointIndex+7]=joint.m21;
joints[jointIndex+8]=joint.m22;
jointIndex+=9;
This repeats for every joint of the skeleton.
The last row and last column is always [0 0 0 1] [0, 0, 0, 1], so I just append that on the client once I receive it and build a 4x4 matrix.
I want to be able to make the right rotations with this data, but the rotations I'm getting are definitely wrong.
This is how I'm processing the matrix: (pseudo-code)
row1 = [m00 m01 m02 0]
row2 = [m10 m11 m12 0]
row3 = [m20 m21 m22 0]
row4 = [0 0 0 1]
matrix4by4 = matrix4x4(rows:[row1,row2,row3,row4])
And then I got the quaternion with two methods, and both methods were showing bad rotations, I cannot find what's wrong or what I'm missing.
First method
There's an iOS function that gets a 3x3 or 4x4 matrix, and transforms it into quaternion:
boneRotation = simd_quatf.init(matrix4by4).vector //X,Y,Z,W
Second method
I found the following code on the web:
let tr = m00 + m11 + m22
var qw = 0
var qx = 0
var qy = 0
var qz = 0
if (tr > 0) {
var S = sqrt(tr+1.0) * 2 // S=4*qw
qw = 0.25 * S
qx = (m21 - m12) / S
qy = (m02 - m20) / S
qz = (m10 - m01) / S
} else if ((m00 > m11) && (m00 > m22)) {
var S = sqrt(1.0 + m00 - m11 - m22) * 2 // S=4*qx
qw = (m21 - m12) / S
qx = 0.25 * S
qy = (m01 + m10) / S
qz = (m02 + m20) / S
} else if (m11 > m22) {
var S = sqrt(1.0 + m11 - m00 - m22) * 2 // S=4*qy
qw = (m02 - m20) / S
qx = (m01 + m10) / S
qy = 0.25 * S
qz = (m12 + m21) / S
} else {
let S = sqrt(1.0 + m22 - m00 - m11) * 2 // S=4*qz
var = (m10 - m01) / S
qx = (m02 + m20) / S
qy = (m12 + m21) / S
qz = 0.25 * S
}
boneRotation = vector4(qx, qy, qw, qz)
I started testing only with shoulder and elbow rotation to help me visualize what could be wrong or missing, and made a video.
Here's how it's behaving: https://www.youtube.com/watch?v=xUtNiwH_AGk
What could I be missing? For example, the axis of rotation of the shoulder are like this:
X-Axis
Y-Axis
Z-Axis
Thank you in advance :-)
YouTuve Video: https://www.youtube.com/watch?v=xUtNiwH_AGk

Calculate hue rotation in fabric js

How to we calculate rotation parameter value(-1 to 1) in Hue Rotation Filter in fabricjs from normal hue degree value(0-360)?
How does fabricjs calculate the rotation value in the Hue Rotation filter?
The rotation is from -180 to 180, just for simmetry with other filters like contrast and brightness that move from -1 to 1.
When fabricjs calculate the rotation multiply the value of rotation for Math.PI so it moves from -1 radian to 1 radian, making a full rotation of 2 radians that equal to 360 degree.
Since the hue rotation matrix is built just with sin and cos that are periodic function of the angle in radians, you do not have to worry about the limit.
If you want to set rotation to 2 is fine, you will get the same result as 0. rotation of 3 will give the result of and so on.
calculateMatrix: function() {
var rad = this.rotation * Math.PI, cos = Math.cos(rad), sin = Math.sin(rad),
aThird = 1 / 3, aThirdSqtSin = Math.sqrt(aThird) * sin, OneMinusCos = 1 - cos;
this.matrix = [
1, 0, 0, 0, 0,
0, 1, 0, 0, 0,
0, 0, 1, 0, 0,
0, 0, 0, 1, 0
];
this.matrix[0] = cos + OneMinusCos / 3;
this.matrix[1] = aThird * OneMinusCos - aThirdSqtSin;
this.matrix[2] = aThird * OneMinusCos + aThirdSqtSin;
this.matrix[5] = aThird * OneMinusCos + aThirdSqtSin;
this.matrix[6] = cos + aThird * OneMinusCos;
this.matrix[7] = aThird * OneMinusCos - aThirdSqtSin;
this.matrix[10] = aThird * OneMinusCos - aThirdSqtSin;
this.matrix[11] = aThird * OneMinusCos + aThirdSqtSin;
this.matrix[12] = cos + aThird * OneMinusCos;
},
so
filter.rotation = angleInDegree / Math.PI
Should work

Finding the line along the intersection of two planes

I am trying to draw the line formed by the intersections of two planes in 3D, but I am having trouble understanding the math, which has been explained here and here.
I tried to figure it out myself, but the closest that I got to a solution was a vector pointing along the same direction as the intersection line, by using the cross product of the normals of the planes. I have no idea how to find a point on the intersection line, any point would do. I think that this method is a dead end. Here is a screenshot of this attempt:
I tried to use the solution mentioned in this question, but it has a dead link to the original explanation, and the equation didn't work for me (it has unbalanced parentheses, which I tried to correct below).
var planeA = new THREE.Plane((new THREE.Vector3(0, 0, 1)).normalize(), 100);
var planeB = new THREE.Plane((new THREE.Vector3(1, 1, 1)).normalize(), -100);
var x1 = planeA.normal.x,
y1 = planeA.normal.y,
z1 = planeA.normal.z,
d1 = planeA.constant;
var x2 = planeB.normal.x,
y2 = planeB.normal.y,
z2 = planeB.normal.z,
d2 = planeB.constant;
var point1 = new THREE.Vector3();
point1.x = 0;
point1.z = (y2 / y1) * (d1 - d2) / (z2 - z1 * y2 / y1);
point1.y = (-z1 * point1.z - d1) / y1;
var point2 = new THREE.Vector3();
point2.x = 1;
point2.z = (y2 / y1) * (x1 * point2.x + d1) - (x2 * point2.x - d2) / (z2 - z1 * y2 / y1);
point2.y = (-z1 * point2.z - x1 * point2.x - d1) / y1;
console.log(point1, point2);
output:
THREE.Vector3 {x: -1, y: NaN, z: NaN, …}
THREE.Vector3 {x: 1, y: Infinity, z: -Infinity, …}
expected output:
A point along the intersection where x = 0, and
Another point on the same line where x = 1
If someone could point me to a good explanation of how this is supposed to work, or an example of a plane-plane intersection algorithm, I would be grateful.
Here is an implementation of a solution for plane-plane intersections described at http://geomalgorithms.com/a05-_intersect-1.html . Essentially, you first use the cross product of the normals of the planes to find the direction of a line in both planes. Secondly, you use some algebra on the implicit equation of the planes (P . n + d = 0 where P is some point on the plane, n is the normal and d is the plane constant) to solve for a point which is on the intersection of the planes and also on one of the x=0, y=0 or z=0 planes. The solution is then the line described by a point and a vector. I was using three.js version 79
/*
Algorithm taken from http://geomalgorithms.com/a05-_intersect-1.html. See the
section 'Intersection of 2 Planes' and specifically the subsection
(A) Direct Linear Equation
*/
function intersectPlanes(p1, p2) {
// the cross product gives us the direction of the line at the intersection
// of the two planes, and gives us an easy way to check if the two planes
// are parallel - the cross product will have zero magnitude
var direction = new THREE.Vector3().crossVectors(p1.normal, p2.normal)
var magnitude = direction.distanceTo(new THREE.Vector3(0, 0, 0))
if (magnitude === 0) {
return null
}
// now find a point on the intersection. We use the 'Direct Linear Equation'
// method described in the linked page, and we choose which coordinate
// to set as zero by seeing which has the largest absolute value in the
// directional vector
var X = Math.abs(direction.x)
var Y = Math.abs(direction.y)
var Z = Math.abs(direction.z)
var point
if (Z >= X && Z >= Y) {
point = solveIntersectingPoint('z', 'x', 'y', p1, p2)
} else if (Y >= Z && Y >= X){
point = solveIntersectingPoint('y', 'z', 'x', p1, p2)
} else {
point = solveIntersectingPoint('x', 'y', 'z', p1, p2)
}
return [point, direction]
}
/*
This method helps finding a point on the intersection between two planes.
Depending on the orientation of the planes, the problem could solve for the
zero point on either the x, y or z axis
*/
function solveIntersectingPoint(zeroCoord, A, B, p1, p2){
var a1 = p1.normal[A]
var b1 = p1.normal[B]
var d1 = p1.constant
var a2 = p2.normal[A]
var b2 = p2.normal[B]
var d2 = p2.constant
var A0 = ((b2 * d1) - (b1 * d2)) / ((a1 * b2 - a2 * b1))
var B0 = ((a1 * d2) - (a2 * d1)) / ((a1 * b2 - a2 * b1))
var point = new THREE.Vector3()
point[zeroCoord] = 0
point[A] = A0
point[B] = B0
return point
}
var planeA = new THREE.Plane((new THREE.Vector3(0, 0, 1)).normalize(), 100)
var planeB = new THREE.Plane((new THREE.Vector3(1, 1, 1)).normalize(), -100)
var [point, direction] = intersectPlanes(planeA, planeB)
When I have problems like this, I usually let a symbolic algebra package (Mathematica in this case) deal with it. After typing
In[1]:= n1={x1,y1,z1};n2={x2,y2,z2};p={x,y,z};
In[2]:= Solve[n1.p==d1&&n2.p==d2,p]
and simplifying and substituting x=0 and x=1, I get
d2 z1 - d1 z2 d2 y1 - d1 y2
Out[5]= {{{y -> -------------, z -> ----------------}},
y2 z1 - y1 z2 -(y2 z1) + y1 z2
d2 z1 - x2 z1 - d1 z2 + x1 z2
> {{y -> -----------------------------,
y2 z1 - y1 z2
d2 y1 - x2 y1 + (-d1 + x1) y2
> z -> -----------------------------}}}
-(y2 z1) + y1 z2
It is easy to let three.js solve this for you.
If you were to express your problem in matrix notation
m * x = v
Then the solution for x is
x = inverse( m ) * v
We'll use a 4x4 matrix for m, because three.js has an inverse() method for the Matrix4 class.
var x1 = 0,
y1 = 0,
z1 = 1,
d1 = 100;
var x2 = 1,
y2 = 1,
z2 = 1,
d2 = -100;
var c = 0; // the desired value for the x-coordinate
var v = new THREE.Vector4( d1, d2, c, 1 );
var m = new THREE.Matrix4( x1, y1, z1, 0,
x2, y2, z2, 0,
1, 0, 0, 0,
0, 0, 0, 1
);
var minv = new THREE.Matrix4().getInverse( m );
v.applyMatrix4( minv );
console.log( v );
The x-component of v will be equal to c, as desired, and the y- and z-components will contain the values you are looking for. The w-component is irrelevalent.
Now, repeat for the next value of c, c = 1.
three.js r.58
Prerequisites
Recall that to represent a line we need a vector describing its direction and a point through which this line goes. This is called parameterized form:
line_point(t) = t * (point_2 - point_1) + point_1
where point_1 and point_2 are arbitrary points through which the line goes, and t is a scalar which parameterizes our line. Now we can find any point line_point(t) on the line if we put arbitrary t into the equation above.
NOTE: The term (point_2 - point_1) is nothing, but a vector describing the direction of our line, and the term point_1 is nothing, but a point through which our line goes (of course point_2) would also be fine to use too.
The Algorithm
Find the direction direction of the intersection line by taking
cross product of plane normals, i.e. direction = cross(normal_1,
normal_2).
Take any plane, for example the first one, and find any 2 distinct points
on this plane: point_1 and point_2. If we assume that the plane equation
is of the form a1 * x + b1 * y + c1 * z + d1 = 0, then to find 2
distinct points we could do the following:
y1 = 1
z1 = 0
x1 = -(b1 + d1) / a1
y2 = 0
z2 = 1
x2 = -(c1 + d1) / a1
where point_1 = (x1, y1, z1) and point_2 = (x2, y2, z2).
Now that we have 2 points, we can construct the parameterized
representation of the line lying on this first plane:
line_point(t) = t * (point_2 - point_1) + point_1, where line_point(t)
describes any point on this line, and t is just an input scalar
(frequently called parameter).
Find the intersection point intersection_point of the line
line_point(t) and the second plane a2 * x + b2 * y + c2 * z + d2 = 0 by using
the standard line-plane intersection algorithm (pay attention to the
Algebraic form section as this is all you need to implement line-plane
intersection, if you haven't done so already).
Our intersection line is now found and can be constructed in
parameterized form as usual: intersection_line_point(s) = s *
direction + intersection_point, where intersection_line_point(s)
describes any point on this intersection line, and s is parameter.
NOTE: I didn't read this algorithm anywhere, I've just devised it from the top of my head based on my knowledge of linear algebra. That doesn't mean that it doesn't work, but it might be possible that this algorithm can be optimized further.
Conditioning
When 2 normal vectors normal_1 and normal_2 are almost collinear this problem gets extremely ill-conditioned. Geometrically it means that the 2 planes are almost parallel to each other and determining the intersection line with acceptable precision becomes impossible in finite-precision arithmetic which is floating-point arithmetic in this case.

Resources