   ray tracing camera placement
  HelloI am writing a ray tracer. Still far, I have placed my camera on the z axis at a distance given by the user, and looking into the z axis. The view plane is situated at one unit distance from the camera. I get good results, so now I wanted to take it to the next level and control the camera using a position and a look at vector. The problem is that I am not really sure how I can do that. The various posts on the Internet narrow it down to two possibilities: either transform the screen, or each ray. I would prefer the first version, but, again, got stuck trying to implement it. Here is my code (with camera on the z-axis) :// Compute field of view valuefloat fovx = (float)fov*PI/(float)180;// Slice of FOV-cone, 1 unit distance away from camerafloat radius = tan(fovx/(float)2);// Aspect ratio of the screenfloat ratio = (float)width/(float)height;// Angle of aspect ratiofloat beta = atan(1.0/ratio);// Width and height of picture plane on slice of FOV-conefloat picw = 2 * radius * cos(beta);float pich = 2 * radius * sin(beta);// Pixel size valuesfloat psx =  picw / (float)width;float psy = -pich / (float)height;// Start corner of picture plane, in world spacefloat start_x = viewpoint.x - picw/(float)2;float start_y = viewpoint.y + pich/(float)2;float start_z = viewpoint.z - 1;Point EndPoint;POINT_map(&EndPoint, start_x, start_y, start_z);//---------------------------------------- for (i = 0 ; i < height ; i ++){for (j = 0 ; j < width ; j ++){Fire_Ray(viewpoint, EndPoint);// Move one pixel on the x axisEndPoint.x += psx;}// New lineEndPoint.x = start_x;// Move one pixel on the y axisEndPoint.y += psy;}Thank you   
  As you most probably have your geometry in world space, than you want to transform the ray in the same space. I can't look at the code right now, but from a first look there is no code related to the actual transformation: are you using a look at matrix?   ;  Yes, I am using a look-at matrix.So I am basically doing this : place camera at (0 0 0)place screen at (0 0 1)compute P1 P2 P3 P4 - screen cornersThen take the input parameters : viewpoint + look@ and compute the new axis :zaxis = normal(look@ - viewpoint)xaxis = normal(cross((0 1 0), zaxis))yazis = cross(zaxis, xaxis)Then I transform the screen corners using the transformation matrix.And then compute Dx, Dy, the interpolation vectorsvoid Tracer(){// Compute field of view valuefloat fovx = (float)W.fov*PI/(float)180;// Slice of FOV-cone, 1 unit distance away from camerafloat radius = tan(fovx/(float)2);// Aspect ratio of the screenfloat ratio = (float)width/(float)height;// Angle of aspect ratiofloat beta = atan(1.0/ratio);// Width and height of picture plane on slice of FOV-conefloat picw = 2 * radius * cos(beta);float pich = 2 * radius * sin(beta);// Pixel size valuesfloat psx =  picw / (float)width;float psy = -pich / (float)height;// Start corner of picture plane, in world spacefloat x = viewpoint.x - picw/(float)2;float y = viewpoint.y + pich/(float)2;float z = viewpoint.z + 1;// Matrix testingPoint X_axis, Y_axis, Z_axis;Point up, aux;Z_axis.x = (look_at.x - viewpoint.x);Z_axis.y = (look_at.y - viewpoint.y);Z_axis.z = (look_at.z - viewpoint.z);POINT_normalize(&Z_axis);POINT_map(&up, 0, 1, 0);POINT_map_cross(&X_axis, &up, &Z_axis);POINT_normalize(&X_axis);POINT_map_cross(&Y_axis, &Z_axis, &X_axis);POINT_normalize(&Y_axis);Matrix M;M.mcell[0] = X_axis.x;M.mcell[1] = Y_axis.x;M.mcell[2] = Z_axis.x;M.mcell[4] = X_axis.y;M.mcell[5] = Y_axis.y;M.mcell[6] = Z_axis.y;M.mcell[8] = X_axis.z;M.mcell[9] = Y_axis.z;M.mcell[10] = Z_axis.z;M.mcell[3] = W.viewpoint.x;M.mcell[7] = W.viewpoint.y;M.mcell[11] = W.viewpoint.z;POINT_map(&P1, (W.viewpoint.x - picw/(float)2), (W.viewpoint.y + pich/(float)2), 1);POINT_map(&P2, (W.viewpoint.x + picw/(float)2), (W.viewpoint.y + pich/(float)2), 1);POINT_map(&P3, (W.viewpoint.x + picw/(float)2), (W.viewpoint.y - pich/(float)2), 1);POINT_map(&P4, (W.viewpoint.x - picw/(float)2), (W.viewpoint.y - pich/(float)2), 1);MATRIX_Transform(&M, &P1, &new_P1);MATRIX_Transform(&M, &P2, &new_P2);MATRIX_Transform(&M, &P3, &new_P3);MATRIX_Transform(&M, &P4, &new_P4);Point Dx, Dy;Dx.x = (new_P2.x - new_P1.x) / (float)width;Dx.y = (new_P2.y - new_P1.y) / (float)width;Dx.z = (new_P2.z - new_P1.z) / (float)width;Dy.x = (new_P4.x - new_P1.x) / (float)height;Dy.y = (new_P4.y - new_P1.y) / (float)height;Dy.z = (new_P4.z - new_P1.z) / (float)height;//---------------------------------------- EndPoint = new_P1;for (i = 0 ; i < height ; i ++){for (j = 0 ; j < width ; j ++){RayTrace(W.viewpoint, EndPoint, resultT);EndPoint.x += Dx.x;EndPoint.y += Dx.y;EndPoint.z += Dx.z;// Move one pixel on the x axis}EndPoint.x = new_P1.x;EndPoint.x += Dy.x;EndPoint.y += Dy.y;EndPoint.z += Dy.z;}}I get perfect results if both my viewpoint and look@ point are on the z axis. If not, I get nothing.Please help.Thank you.   ;  I think most hit tests are done in model local space, though, so the following seems me senseful:1. Generate the ray in view local space.2. Transform the ray into the global space.3. Let the hit tests do what ever they need to do with the global ray.(The following code is not tested; it may be incorrect but should give you the idea.)To compute a ray in view local space you first sample the view plane. I assume you use an orthogonal raster and don't sample with progressive resolutions (i.e. a single pass sampling). Lets ignore super-sampling and stochastical sampling, too. Then 2 nested loopsconst float halfPelWidth = 0.5f * picw / (float)width;const float halfPelHeight = 0.5f * pich / (float)height;for( i = 0 ; i < height ; i++ ) {   const float vertPelOffset = 0.5 * pich - ( 2 * i + 1 ) * halfPelHeight;   for( j = 0 ; j < width ; j++ ) { const float horizPelOffset = ( 2 * j + 1 ) * halfPelWidth - 0.5f * picw; ...   }}iterate over the samples and compute the pixel offsets. Notice please that the above way deals with half pixel extents (as opposed to the OP's solution) so that the rays pass throught the center of the respective pixel instead of its upper left corner. The iteration along the vertical direction is mirrored, so it goes top down (what is not really necessary but just a concession to an immediate display of he rendering result).Having the sample offsets, you can construct the ray. The ray ever ends in the samples pixel (until the normalization step). It ever starts at [0,0,0] for perspective projection, or in an equivalent to the pixel but on the depth of the camera (i.e. 0): ... if( perspectiveProjection ) {    ray.origin.x = ray.origin.y = ray.origin.z = 0; } else if( orthogonalProjection ) {    ray.origin.x = horizPelOffset;    ray.origin.y = vertPelOffset;    ray.origin.z = 0 } else do_something_other_or_throw_an_error; ray.track.x = horizPelOffset; ray.track.y = vertPelOffset; ray.track.z = viewPlaneDistance; normalize( ray.track );Now apply the local-to-world transformation of the camera to the ray. That transformation relocates and rotates the ray so that it now look like sampling the viewplane in world space.   ;  I agree with haegarr: it easier to generate rays in camera space and then transform them in world space. Be sure that your look at matrix is correct for the handness of your coordinate system. I could post my camera code later when I'm back at home (If I remember to do that)...;  I think the transformation depends on the situation:I would transform less if possible. So I would transform the geometry instead of the rays (number_of_rays = vertical_resolution * horizontal_resolution, geometry: most likely to be less than a million vertices).But I guess it's not the bottleneck.   ;Quote:Now apply the local-to-world transformation of the camera to the ray. That transformation relocates and rotates the ray so that it now look like sampling the viewplane in world space.The point that I don't know how to do is exactly the local-to-world transformation. I have computed my transformation matrix, but I am either computing it wrong or have some sort of error somewhere else.   ;Quote:Original post by szecsI think the transformation depends on the situation:I would transform less if possible. So I would transform the geometry instead of the rays (number_of_rays = vertical_resolution * horizontal_resolution, geometry: most likely to be less than a million vertices).But I guess it's not the bottleneck.Don't forget reflected/refracted rays, shadow rays, GI, ...Not always the intersection test is done in world space (i.e. some mathematical primitives, or when using instancing, are tested in their own local space), but as far as I know, raytracers do scene traversal in world space (in addition, having sevaral millions polys scenes is not uncommon, though I never went over 500k with my one);Quote:Original post by cignox1Quote:Original post by szecsI think the transformation depends on the situation:I would transform less if possible. So I would transform the geometry instead of the rays (number_of_rays = vertical_resolution * horizontal_resolution, geometry: most likely to be less than a million vertices).But I guess it's not the bottleneck.Don't forget reflected/refracted rays, shadow rays, GI, ...Not always the intersection test is done in world space (i.e. some mathematical primitives, or when using instancing, are tested in their own local space), but as far as I know, raytracers do scene traversal in world space (in addition, having sevaral millions polys scenes is not uncommon, though I never went over 500k with my one)Well, in my simple ray-caster (had reflection/refraction/shadows) I store the transformed geometry too, so non-primary rays had nothing to do with the transformation. But it depends, transformation is so simple in both directions, so maybe it could depend on the situation, is the resolution is high with low vertex counts: transform geometry, otherwise transform rays. But obviously I'm not an expert in the topic, so it's more like a question/idea.   