6. Camera Translation and World Coordinates
This renderer modifies the Camera class to give us more control over
the renderer's virtual camera.
In the previous renderers, the camera is fixed at the origin, looking down the negative z-axis. This is not very convenient. In order to render a complex scene, we have to position each object relative to the camera. It would be better to have the camera act like a real movie camera, where the objects are positioned in a scene and then the camera is positioned in whatever location seems best. we should be able to move the camera around the scene, independently of moving the objects in the scene.
In this renderer we give the Camera class the ability to set the camera's
location (but not its orientation). This lets us translate the camera in
the x, y and z directions, but we cannot yet rotate the camera.
In a later renderer we will allow the camera to both translate and rotate in space and to point in any direction.
6.1 Renderer source code
The Java source code for this renderer is publicly available as a zip file.
Here is a link to the renderer's source code.
Download and unzip the source code to any convenient location in your computer's file system. The renderer does not have any dependencies other than the Java 11 (or later) JDK. Once you have downloaded and unzipped the distribution, you are ready to compile the renderer and run the renderer's (interactive) example programs.
In the previous renderer the first pipeline stage was Model2View. This
renderer splits that stage into two stages, Model2World and World2View.
This renderer has one new file in the pipeline package,
World2View.java
and the previous Model2View.java pipeline stage is renamed
Model2World.java.
The files Pipeline.java and Pipeline2.java are modified to put the
new World2View stage into the rendering pipeline after the Model2World
stage.
In the scene package, the file
Camera.java
is updated to hold the camera's translation parameters.
The client programs are modified to make use of the camera's translation feature.
6.2 Pipeline
The pipeline has the following eight stages.
v_0 ... v_n A Model's list of Vertex objects
\ /
\ /
|
| model coordinates (of v_0 ... v_n)
|
+-------+
| |
| P1 | Model-to-world transformation (of the vertices)
| |
+-------+
|
| world coordinates (of v_0 ... v_n)
|
+-------+
| |
| P2 | World-to-view transformation (of the vertices)
| |
+-------+
|
| view coordinates (of v_0 ... v_n) relative to an arbitrary view volume
|
+-------+
| |
| P3 | View-to-camera (normalization) transformation (of the vertices)
| |
+-------+
|
| camera coordinates (of v_0 ... v_n) relative to the standard view volume
|
/ \
/ \
/ \
| P4 | Near Clipping (of each line segment)
\ /
\ /
\ /
|
| camera coordinates (of the near-clipped v_0 ... v_n)
|
+-------+
| |
| P5 | Projection transformation (of the vertices)
| |
+-------+
|
| image plane coordinates (of v_0 ... v_n)
|
/ \
/ \
/ \
| P6 | Clipping (of each line segment)
\ /
\ /
\ /
|
| image plane coordinates (of the clipped vertices)
|
+-------+
| |
| P7 | Viewport transformation (of the clipped vertices)
| |
+-------+
|
| pixel-plane coordinates (of the clipped vertices)
|
/ \
/ \
/ \
| P8 | Rasterization & anti-aliasing (of each clipped line segment)
\ /
\ /
\ /
|
| shaded pixels (for each clipped, anti-aliased line segment)
|
\|/
FrameBuffer.ViewPort
6.3 World vs. View Coordinate Systems
Creating a moving camera involves a mathematical trick. We create a new coordinate system, the world coordinate system, and place all our objects, including the camera, into that coordinate system. Then we use a world-to-view coordinate transformation to convert every vertex's coordinates from the world coordinate system to the camera's view coordinate system.
In the previous renderers, the camera was fixed at the origin looking down the negative z-axis. With the camera at the origin, every vertex had its coordinates measured in a natural way relative to the camera's location. We will call coordinates measured relative to the camera "view coordinates" (they are also called "eye coordinates"). In the previous renderer, we used a model's translation vector to place the model in the camera's view coordinate system (hence, the model-to-view coordinate transformation stage in the rendering pipeline).
What we want to do in this renderer is remove the camera from the origin
and call the coordinate system relative to the origin the "world coordinate
system". Then place all our geometric objects in the world coordinate space
(so every object is located by its coordinates relative to the origin). Then
we want to place a camera (with its view volume) somewhere in the world
coordinate system. Once the camera is in place, notice that every geometric
object can be located relative to either the origin or the camera. What we
mean is that every vertex will have two sets of coordinates. Every vertex
v will have "world" coordinates (x_w, y_w, z_w) relative to the origin AND
it simultaneously has "view" coordinates (x_v, y_v, z_v) relative to the
location of the camera (and its view volume). What this renderer does is add
a method to the Camera class that implements a transformation T that converts
vertex coordinates from world-coordinates to view-coordinates
T(x_w, y_w, z_w) = (x_v, y_v, z_v)
In addition, there is a new pipeline stage, World2View.java, that applies
this camera transformation to all the Vertex objects in a Scene.
Once the coordinates of a Vertex are transformed from world coordinates to
the camera's view coordinates, the rest of the pipeline stages work on the
Vertex just as they did in the previous renderer.
The world-to-view transformation is implemented as a translation Vector
in the Camera class, similar to the translation Vector in the Position
class that is used for the model-to-world transformation.
6.4 World2View Pipeline Stage
We need to explain how to compute the translation vector used in the
world-to-view transformation. It is not quite the same as the translation
Vector in the Camera class.
The translation Vector in the Camera class is the vector that places
the camera at the location in world coordinates where we want the camera
to be. The camera's translation Vector acts just like the translation
Vector in a Position that places a Model where we want it to be in
world coordinates.
On the other hand, the translation vector in the world-to-view transformation
is the opposite of the vector in the Camera object. The reason for this is
not too hard to understand. If you are taking a picture with a physical
camera, and you move the camera to the right, it would look the same to the
camera if you had instead moved everything in front of the camera to the left.
If you move the camera up, it would look the same to the camera if you had
moved everything in front of the camera down. Moving a camera by a vector
v will look the same to the camera as moving everything in front of the
camera by the vector -v.
If we start with a camera located at the origin and looking down the negative z-axis and we move the camera up 5 units in the positive y-direction, then every object the camera is looking at effectively translates 5 units down in the negative y-direction relative to the camera. So the world-to-view translation vector (which translate all objects -5 units in the y-direction) is the opposite of the camera's position vector (which translate the camera 5 units in the y-direction).
If we start with a camera located at the origin and looking down the negative z-axis and we move the camera back 5 units in the positive z-direction, then every object the camera is looking at effectively translates 5 units in the negative z-direction relative to the camera. So the world-to-view translation vector (which translate all objects -5 units in the z-direction) is the opposite of the camera's position vector (which translate the camera 5 units in the z-direction).
In the World2View.java file, the world-to-view transformation is
implemented with code that looks like this.
public static Model world2view(Model model, Camera camera)
{
Vector negViewVector = camera.getViewVector().times(-1);
List<Vertex> newVertexList = new ArrayList<Vertex>(model.vertexList.size());
for (Vertex v : model.vertexList)
{
newVertexList.add( negViewVector.plus(v) );
}
return new Model(newVertexList,
model.primitiveList,
model.colorList,
model.name,
model.visible);
}
This code adds the negative of the camera's translation vector to every
Vertex in every Model in the Scene.