3D Camera Control


#1

Greetings,
I am writing a ray tracer in Rust with nalgebra and I’ve been having trouble with implementing the camera for the past several days.

Camera: https://github.com/Limeth/euclider/blob/master/src/universe/d3/entity/camera.rs

My goal is to make a camera behave the same as in FPS games. Which objects should I use to represent such a camera? https://gamedev.stackexchange.com/questions/19507/how-should-i-implement-a-first-person-camera
Are yaw/pitch angles and location enough?

That was the part I know how to implement, but would just like suggestions about which objects to use. Eg. for the angles - Rotation3?
The difficult part is, that I also need to cast a ray for each point on screen, originating in the camera location. I am pretty sure I would need to use quaternions for this, but I have no idea what would be the correct procedure.

Illustration:

What I know how to calculate:

  • camera location,
  • camera direction vector,
  • screen width,
  • screen height,
  • screen point x,
  • screen point y,
  • the field of view (angle between the top left screen point, the camera location and the bottom right screen point),
  • distance of camera from the screen center

What I would like to calculate:

  • the directional vector to each point on screen from the camera location

Any help is much appreciated.


#2

I’ve found a way to solve this problem without having to use quaternions.
I now track another vector - up, which is perpendicular to the forward vector. That lets me get the left/right vectors perpendicular to the forward/up plane. By combining the vectors, I can find the location in 3d space of the screen point.

These are the changes I made:
github com/Limeth/euclider/commit/6e24a210a4a909c3d1980068b7dd271310e29dea
(I cannot add a link, because I am a new user)


#3

If you wish to reproduce the behavior of a FPS game camera, the simplest method is to combine a projection with a view transform. The projection will be responsible for the generation of the rays on a predefined reference frame, and the view matrix will transform them to the required location.

As you probably know, the convention in computer graphics for a perspective projection will transform a frustrum (defined with the screen dimensions, the field of view, and the near and far planes) to the unit cube. You can construct such projection as a matrix using the PerspectiveMatrix3 structure. Then the orientation and location of your camera in the world may be modeled by an Isometry3 initialized by Isometry3::look_at_rh(...).

Now, what you actually want to do is to perform a inverse projection in order to retrieve the rays locations and their orientation. Thus, you need to use the inverse matrix that combines an inverse camera transformation and an inverse perspective projection. To do that you have to retrieve the 4x4 matrix representation of the isometry (using na::to_homogeneous) and of the perspective (using .to_matrix())), multiply them, and inverse the result. You may want to see that for an example on my toy ray-tracer.

This inverse transformation maps into world space any point on a face of the unit cube with constant z value. In other words, sampling the rectangle with the four vertices given by:

(-1, +1, -1) (+1, +1, -1)
(-1, -1, -1) (+1, -1, -1)

and multiplying those samples by the inverse transformation previously computed, will give you a sampling of the visible near-plane of the frustrum in world coordinates (which corresponds exactly
to the blue rectangle on your diagram, modulo a change of sign along the view direction). The ray direction is then simply the normalized difference of this point with the camera location. For example, you may see that.

By the way, all that is quite similar to what you are already doing (including the up vector that is required for the isometry construction). Though this will need less coding on your side and you will then be able to append more easily other transformations to your camera, or switch to another kind of projection (e.g. orthographic) very easily.