I am building a ray Tracer from scratch. My question is:
When I change camera coordinates the Sphere changes to ellipse. I don't understand why it's happening.
Here are some images to show the artifacts:
Sphere: 1 1 -1 1.0 (Center, radius)
Camera: 0 0 5 0 0 0 0 1 0 45.0 1.0 (eyepos, lookat, up, foy, aspect)
But when I changed camera coordinate, the sphere looks distorted as shown below:
Camera: -2 -2 2 0 0 0 0 1 0 45.0 1.0
I don't understand what is wrong. If someone can help that would be great!
I set my imagePlane as follows:
//Computing u,v,w axes coordinates of Camera as follows:
{
Vector a = Normalize(eye - lookat); //Camera_eye - Camera_lookAt
Vector b = up; //Camera Up Vector
m_w = a;
m_u = b.cross(m_w);
m_u.normalize();
m_v = m_w.cross(m_u);
}
After that I compute directions for each pixel from the Camera position (eye) as mentioned below:
//Then Computing direction as follows:
int half_w = m_width * 0.5;
int half_h = m_height * 0.5;
double half_fy = fovy() * 0.5;
double angle = tan( ( M_PI * half_fy) / (double)180.0 );
for(int k=0; k<pixels.size(); k++){
double j = pixels[k].x(); //width
double i = pixels[k].y(); //height
double XX = aspect() * angle * ( (j - half_w ) / (double)half_w );
double YY = angle * ( (half_h - i ) / (double)half_h );
Vector dir = (m_u * XX + m_v * YY) - m_w ;
directions.push_back(dir);
}
After that:
for each dir:
Ray ray(eye, dir);
int depth = 0;
t_color += Trace(g_primitive, ray, depth);
After playing a lot and with the help of the comments of all you guys I was able to create successfully my rayTracer properly. Sorry for answering late, but I would like to close this thread with few remarks.
So, the above mentioned code is perfectly correct. Based on my own assumptions (as mentioned in above comments) I have decided to set my Camera parameters like that.
The problem I mentioned above is a normal behaviour of the camera (as also mentioned above in the comments).
I have got good results now but there are few things to check while coding a rayTracer:
Always make sure to take care of Radians to Degrees (or vice versa) conversion while computing FOV and ASPECT RATIO. I did it as follows:
double angle = tan((M_PI * 0.5 * fovy) / 180.0);
double y = angle;
double x = aspect * angle;
2) While computing Triangle intersections, make sure to implement cross product properly.
3) While using intersections of different objects make sure to find the intersection which is at a minimum distance from the camera.
Here's the result I got:
Above is a very simple model (courtesy UCBerkeley), which I rayTraced.
This is the correct behavior. Get a camera with a wide angle lens, put the sphere near the edge of the field of view and take a picture. Then in a photo app draw a circle on top of the photo of the sphere and you will see that it's not a circular projection.
This effect will be magnified by the fact that you set aspect to 1.0 but your image is not square.
A few things to fix:
A direction vector is (to - from). You have (from - to), so a is pointing backward. You'll want to add m_w at the end, rather than subtract it. Also, this fix will rotate your m_u,m_v by 180 degrees, which will make you about to change (j - half_w) to (half_w - j).
Also, putting all the pixels and all the directions in lists is not as efficient as just looping over x,y values.
Related
I am currently trying to get the rotation of an object. I am using C++ and Bullet Physics. This is my code:
btScalar x, y, z;
body[0]->getCenterOfMassTransform().getBasis().getEulerZYX(z, y, x);
However, as I rotate the object around clockwise the number I get from the y (y is vertical in Bullet) axis goes from 0 to -90 to 0 to 90 and finally back to 0 for every quarter rotation. It is close but what I need is for it to go all the way from 0 to 360.
Bullet documentation says:
void getEulerZYX (btScalar &yaw, btScalar &pitch, btScalar &roll, unsigned int solution_number=1) const
and
solution_number Which solution of two possible solutions ( 1 or 2) are possible values
this is because euler angles are ambigous. have you tried solution 2?
I had the same problem. I using LibGDX with Bullet engine, so my code sample on Java, but I'm sure, that it will works on C++ too. Here is my solution (for Z axis):
body.getWorldTransform().getRotation(mRotation);
// That gives you an angle in all range but excluding (85, 95) and (-95, 85). For other axis you can try to get Pitch or Yaw.
float roll = mRotation.getRoll();
// That gives you an angle in range [0, 240). Clockwise and counterclockwise directions isn't detected.
float angle = mRotation.getAngleAround(0, 0, 1);
// Usually 0, but on (85, 95) and (-95, 85) becomes 1 and -1.
int gimbalPole = mRotation.getGimbalPole();
// Using roll (pitch/yaw for other axis) if it's defined, and using angle with gimble pole otherwise.
float rotation = (gimbalPole == 0) ? roll : angle * gimbalPole;
Obtained rotation will be in range (-180, 180). It can be easily converted to [0, 360) range:
if (rotation < 0) rotation += 360;
I have a 3D scene with a movable camera. I have the 3D coordinates for that camera (x, y, z -> Y being the height) and the X and Y rotation (Up/Down, Left/Right).
I want get the coordinates (x1, z1) in the floor where I'm looking at.
Basically if the camera is at (0, 4096, 0) (4096 is the height) and my xRotation is 45º and my yRotation is 0, I will be looking at the point on the floor (4096, 0, 0)
I was trying to program it but i got stuck with the trigonometry. Help me with it.
The following code is what I have right now and not fully working:
float x1, z1, anguloX, anguloY;
anguloX = (90 - Xrotation) / 180 * Pi;
anguloY = (90 - Yrotation) / 180 * Pi;
x1 = Yposition * tan(anguloX) * cos(anguloY);
z1 = Yposition * tan(anguloY) * cos(anguloY);
x1 += Xposition;
z1 += Zposition;
Do not bother yourself with this kind of trigonometry, It is often more practical and understandable to use matrices and transformation to do this kind of stuff. I suggest to try this approach instead which is called Ray Casting:
Calculate the direction of camera sight, you must get the looking
direction of camera
Use ray casting to determine the intersection point of sight ray and
scene meshes(or just a plane that represents the floor).
Now about the first, here is the pseudo code :
XRotMat = CreateRotationMatrixAroundXAxis(verticalCameraAngel);
YRotMat = CreateRotationMatrixAroundYAxis(horizentalCameraAngel);
CameraSightDir = YRotMat*XRotMa*initialCameraDir;
and about the second step:
SightRay.Source = Camera.Position;
SightRay.Direction = CameraSightDir;
Intersection = IntersectRayWithPlane(SightRay , FloorPlane);
IntersectRayWithPlane is quite a simple procedure, you can read about it here.
The problem is I have two points in 3D space where y+ is up, x+ is to the right, and z+ is towards you. I want to orientate a cylinder between them that is the length of of the distance between both points, so that both its center ends touch the two points. I got the cylinder to translate to the location at the center of the two points, and I need help coming up with a rotation matrix to apply to the cylinder, so that it is orientated the correct way. My transformation matrix for the entire thing looks like this:
translate(center point) * rotateX(some X degrees) * rotateZ(some Z degrees)
The translation is applied last, that way I can get it to the correct orientation before I translate it.
Here is what I have so far for this:
mat4 getTransformation(vec3 point, vec3 parent)
{
float deltaX = point.x - parent.x;
float deltaY = point.y - parent.y;
float deltaZ = point.z - parent.z;
float yRotation = atan2f(deltaZ, deltaX) * (180.0 / M_PI);
float xRotation = atan2f(deltaZ, deltaY) * (180.0 / M_PI);
float zRotation = atan2f(deltaX, deltaY) * (-180.0 / M_PI);
if(point.y < parent.y)
{
zRotation = atan2f(deltaX, deltaY) * (180.0 / M_PI);
}
vec3 center = vec3((point.x + parent.x)/2.0, (point.y + parent.y)/2.0, (point.z + parent.z)/2.0);
mat4 translation = Translate(center);
return translation * RotateX(xRotation) * RotateZ(zRotation) * Scale(radius, 1, radius) * Scale(0.1, 0.1, 0.1);
}
I tried a solution given down below, but it did not seem to work at all
mat4 getTransformation(vec3 parent, vec3 point)
{
// moves base of cylinder to origin and gives it unit scaling
mat4 scaleFactor = Translate(0, 0.5, 0) * Scale(radius/2.0, 1/2.0, radius/2.0) * cylinderModel;
float length = sqrtf(pow((point.x - parent.x), 2) + pow((point.y - parent.y), 2) + pow((point.z - parent.z), 2));
vec3 direction = normalize(point - parent);
float pitch = acos(direction.y);
float yaw = atan2(direction.z, direction.x);
return Translate(parent) * Scale(length, length, length) * RotateX(pitch) * RotateY(yaw) * scaleFactor;
}
After running the above code I get this:
Every black point is a point with its parent being the point that spawned it (the one before it) I want the branches to fit into the points. Basically I am trying to implement the space colonization algorithm for random tree generation. I got most of it, but I want to map the branches to it so it looks good. I can use GL_LINES just to make a generic connection, but if I get this working it will look so much prettier. The algorithm is explained here.
Here is an image of what I am trying to do (pardon my paint skills)
Well, there's an arbitrary number of rotation matrices satisfying your constraints. But any will do. Instead of trying to figure out a specific rotation, we're just going to write down the matrix directly. Say your cylinder, when no transformation is applied, has its axis along the Z axis. So you have to transform the local space Z axis toward the direction between those two points. I.e. z_t = normalize(p_1 - p_2), where normalize(a) = a / length(a).
Now we just need to make this a full 3 dimensional coordinate base. We start with an arbitrary vector that's not parallel to z_t. Say, one of (1,0,0) or (0,1,0) or (0,0,1); use the scalar product ·(also called inner, or dot product) with z_t and use the vector for which the absolute value is the smallest, let's call this vector u.
In pseudocode:
# Start with (1,0,0)
mindotabs = abs( z_t · (1,0,0) )
minvec = (1,0,0)
for u_ in (0,1,0), (0,0,1):
dotabs = z_t · u_
if dotabs < mindotabs:
mindotabs = dotabs
minvec = u_
u = minvec_
Then you orthogonalize that vector yielding a local y transformation y_t = normalize(u - z_t · u).
Finally create the x transformation by taking the cross product x_t = z_t × y_t
To move the cylinder into place you combine that with a matching translation matrix.
Transformation matrices are effectively just the axes of the space you're "coming from" written down as if seen from the other space. So the resulting matrix, which is the rotation matrix you're looking for is simply the vectors x_t, y_t and z_t side by side as a matrix. OpenGL uses so called homogenuous matrices, so you have to pad it to a 4×4 form using a 0,0,0,1 bottommost row and rightmost column.
That you can load then into OpenGL; if using fixed functio using glMultMatrix to apply the rotation, or if using shader to multiply onto the matrix you're eventually pass to glUniform.
Begin with a unit length cylinder which has one of its ends, which I call C1, at the origin (note that your image indicates that your cylinder has its center at the origin, but you can easily transform that to what I begin with). The other end, which I call C2, is then at (0,1,0).
I'd like to call your two points in world coordinates P1 and P2 and we want to locate C1 on P1 and C2 to P2.
Start with translating the cylinder by P1, which successfully locates C1 to P1.
Then scale the cylinder by distance(P1, P2), since it originally had length 1.
The remaining rotation can be computed using spherical coordinates. If you're not familiar with this type of coordinate system: it's like GPS coordinates: two angles; one around the pole axis (in your case the world's Y-axis) which we typically call yaw, the other one is a pitch angle (in your case the X axis in model space). These two angles can be computed by converting P2-P1 (i.e. the local offset of P2 with respect to P1) into spherical coordinates. First rotate the object with the pitch angle around X, then with yaw around Y.
Something like this will do it (pseudo-code):
Matrix getTransformation(Point P1, Point P2) {
float length = distance(P1, P2);
Point direction = normalize(P2 - P1);
float pitch = acos(direction.y);
float yaw = atan2(direction.z, direction.x);
return translate(P1) * scaleY(length) * rotateX(pitch) * rotateY(yaw);
}
Call the axis of the cylinder A. The second rotation (about X) can't change the angle between A and X, so we have to get that angle right with the first rotation (about Z).
Call the destination vector (the one between the two points) B. Take -acos(BX/BY), and that's the angle of the first rotation.
Take B again, ignore the X component, and look at its projection in the (Y, Z) plane. Take acos(BZ/BY), and that's the angle of the second rotation.
This is what I have
I have a plane in 2D X,Y
I set his destination by clicking on the screen X', Y'
I calculate the angle it needs to turn to face this destination with:
// Calculate the angle between plane position and destination point
CVector3 facingVec = m_vDestination - m_vPosition;
fAngle = -Math::radiansToDegrees ( (float)atan2f(m_vDestination.x - m_vPosition.x, m_vDestination.y - m_vPosition.y) ) ;
//This doesn't work, when rotating from ex. 350 degree to 0
//plane has to go all the way around 360,350,340,330,
//...,120,...100,90,..down to zero
float angleToTurn = fAngle - m_fRotationAngle;
if(angleToTurn < 0)
{
angleToTurn += 360.0f;
}
m_fRotationAngle += (angleToTurn) / 5;
// Move the unit towards the calculated angle m_fRotationAngle
m_vDirection.x = (-sin(Math::degreesToRadians(m_fRotationAngle)));
m_vDirection.y = (cos(Math::degreesToRadians(m_fRotationAngle)));
m_vPosition += ( 2 * m_vDirection * fDelta);
This is how it looks like
YT Video - sorry for the demo version, i couldn't get anything free at this moment.
This is what I need
I need this to behave properly, let's say plane is rotated at angle 350.
I set the destination and new angle should be 15.
Instead of going: 350,340,330,320,310,300,290,...10,0,15
It should continue: 350,0,15
Hope you can help me out with this guys, I've already dropped bezier approach - and I'm struggling with this since few days now.
If I read this correctly, you're trying to find the smallest angle to interpolate between the two vectors? If so, the following algorithm should work:
Find the angle of the first vector, relative to the fixed vector [1, 0]. This is a1.
Find the angle of the second vector, relative to the fixed vector [1, 0]. This is a2.
Let da = a2 - a1.
if da > 180, da -= 360;
else if da < 180, da += 360;
You need to calculate the angles with respect to another third vector [1, 0] so you can determine weather to rotate left or right.
Edit: I saw your YouTube link was broken, now I see it's working again. I think my answer is what you're after.
I've been struggling with this for a good while now. I'm trying to determine the screen coordinates of the vertexes in a model on the screen of my NDS using devKitPro. The library seems to implement some functionality of OpenGL, but in particular, the gluProject function is missing, which would (I assume) allow me to do just exactly that, easily.
I've been trying for a good while now to calculate the screen coordinates manually using the projection matricies that are stored in the DS's registers, but I haven't been having much luck, even when trying to build the projection matrix from scratch based on OpenGL's documentation. Here is the code I'm trying to use:
void get2DPoint(v16 x, v16 y, v16 z, float &result_x, float &result_y)
{
//Wait for the graphics engine to be ready
/*while (*(int*)(0x04000600) & BIT(27))
continue;*/
//Read in the matrix that we're currently transforming with
double currentMatrix[4][4]; int i;
for (i = 0; i < 16; i++)
currentMatrix[0][i] =
(double(((int*)0x04000640)[i]))/(double(1<<12));
//Now this hurts-- take that matrix, and multiply it by the projection matrix, so we obtain
//proper screen coordinates.
double f = 1.0 / tan(70.0/2.0);
double aspect = 256.0/192.0;
double zNear = 0.1;
double zFar = 40.0;
double projectionMatrix[4][4] =
{
{ (f/aspect), 0.0, 0.0, 0.0 },
{ 0.0, f, 0.0, 0.0 },
{ 0.0, 0.0, ((zFar + zNear) / (zNear - zFar)), ((2*zFar*zNear)/(zNear - zFar)) },
{ 0.0, 0.0, -1.0, 0.0 },
};
double finalMatrix[4][4];
//Ugh...
int mx = 0; int my = 0;
for (my = 0; my < 4; my++)
for (mx = 0; mx < 4; mx++)
finalMatrix[mx][my] =
currentMatrix[my][0] * projectionMatrix[0][mx] +
currentMatrix[my][1] * projectionMatrix[1][mx] +
currentMatrix[my][2] * projectionMatrix[2][mx] +
currentMatrix[my][3] * projectionMatrix[3][mx] ;
double dx = ((double)x) / (double(1<<12));
double dy = ((double)y) / (double(1<<12));
double dz = ((double)z) / (double(1<<12));
result_x = dx*finalMatrix[0][0] + dy*finalMatrix[0][1] + dz*finalMatrix[0][2] + finalMatrix[0][3];
result_y = dx*finalMatrix[1][0] + dy*finalMatrix[1][1] + dz*finalMatrix[1][2] + finalMatrix[1][3];
result_x = ((result_x*1.0) + 4.0)*32.0;
result_y = ((result_y*1.0) + 4.0)*32.0;
printf("Result: %f, %f\n", result_x, result_y);
}
There are lots of shifts involved, the DS works internally using fixed point notation and I need to convert that to doubles to work with. What I'm getting seems to be somewhat correct-- the pixels are translated perfectly if I'm using a flat quad that's facing the screen, but the rotation is wonky. Also, since I'm going by the projection matrix (which accounts for the screen width/height?) the last steps I'm needing to use don't seem right at all. Shouldn't the projection matrix be accomplishing the step up to screen resolution for me?
I'm rather new to all of this, I've got a fair grasp on matrix math, but I'm not as skilled as I would like to be in 3D graphics. Does anyone here know a way, given the 3D, non-transformed coordinates of a model's vertexes, and also given the matricies which will be applied to it, to actually come up with the screen coordinates, without using OpenGL's gluProject function? Can you see something blatantly obvious that I'm missing in my code? (I'll clarify when possible, I know it's rough, this is a prototype I'm working on, cleanliness isn't a high priority)
Thanks a bunch!
PS: As I understand it, currentMatrix, which I pull from the DS's registers, should be giving me the combined projection, translation, and rotation matrix, as it should be the exact matrix that's going to be used for the translation by the DS's own hardware, at least according to the specs at GBATEK. In practise, it doesn't seem to actually have the projection coordinates applied to it, which I suppose has something to do with my issues. But I'm not sure, as calculating the projection myself isn't generating different results.
That is almost correct.
The correct steps are:
Multiply Modelview with Projection matrix (as you've already did).
Extend your 3D vertex to a homogeneous coordinate by adding a W-component with value 1. E.g your (x,y,z)-vector becomes (x,y,z,w) with w = 1.
Multiply this vector with the matrix product. Your matrix should be 4x4 and your vector of size 4. The result will be a vector of size4 as well (don't drop w yet!). The result of this multiplication is your vector in clip-space. FYI: You can already do a couple of very useful things here with this vector: Test if the point is on the screen. The six conditions are:
x < -w : Point is outside the screen (left of the viewport)
x > W : Point is outside the screen (right of the viewport)
y < -w : Point is outside the screen (above the viewport)
y > w : Point is outside the screen (below the viewport)
z < -w : Point is outside the screen (beyond znear)
z > w : Point is outside the screen (beyond zfar)
Project your point into 2D space. To do this divide x and y by w:
x' = x / w;
y' = y / w;
If you're interested in the depth-value (e.g. what gets written to the zbuffer) you can project z as well:
z' = z / w
Note that the previous step won't work if w is zero. This case happends if your point is equal to the camera position. The best you could do in this case is to set x' and y' to zero. (will move the point into the center of the screen in the next step..).
Final Step: Get the OpenGL viewport coordinates and apply it:
x_screen = viewport_left + (x' + 1) * viewport_width * 0.5;
y_screen = viewport_top + (y' + 1) * viewport_height * 0.5;
Important: The y coordinate of your screen may be upside down. Contrary to most other graphic APIs in OpenGL y=0 denotes the bottom of the screen.
That's all.
I'll add some more thoughts to Nils' thorough answer.
don't use doubles. I'm not familiar with NDS, but I doubt it's got any hardware for double math.
I also doubt model view and projection are not already multiplied if you are reading the hardware registers. I have yet to see a hardware platform that does not use the full MVP in the registers directly.
the matrix storage into registers may or may not be in the same order as OpenGL. if they are not, the multiplication matrix-vector needs to be done in the other order.