I want to use a smart Rubik's Cube (GoCube) to control a 3D object in Unity, it has IMU and a Unity plugin is also available. Currently I'm trying to implement the basic logic to orient the 3D object the way the cube is oriented with IMU data, meaning the 3D object should follow the cube's rotations. For this, I tried to copy relevant elements from the original plugin.
Unfortunately, I cannot get the orientation quaternions to behave the right way: Rotating the cube on the X- and Z-axes results in a mirrored result, as I demonstrate in the GIF. What works is the rotation around the Y-axis.
Demo of quaternion rotations
Without the smart cube, there's obviously no working example, so I copied that part of the code I'm using to orient the 3D object:
public GameObject objectToBeSynchronised;
public Quaternion fixedOffset = Quaternion.Euler(90, 90, 0);
void Update()
{
objectToBeSynchronised.transform.rotation = GetCurrentOrientation(); // live-rotate object
}
/// <summary>
/// Function to get current cube orientation as quaternion
/// </summary>
/// <returns>cube orientation as quaternion</returns>
public Quaternion GetCurrentOrientation()
{
Quat q = GoCubeProvider.GetProvider().GetConnectedGoCube().orientation;
Quaternion currentOrientation = new Quaternion();
currentOrientation = ConvertQuatToQuaternion(q) * fixedOffset; // convert from type Particula.Quat to Quaternion and multiply fixed offset from Particula
return currentOrientation;
}
/// <summary>
/// Function to convert quaternion from Particula.Quat to Quaternion type
/// </summary>
/// <param name="quat">quaternion type Particula.Quat from cube orientation</param>
/// <returns>converted Quaternion</returns>
Quaternion ConvertQuatToQuaternion(Quat quat)
{
return new Quaternion(quat.x, quat.y, quat.z, quat.w);
}
The fixedOffset and ConvertQuatToQuaternion() are parts I imitated from the original plugin, the relevant file is CubeViewModel.cs. fixedOffset makes the 3D object stand upright and not lie on the side. Maybe I'm missing part of the original script to make it work - file CubeView.cs could also be interesting.
After reading answers such as here, here and here I played around with the ConvertQuatToQuaternion function - I tried switching the values to e.g. (w, x, y, z) or added minus (-x, y, -z, w) and a lot of other combinations. None made it right. Also tried Quaternion.Inverse on the converted quaternion - no success.
I don't have much knowledge about, let alone experience with quaternions and am a bit lost. I was expecting negating x and z to work, since those are the axes that are flipped. However, there seems to be more to this, maybe or probably I'm missing a step from the original plugin.
For anyone planning on developing with the GoCube and their API: I found a solution! I changed nothing about my code sample except the fixedOffset. As suggested in the Unity Docs, I declared it as public static Quaternion fixedOffset = Quaternion.Euler(90, 90, 0) instead of just public and this seems to do the job! For some reason, the Inspector was messing with things. Don't know, why the original plugin didn't declare it as static. Hope this might help anyone struggling with the same GoCube-Unity implementation.
Try converting the quaternion into Unity's left handed coordinate system.
The correct conversion is as follows:
Original quaternion:
q = w + xi + yj + zk
Transformed into opposite coordinate system:
q = w - xi - yj - zk
Since Unity constructs quaternion in this manner:
Quaternion q = new Quaternion(x, y, z, w)
the transformed Quaternion can be constructed as follows:
Quaternion qTrans = new Quaternion(-q.x, -q.y, -q.z, q.w)
I have a robot arm and a Microsoft Kinect 2.0. The Kinect can output 3D point clouds, which is offset by the Kinects origo and rotation. The robot arm can move its outermost wrist to XYZ positions (and rotations) given in mm.
Imagine that the Kinect is mounted on the arm.
The robot moves to position X:0, Y: 570, Z: -950, and rotates the wrist so that the Kinect points directly downwards (rotated -90 degrees around the Z-axis).
If I get a ColorMesh from the Kinect, how would I rotate and offset the mesh so that it is in robot space instead of 3D camera space? I don't know much about this so any help is appreciated.
Here's an illustration of the real-life setup in mind:
The sphere denotes the workspace reach of the arm.
I was figuring the class to look something like this:
public class CameraToRobotCalibrator
{
//offset in mm compared to robot origo
public const float x_offset = 0f;
public const float y_offset = 570f;
public const float z_offset = -950f;
//camera rotated around its own origo
public const float x_rotation = 0f;
public const float y_rotation = 0f;
public const float z_rotation = -90f;
public ColorMesh ConvertToRobospace(ColorMesh mesh)
{
//?
}
}
I'm guessing if it was for 1 point it wouldn't be too hard, but I don't know about an entire mesh with vertices and faces?
Given the specifications of the robot and its degree of freedom, you can usually calculate a 4x4 matrix that describes the endeffector's position and orientation. This is specific to the robot's design. Let's call this matrix E.
Then, you need to mount the Kinect on the endeffector. This will introduce another translation and possibly a rotation. So you need another matrix M that represents how the Kinect is mounted on the endeffector. It is the transform from the endeffector's coordinate system to the Kinect's coordinate system. Ideally, this matrix would be calibrated somehow. A good initial guess might be derived by measuring.
Then, the overall transform from the robot's root coordinate system to the Kinect coordinate system is T = R * M (assuming column-major matrices).
And this is the matrix that you need to transform your mesh. Transforming a mesh is just like transforming a single point. But now, you transform all vertex positions (add 1 as w-component) and all vertex normals (add 0 as w-component). Keep the face indices as they are.
For those interested in my implementation, see here:
http://pastebin.com/zfCdVFNE
You can probably find some better matrix library out there, I just use the Matrix4 from Kinect's API.
The properties were measured in real life and then calibrated after running a test with the robot and Kinect.
I figured someone might stumble upon the question some day and go 'but where is the CODE?'
I followed this guide which is basically matrices for dummies: http://www.codinglabs.net/article_world_view_projection_matrix.aspx
Few days ago I decided to start in 3D programming and came across perspective projection.
I use the following code to get the matrix:
public static Matrix3D ProjectionMatrix(double angle, double aspect, double near, double far)
{
double size = near * Math.Tan(MathUtils.DegreeToRadian(angle) / 2.0);
double left = -size, right = size, bottom = -size / aspect, top = size / aspect;
Matrix3D m = new Matrix3D(new double[,] {
{2*near/(right-left),0,(right + left)/(right - left),0},
{0,2*near/(top-bottom),(top+bottom)/(top-bottom),0},
{0,0,-(far+near)/(far-near),-(2 * far * near) / (far - near)},
{0,0,-1,0}
});
return m;
}
I use the following code for the camera:
Matrix3D Camera
{
get
{
Vector3D cameraZAxis = -this.LookDirection;
cameraZAxis.Normalize();
Vector3D cameraXAxis = Vector3D.CrossProduct(this.UpDirection, cameraZAxis);
cameraXAxis.Normalize();
Vector3D cameraYAxis = Vector3D.CrossProduct(cameraZAxis, cameraXAxis);
Vector3D cameraPosition = (Vector3D)this.Position;
double offsetX = -Vector3D.DotProduct(cameraXAxis, cameraPosition);
double offsetY = -Vector3D.DotProduct(cameraYAxis, cameraPosition);
double offsetZ = -Vector3D.DotProduct(cameraZAxis, cameraPosition);
return new Matrix3D(new double[,]{{cameraXAxis.X, cameraYAxis.X, cameraZAxis.X, 0},
{cameraXAxis.Y, cameraYAxis.Y, cameraZAxis.Y, 0},
{cameraXAxis.Z, cameraYAxis.Z, cameraZAxis.Z, 0},
{offsetX, offsetY, offsetZ, 1}});
}
}
However, I don't know how to get the Model or World Matrix, also is there anything wrong with the previous code?
A Matrix is used to transform an object from one "space" into another. Think of a Model which is a cube, the model center is 0,0,0 and each corner is at an extent of 5. Now to transform that into your world you would apply a Worldmatrix with the transformation to put these model coordinates into your world.
A translation for example which should "move" the model to 5, 5, 5. Now just think of adding this position to your model and all its points, and now these new coordinates are called "in world space". Now the model is, via your World matrix placed in the world at position 5,5,5 (your former model center resides now in your world at this position).
The view matrix is a little bit tricky to understand, but the easiest is to think of "We can't move a camera, so we move all ojects in the opposite direction so it looks like we moved the camera". This sounds difficult, but in fact its the same like transforming from model space to world space.
Now we transform from World space into view space. Finally, we need to get it on the screen. Obviously your screen is 2D and you still have a 3D scene, now you need a way to transform your 3D objects into your view space. This can be achieved with different kinds of projection. The two important ones are orthogonal projection and perspective projection. In fact this uses the Z component( which we don't have on a 2D surface like a screen) and the screen resolution to "project" our visible world into our screen space.
All this is easily accomplished using matrices. One for each transformation is a good start. To be fair, you don't need any of it, you could directly supply your data in screen space, but this wouldn't be practical. If you don't need a World matrix for example, which happens if your model is modeled so that it could be used directly, you would use an Matrix.Identity which, can roughly be interpreted as multiplying a number by 1 which gives you the same number. Also an Identity Matrix for the view is like having the camera placed in the world on 0,0,0 and looking down the Z axis (which axis heavly depends on the coordinate system you use)
To get the final matrix for a shader you usually pass all these matrices (or a combined one via multiplication) to your shader. If you use the fixed function pipeline there are usually methods to supply them. The projection is usually fixed, but could be changed to apply some visual effects like zooming a sniper rifle or a fisheye effect. The camera matrix of course is used to move and rotate the camera. And the world matrix is used to position your objects in the scene, move players, rotate doors etc.
Disclaimer: This is how i understood the whole thing for myself, so it is by no means a mathematical correct explanation, but maybe it is of any help for the OP.
To get a better understanding of the whole subject you can read this.
Let's say I have a data structure like the following:
Camera {
double x, y, z
/** ideally the camera angle is positioned to aim at the 0,0,0 point */
double angleX, angleY, angleZ;
}
SomePointIn3DSpace {
double x, y, z
}
ScreenData {
/** Convert from some point 3d space to 2d space, end up with x, y */
int x_screenPositionOfPt, y_screenPositionOfPt
double zFar = 100;
int width=640, height=480
}
...
Without screen clipping or much of anything else, how would I calculate the screen x,y position of some point given some 3d point in space. I want to project that 3d point onto the 2d screen.
Camera.x = 0
Camera.y = 10;
Camera.z = -10;
/** ideally, I want the camera to point at the ground at 3d space 0,0,0 */
Camera.angleX = ???;
Camera.angleY = ????
Camera.angleZ = ????;
SomePointIn3DSpace.x = 5;
SomePointIn3DSpace.y = 5;
SomePointIn3DSpace.z = 5;
ScreenData.x and y is the screen x position of the 3d point in space. How do I calculate those values?
I could possibly use the equations found here, but I don't understand how the screen width/height comes into play. Also, I don't understand in the wiki entry what is the viewer's position vers the camera position.
http://en.wikipedia.org/wiki/3D_projection
The 'way it's done' is to use homogenous transformations and coordinates. You take a point in space and:
Position it relative to the camera using the model matrix.
Project it either orthographically or in perspective using the projection matrix.
Apply the viewport trnasformation to place it on the screen.
This gets pretty vague, but I'll try and cover the important bits and leave some of it to you. I assume you understand the basics of matrix math :).
Homogenous Vectors, Points, Transformations
In 3D, a homogenous point would be a column matrix of the form [x, y, z, 1]. The final component is 'w', a scaling factor, which for vectors is 0: this has the effect that you can't translate vectors, which is mathematically correct. We won't go there, we're talking points.
Homogenous transformations are 4x4 matrices, used because they allow translation to be represented as a matrix multiplication, rather than an addition, which is nice and quick for your videocard. Also convenient because we can represent successive transformations by multiplying them together. We apply transformations to points by performing transformation * point.
There are 3 primary homogeneous transformations:
Translation,
Rotation, and
Scaling.
There are others, notably the 'look at' transformation, which are worth exploring. However, I just wanted to give a brief list and a few links. Successive application of moving, scaling and rotating applied to points is collectively the model transformation matrix, and places them in the scene, relative to the camera. It's important to realise what we're doing is akin to moving objects around the camera, not the other way around.
Orthographic and Perspective
To transform from world coordinates into screen coordinates, you would first use a projection matrix, which commonly, come in two flavors:
Orthographic, commonly used for 2D and CAD.
Perspective, good for games and 3D environments.
An orthographic projection matrix is constructed as follows:
Where parameters include:
Top: The Y coordinate of the top edge of visible space.
Bottom: The Y coordinate of the bottom edge of the visible space.
Left: The X coordinate of the left edge of the visible space.
Right: The X coordinate of the right edge of the visible space.
I think that's pretty simple. What you establish is an area of space that is going to appear on the screen, which you can clip against. It's simple here, because the area of space visible is a rectangle. Clipping in perspective is more complicated because the area which appears on screen or the viewing volume, is a frustrum.
If you're having a hard time with the wikipedia on perspective projection, Here's the code to build a suitable matrix, courtesy of geeks3D
void BuildPerspProjMat(float *m, float fov, float aspect,
float znear, float zfar)
{
float xymax = znear * tan(fov * PI_OVER_360);
float ymin = -xymax;
float xmin = -xymax;
float width = xymax - xmin;
float height = xymax - ymin;
float depth = zfar - znear;
float q = -(zfar + znear) / depth;
float qn = -2 * (zfar * znear) / depth;
float w = 2 * znear / width;
w = w / aspect;
float h = 2 * znear / height;
m[0] = w;
m[1] = 0;
m[2] = 0;
m[3] = 0;
m[4] = 0;
m[5] = h;
m[6] = 0;
m[7] = 0;
m[8] = 0;
m[9] = 0;
m[10] = q;
m[11] = -1;
m[12] = 0;
m[13] = 0;
m[14] = qn;
m[15] = 0;
}
Variables are:
fov: Field of view, pi/4 radians is a good value.
aspect: Ratio of height to width.
znear, zfar: used for clipping, I'll ignore these.
and the matrix generated is column major, indexed as follows in the above code:
0 4 8 12
1 5 9 13
2 6 10 14
3 7 11 15
Viewport Transformation, Screen Coordinates
Both of these transformations require another matrix matrix to put things in screen coordinates, called the viewport transformation. That's described here, I won't cover it (it's dead simple).
Thus, for a point p, we would:
Perform model transformation matrix * p, resulting in pm.
Perform projection matrix * pm, resulting in pp.
Clipping pp against the viewing volume.
Perform viewport transformation matrix * pp, resulting is ps: point on screen.
Summary
I hope that covers most of it. There are holes in the above and it's vague in places, post any questions below. This subject is usually worthy of a whole chapter in a textbook, I've done my best to distill the process, hopefully to your advantage!
I linked to this above, but I strongly suggest you read this, and download the binary. It's an excellent tool to further your understanding of theses transformations and how it gets points on the screen:
http://www.songho.ca/opengl/gl_transform.html
As far as actual work, you'll need to implement a 4x4 matrix class for homogeneous transformations as well as a homogeneous point class you can multiply against it to apply transformations (remember, [x, y, z, 1]). You'll need to generate the transformations as described above and in the links. It's not all that difficult once you understand the procedure. Best of luck :).
#BerlinBrown just as a general comment, you ought not to store your camera rotation as X,Y,Z angles, as this can lead to an ambiguity.
For instance, x=60degrees is the same as -300 degrees. When using x,y and z the number of ambiguous possibilities are very high.
Instead, try using two points in 3D space, x1,y1,z1 for camera location and x2,y2,z2 for camera "target". The angles can be backward computed to/from the location/target but in my opinion this is not recommended. Using a camera location/target allows you to construct a "LookAt" vector which is a unit vector in the direction of the camera (v'). From this you can also construct a LookAt matrix which is a 4x4 matrix used to project objects in 3D space to pixels in 2D space.
Please see this related question, where I discuss how to compute a vector R, which is in the plane orthogonal to the camera.
Given a vector of your camera to target, v = xi, yj, zk
Normalise the vector, v' = xi, yj, zk / sqrt(xi^2 + yj^2 + zk^2)
Let U = global world up vector u = 0, 0, 1
Then we can compute R = Horizontal Vector that is parallel to the camera's view direction R = v' ^ U,
where ^ is the cross product, given by
a ^ b = (a2b3 - a3b2)i + (a3b1 - a1b3)j + (a1b2 - a2b1)k
This will give you a vector that looks like this.
This could be of use for your question, as once you have the LookAt Vector v', the orthogonal vector R you can start to project from the point in 3D space onto the camera's plane.
Basically all these 3D manipulation problems boil down to transforming a point in world space to local space, where the local x,y,z axes are in orientation with the camera. Does that make sense? So if you have a point, Q=x,y,z and you know R and v' (camera axes) then you can project it to the "screen" using simple vector manipulations. The angles involved can be found out using the dot product operator on Vectors.
Following the wikipedia, first calculate "d":
http://upload.wikimedia.org/wikipedia/en/math/6/0/b/60b64ec331ba2493a2b93e8829e864b6.png
In order to do this, build up those matrices in your code. The mappings from your examples to their variables:
θ = Camera.angle*
a = SomePointIn3DSpace
c = Camera.x | y | z
Or, just do the equations separately without using matrices, your choice:
http://upload.wikimedia.org/wikipedia/en/math/1/c/8/1c89722619b756d05adb4ea38ee6f62b.png
Now we calculate "b", a 2D point:
http://upload.wikimedia.org/wikipedia/en/math/2/5/6/256a0e12b8e6cc7cd71fa9495c0c3668.png
In this case ex and ey are the viewer's position, I believe in most graphics systems half the screen size (0.5) is used to make (0, 0) the center of the screen by default, but you could use any value (play around). ez is where the field of view comes into play. That's the one thing you were missing. Choose a fov angle and calculate ez as:
ez = 1 / tan(fov / 2)
Finally, to get bx and by to actual pixels, you have to scale by a factor related to the screen size. For example, if b maps from (0, 0) to (1, 1) you could just scale x by 1920 and y by 1080 for a 1920 x 1080 display. That way any screen size will show the same thing. There are of course many other factors involved in an actual 3D graphics system but this is the basic version.
Converting points in 3D-space into a 2D point on a screen is simply made by using a matrix. Use a matrix to calculate the screen position of your point, this saves you a lot of work.
When working with cameras you should consider using a look-at-matrix and multiply the look at matrix with your projection matrix.
Assuming the camera is at (0, 0, 0) and pointed straight ahead, the equations would be:
ScreenData.x = SomePointIn3DSpace.x / SomePointIn3DSpace.z * constant;
ScreenData.y = SomePointIn3DSpace.y / SomePointIn3DSpace.z * constant;
where "constant" is some positive value. Setting it to the screen width in pixels usually gives good results. If you set it higher then the scene will look more "zoomed-in", and vice-versa.
If you want the camera to be at a different position or angle, then you will need to move and rotate the scene so that the camera is at (0, 0, 0) and pointed straight ahead, and then you can use the equations above.
You are basically computing the point of intersection between a line that goes through the camera and the 3D point, and a vertical plane that is floating a little bit in front of the camera.
You might be interested in just seeing how GLUT does it behind the scenes. All of these methods have similar documentation that shows the math that goes into them.
The three first lectures from UCSD might be very helful, and contain several illustrations on this topic, which as far as I can see is what you are really after.
Run it thru a ray tracer:
Ray Tracer in C# - Some of the objects he has will look familiar to you ;-)
And just for kicks a LINQ version.
I'm not sure what the greater purpose of your app is (you should tell us, it might spark better ideas), but while it is clear that projection and ray tracing are different problem sets, they have a ton of overlap.
If your app is just trying to draw the entire scene, this would be great.
Solving problem #1: Obscured points won't be projected.
Solution: Though I didn't see anything about opacity or transparency on the blog page, you could probably add these properties and code to process one ray that bounced off (as normal) and one that continued on (for the 'transparency').
Solving problem #2: Projecting a single pixel will require a costly full-image tracing of all pixels.
Obviously if you just want to draw the objects, use the ray tracer for what it's for! But if you want to look up thousands of pixels in the image, from random parts of random objects (why?), doing a full ray-trace for each request would be a huge performance dog.
Fortunately, with more tweaking of his code, you might be able to do one ray-tracing up front (with transparancy), and cache the results until the objects change.
If you're not familiar to ray tracing, read the blog entry - I think it explains how things really work backwards from each 2D pixel, to the objects, then the lights, which determines the pixel value.
You can add code so as intersections with objects are made, you are building lists indexed by intersected points of the objects, with the item being the current 2d pixel being traced.
Then when you want to project a point, go to that object's list, find the nearest point to the one you want to project, and look up the 2d pixel you care about. The math would be far more minimal than the equations in your articles. Unfortunately, using for example a dictionary of your object+point structure mapping to 2d pixels, I am not sure how to find the closest point on an object without running through the entire list of mapped points. Although that wouldn't be the slowest thing in the world and you could probably figure it out, I just don't have the time to think about it. Anyone?
good luck!
"Also, I don't understand in the wiki entry what is the viewer's position vers the camera position" ... I'm 99% sure this is the same thing.
You want to transform your scene with a matrix similar to OpenGL's gluLookAt and then calculate the projection using a projection matrix similar to OpenGL's gluPerspective.
You could try to just calculate the matrices and do the multiplication in software.
The setup: I'm using a cubemap projection to create a planet out of blocks. A cubemap projection is quite simple: take the vector from the center of a cube to any point on that cube, normalize it, then multiply that by the radius of a sphere and you have your coordinate's new position. Here's a quick illustration in 2D:
[link]
Now, as I said, I've created this so that it's made of blocks. So in practice, I divide my cube into equal square subdivisions (like a rubik's cube). I use a custom coordinate: (Face, X, Y, Shell). Face refers to which face on the cube the point is on. X and Y refer to its position on the face. Shell refers to its 'height'. In practice this translates into the radius of the sphere I project the point onto. If I haven't explained it well, hopefully an image will help:
[link]
--That's a planet generated with an entirely random heightmap, with backface culling turned off. Anyways, now that you have the idea of what I'm working with--
My problem is that I cannot get backface culling to work predictably. My current system works as follows:
Calculate the center of the block
Get the normal of the vertices on each triangle of the block by taking the cross product of two sides of the triangle
Get the vector from the center of the triangle (the average of the triangle's vertices) to the center of the block, normalize it.
Take the dot product of the normal of the triangle and the normal to the center of the block
If the dot product is >= 0, flip the first and last indices of the triangle
Here's that in code:
public bool CheckIndices(Quad q, Vector3 centerOfBlock)
{
Vector3[] vertices = new Vector3[3];
for (int v = 0; v < 3; v++)
vertices[v] = q.Corners[indices[v]].Position;
Vector3 center = (vertices[0] + vertices[1] + vertices[2]) / 3f;
Vector3 normal = Vector3.Cross(vertices[1] - vertices[0], vertices[2] - vertices[0]);
Vector3 position = center - centerOfBlock;
position.Normalize();
normal.Normalize();
float dotProduct = Vector3.Dot(position, normal);
if (dotProduct >= 0)
{
int swap = indices[0];
indices[0] = indices[2];
indices[2] = swap;
return false;
}
return true;
}
I use a Quad class to hold triangles and some other data. Triangles store an int[3] for indices which correspond to the vertices stored in Quad.
However, when I use this method, at least half of the faces are drawn in the wrong direction. I have noticed two patterns in the problem:
Faces which point outward from the center of the planet are always correct
Faces which point inward toward the center of the planet are always incorrect
This led me to believe that my calculated center of the block was incorrect and in fact somewhere between the block and the center of the planet. However, changing my calculations for the center of the block was ineffective.
I have used two different methods to calculate the center of the block. The first was to find the projected position of a coordinate which had +.5 X, +.5 Y, and +.5 Shell (Z) from the block's position. Because I define block position using the bottom-left-back corner, this new coordinate would naturally be in the center of the block. The other method I use is to calculate the real position of each corner of the block and then average these vectors. This method seemed pretty foolproof to me, yet it did not succeed.
For this reason I am beginning to doubt the code I pasted above which determines if a triangle must be flipped. I do not remember all of the reasoning behind some of the logic, specifically behind the >= 0 statement. I just need another pair of eyes: is something wrong here?
The problem was that I was being too general in my cubemap projection when I got the position of an arbitrary point on a cube. Compare the GetCorrectedCubePosition method here to the same method here to see the improvements made. The methods for clockwise index order checking I noted in my post should are still unknown in effectiveness, as I won't be using them anymore. Using a correct projection means I can hard-define my vertices as clockwise in the generation methods themselves instead of having to guess.