I want to rotate a node in y axis which has euler x value -90 (x:0,y:0,z:0). How can i achieve this? I have checked other posts related to rotation but all solutions are provided for euler values (0,0,0).
Don't rotate the around (0, 1, 0) but rotate this vector by multiplying it to the nodes transform. For eulerAngles of (-90, 0, 0) this results in the axis (0.0, -0.448074, 0.893997).
But I assume you actually want the euler angles set to -90° (all angles are in radians in SceneKit), wich results in the axis (0, 0, 1), so you need to rotate the node around the z-axis of the rotated node.
You can also wrap your node with your euler angles set in a parent node and rotate this parent node around the y-axis and let SceneKit handle the transformations and coordinate spaces.
Related
The Problem
I'm trying to use Blender to create synthetic image for use with OpenCV's pose estimation (specifically, OpenCV's findEssentialMat and recoverPose). However, I found that the rotation matrix R that OpenCV returns corrects for rotations along the camera's Y and Z axes, but not its X axis. I suspect this is because Blender and OpenCV have different camera models (see diagram), but I can't figure out how to correct for this. How would I take a rotation matrix made using OpenCV's camera model and apply it to Blender's camera model?
Additional Details
To test this, I rendered a scene from (0, 0, 30) using an identity camera rotation, then rotated the camera by 10 degrees along X, Y and Z. First, here's the identity rotation matrix (no rotation):
Here's a 10-degree rotation around X:
And here's a 10-degree rotation around Y:
Finally, here's a 10-degree rotation around Z:
Applying the rotation matrix OpenCV returns (for the estimated transformation between the rotated image and the original) corrects for all of these rotations except around X, which looks like this:
It seems that instead of correctly rotating by -10 degrees around X, the rotation matrix rotates a further 10 degrees around X.
Going from an OpenCV matrix to Blender could be accomplished by multiplying by another rotation matrix to compensate for the change in coordinates systems.
mat = [[1, 0, 0],
[0, -1, 0],
[0, 0, -1]]
You said the Y and Z components are already being compensated for so perhaps the negative of the matrix is what you need?
The effect I'm trying to achieve is to have an arrow pointed out from the camera's pointOfView position, aligned with the scene (and gravity) on the x and z axis, but pointing in the same direction as the camera. It might look something like this:
Right now, I have its euler angles x and z set to 0, and it's y set to match that of the ARSCNView.pointOfView.eulerAngles.y. The problem is that as I rotate the device, the eulerAngles.y can end up having the same value for different points. For example, facing the device in one direction, my eulerAngles are:
x: 2.52045, y: -0.300239, z: 3.12887
Facing it in another direction, the eulerAngles are:
euler angles x: -0.383826, y: -0.305686, z: -0.0239297
Even though these directions are quite far apart, the eulerAngles is still pretty much the same. The different x and z values mean the y value doesn't represent which direction the device is facing. As a result, my arrow follows the camera's heading to some point, and then starts rotating back in the opposite direction. How can I zero-out the x and z values in a way that I'll get a truthful y value, that I can then use to orient my arrow?
Do not use euler angles to detect 'real' or 'truthful' values.
Those values are always correct. To work with rotation you have to use either matricies or quaternions.
Remember. It is possible to define lots of 'euler angles' using matrix.
Each euler angle is relative to the previous one.
SceneKit applies these rotations in the reverse order of the
components:
1. first roll
2. then yaw
3. then pitch
So, to calculate the value you angle I would suggest to calculate the vector and its projection to Oxy plane. This angle is not 'euler' angle.
I've imported a Blender 3D model (a sphere with an Earth texture) in SceneKit, and I'm trying to rotate it through a pan gesture. I have to mentally swap the x and y axises, because the rotation system in SceneKit is different from the one adopted by Blender.
This is how I rotate the Earth object:
func pan(gesture:UIPanGestureRecognizer)
{
let translation = gesture.translation(in: self.view) * 0.05
let intensity = Float(magnitude(point: translation)) // Euclidean distance
let rotation = SCNMatrix4MakeRotation(intensity, Float(translation.y), Float(translation.x), 0.0) // I invert the x and y because of the different coordinate system
earth.transform = SCNMatrix4Mult(earth.transform, rotation)
gesture.setTranslation(CGPoint(x:0.0, y:0.0), in: self.view)
}
The rotation around the y axis is correct, but if I try to rotate it along the x axis by panning vertically, it seems like if the x axis of the Earth is oblique and not perpendicular with the y axis.
In this video I first drag the finger upwards and then downwards:
https://www.youtube.com/watch?v=7YumAB_rXlk
When you rotate the node with a gesture, it is done with respect to the node's axes. Say your node is in it's original default orientation, +X direction to the right. Now you rotate the the node 180° about the Y with a left-to-right swipe. If you now apply a top-to-bottom swipe, the node will rotate upward about the X instead of downward. This is because the first swipe swung the X-axis around such that it was pointing in the opposite direction. That's just the extreme example, if your first Y rotation was less than 180°, an attempt to now swipe about the X will cause the node to rotate in a slanted manner such as in your video.
One way to deal with this is to place your earth node in another node (a "container node"). Use the earth node for Y rotation and,separately, use the container node for the X rotation.
But the way I deal with this can be found here: How to rotate an SCNBox
I am applying a force and a torque on an node. This is my code:
myNode?.physicsBody?.applyForce(SCNVector3Make(0, -6, 4), atPosition: SCNVector3Make(0, 1, -1), impulse: true)
myNode?.physicsBody?.applyForce(SCNVector3Make(0, -2, 10), impulse: true)
myNode?.physicsBody?.applyTorque(SCNVector4Make(4, 2, 2.5, 1.6), impulse: true)
The object now falls down and moves from left to right afterwards. I want it fall down and move from right to the left(basically a reflection of the first movement across y-axis). I figured it out that there is very little I can do about the first 2 lines of code, because the force has no x-component. The last line, applyTorque, is the one I need to manipulate. How do you map across the y-axis if the vector has 4 components? I am a little rusty with math
The fuller version of the applyTorque function looks something like this:
.applyTorque(SCNVector4Make(x:, y:, z:, w:), impulse:)
So any numbers you put in the second position should be torque amounts around the y axis.
There's probably a relationship between the numbers and what they create in terms of rotational force on an object, but I've always just used trial-and-error to find what works. Sometimes it's HUGE numbers.
I am assuming that the x-axis is horizontal and the y-axis is vertical and the z-axis points straight at you (see the black arrows below):
I found evidence that this is indeed the case in SceneKit.
If
applyTorque(SCNVector4Make(x, y, z, w), impulse: boolean)
is the correct usage, then x is the amount of counter-clockwise rotation around the x-axis (see green circle arrow), and similarly for y and z. Again, this is my best guess, and it is possible that SceneKit uses clockwise rotation. Therefore, x, y, and z together determine the axis of rotation of the torsional force.
Here is a simpler way to think of it. x, y, and z create a vector in the 3D space described above. The object will rotate counter-clockwise around this vector.
w on the other hand, is the magnitude of the torque, and has nothing to do with the axis of rotation.
Your request to "map vector across the y-axis" is actually a reflection across the yz-plane. If you think about it, what you want is to rotate the opposite direction around the y-axis (negate y) and the same for z.
So the answer should be:
myNode?.physicsBody?.applyTorque(SCNVector4Make(4, -2, -2.5, 1.6), impulse: true)
According to the SceneKit documentation the SCNVector4 argument specifies the direction (x, y, z vector components) and magnitude (w vector component) of the force in newton-meters. To mirror the direction of the applied torque, all you have to do is invert the magnitude. (x, y, z, -magnitude)
Let's say I have a pinhole camera with known intristic values like camera matrix and distortion coefficients. Let's say there is a point in large enough distance from the camera, so we can say it is placed in infinity.
Given image coordinates of this point in pixels, I would like to calculate camera rotation relative to the axis that connects camera and this point (so rotation is 0,0 if camera is directed at this point and it is in the optical center of the image).
How can this be done using opencv?
Many thanks!
You need to specify an additional constraint - rotating the camera from its current pose to one that aligns the optical axis with an arbitrary ray leaves the camera free to rotate about the ray itself (i.e. it leaves the "roll" angle unspecified).
Let's assume that you want the roll to be zero, i.e. that you want the motion to be a pure pan-tilt. This has a unique solution as long as the ray you want to align to is not parallel to the vertical image axis (in which case pan and roll are the same motion).
Then the solution is computed as follows. Let's use the OpenCV camera frame: Z=[0,0,1]' (, where " ' " means transpose) be the camera focal axis, oriented going out of the lens, Y=[0,1,0]' the vertical axis going down, and X = Z x Y (where 'x' is the cross product) the horizontal camera axis going toward the right of the image. So "pan" is a rotation about Y, "tilt" is a rotation about X.
Let U = [u1, u2, u3]', with || u || = 1 be the ray you want to rotate to. You want to apply a pan that brings Z onto the plane Puy defined by the vectors u and Y, then apply a tilt that brings Z onto u.
The angle of the first rotation is (angle between Z and Puy) = [90 deg - (angle between Z and Y x U)]. this is because Y x U is orthogonal to Puy. Look up the expressions for computing the angle between vectors on Wikipedia or elsewhere online. Once you have the angle (or its cosine and sine), the rotation about Y can be expressed as a standard rotation matrix Ry.
The angle of the second rotation, about X after once Z is onto Puy, is the angle between vector Z and U after Ry is applied to Z, or equivalently, between Z and inv(Ry) * U. Compute the angle between the vector, and use to build a standard rotation matrix about X, Rx
The final transformation is then Rx * Ry.