Braiden Grant - 1 year ago 115
Java Question

# Casting Rays in 3D space with rotation components

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,
new Vector3(
Math.sin(Camera.rotation.y+(x*2/Main.renderSize.width)/2) * Math.cos(Camera.rotation.x+(y*2/Main.renderSize.height)/2),
Math.sin(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)
));
``````

This gives me a good viewing projection when the camera is not facing in any upwards or downwards direction.

Image of projection when camera is forwards:

.

Image of projection when camera is looking slightly upwards

.

It would be greatly appreciated if anyone can help me with the algorithm or point me towards a new one or a good source. Speed is a necessity as it is all real-time. Thanks.

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 `focus=(0,0,-f)` where `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 `x` axis `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 `(0,0,0)`

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.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download