I am currently working on a CPU based simple ray tracer to render few triangles as a project. I'm okay with every aspect of it except generating the actual rays. I do not wish to project world coordinates into screen space, but actually create the rays according to where the camera is located in 3D coordinates.
Right now, I have a fairly alright algorithm which allows me to generate a ray for each pixel on the screen for any rotation around the Y-Axis, and it attempts to incorporate the X-Axis as well, giving some up and down looking capability, however when the user looks up or down, the image becomes distorted.
This is what I have worked out so far:
Ray ray = new Ray(Camera.position,
Math.sin(Camera.rotation.y+(x*2/Main.renderSize.width)/2) * Math.cos(Camera.rotation.x+(y*2/Main.renderSize.height)/2),
Math.cos(Camera.rotation.y+(x*2/Main.renderSize.width)/2) * Math.cos(Camera.rotation.x-(y*2/Main.renderSize.height)/2)
there may be more going on then just wrong ray direction. So to be more clear here is what I meant by my comment in more detail:
So let assume all the stuff is in camera space coordinate system. so the screen is on plane
z=0 and the middle pixel is
(0,0,0). The focal point is at
f is the focal length of your projection. Now You need to cast ray (or more) for each pixel so its position and direction are:
pos = (x,y,0); dir = pos-focus = (x,y,f); // most likely you should also normalize it so dir = dir / |dir|;
When your x,y coordinates are not in world units but pixels instead you should rescale them accordingly. So let have resolution of the screen
xs,ys and we want to have field of view in
FOVx = 60.0deg then you need to change this all a bit:
// pixel size sz = f*tan(0.5*FOVx)/(0.5*xs); // x,y screen position [pixels] -> xx,yy [world units] xx=(x-(0.5*xs))*sz; yy=(y-(0.5*ys))*sz; // ray pos = (xx,yy,0); dir = pos-focus = (xx,yy,f); dir = dir / |dir|;
Now comes in the transformation matrix. Let
M be the transformation matrix representing your screen. The origin is set to middle of screen and the
x,y axis vectors correspond to screen axises. You also need
M0 matrix which is the copy of
M but with origin set to
As you want to make all the stuff in world global coordinate system GCS then to transform from screen space to world do:
world_position = M *screen_position; world_direction = M0*screen_direction;
But this could be slightly different if different matrix elements and operand order convention is used then in the link above.
So now the ray in world GCS will be:
// pixel size sz = f*tan(0.5*FOVx)/(0.5*xs); // x,y screen position [pixels] -> xx,yy [world units] xx=(x-(0.5*xs))*sz; yy=(y-(0.5*ys))*sz; // ray pos = (xx,yy,0); dir = pos-focus = (xx,yy,f); // convert to world GCS pos = M *pos; dir = M0*dir; // normalize dir = dir / |dir|;
Usually you can start with
M as unit matrix and from that just multiply by rotations and translations on key strokes to handle view movement and rotations. Before each render you can compute
M0=M and then just set the 3 elements holding origin to zero.