Braiden Grant - 5 months ago 38

Java Question

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.

Answer

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.