# Ray Tracing 101: How does a look at transform work?

The matrix that transforms world space coordinates to view space is called the view matrix. A look at matrix is a view matrix that is constructed from an eye point, a look at direction or target point and an up vector.

If you take a look at the source code of pbrt-v3 and figure out how a camera spans a ray you will recognize that the ray is first described in camera space, which means that the start of a ray is always at the point (0,0,0). This point is then transformed from camera space to world space:

```
// BSD-2-Clause License - see https://github.com/mmp/pbrt-v3
Float PerspectiveCamera::GenerateRay(const CameraSample &sample,
Ray *ray) const {
ProfilePhase prof(Prof::GenerateCameraRay);
// Compute raster and camera sample positions
Point3f pFilm = Point3f(sample.pFilm.x, sample.pFilm.y, 0);
Point3f pCamera = RasterToCamera(pFilm);
*ray = Ray(Point3f(0, 0, 0), Normalize(Vector3f(pCamera)));
// Modify ray for depth of field
if (lensRadius > 0) {
// Sample point on lens
Point2f pLens = lensRadius * ConcentricSampleDisk(sample.pLens);
// Compute point on plane of focus
Float ft = focalDistance / ray->d.z;
Point3f pFocus = (*ray)(ft);
// Update ray for effect of lens
ray->o = Point3f(pLens.x, pLens.y, 0);
ray->d = Normalize(pFocus - ray->o);
}
ray->time = Lerp(sample.time, shutterOpen, shutterClose);
ray->medium = medium;
*ray = CameraToWorld(*ray);
return 1;
}
```

To keep things simple let consider only a 2D environment. Since I favor a test-driven development process I come up with a test first. In Cucumber style using Gherkin we can formulate the following scenario:

```
Scenario Outline: Compute a look-at transform in a 2D environment
Given an <eye> and <target> point
When I compute a look-at matrix given those points
Then my computed look-at matrix (listed in row-major fashion) should be <look-at-matrix>
Examples:
| eye | target | look-at-matrix |
| (0,0) | (1,0) | (1,0,0, 0, 0,1,0,0,0,0,1,0,0,0,0,1) |
| (3,1) | (3,2) | (0,1,0,-1,-1,0,0,3,0,0,1,0,0,0,0,1) |
```

I translated this feature description manually to a Google Test. Of course, the Cucumber framework could be used to simplify this translation process, but this is another story.

```
TEST(Transform44f, lookat1) {
Point2f eye{0.0f, 0.0f};
Point2f target{1.0f, 0.0f};
Transform44f transform = look_at(eye, target);
Matrix44f expectedMatrix;
expectedMatrix << 1.0f,0.0f,0.0f,0.0f,
0.0f,1.0f,0.0f,0.0f,
0.0f,0.0f,1.0f,0.0f,
0.0f,0.0f,0.0f,1.0f;
ASSERT_TRUE(transform.getMatrix().isApprox(expectedMatrix));
}
TEST(Transform44f, lookat2) {
Point2f eye{3.0f, 1.0f};
Point2f target{3.0f, 2.0f};
Transform44f transform = look_at(eye, target);
Matrix44f expectedMatrix;
expectedMatrix << 0.0f,1.0f,0.0f,-1.0f,
-1.0f,0.0f,0.0f, 3.0f,
0.0f,0.0f,1.0f, 0.0f,
0.0f,0.0f,0.0f, 1.0f;
ASSERT_TRUE(transform.getMatrix().isApprox(expectedMatrix));
}
```

A look at transform is just a simple basis transform. Let’s consider a basis transform:

Let’s assume that basis 1 is a standard basis and basis 2 is translated by $(3,2)$ and a rotated by 45 degrees (counter-clockwise). What are the coordinates of point $A$ regarding basis $B_2$?

The answer is as simple as:

All you have do is to move basis 2 back to basis 1. And then you have to the same for the point $A$.

$(T*R)$ describes the transform to convert basis 1 to basis 2. This is composed of a translation $T$ and a rotation $R$.

To go the other way around you have just to invert this transformation:

Let’s translate this into a unit test for your `look_at`

function:

```
Examples:
| eye | target | look-at-matrix |
| (3,2) | (4,3) | (0.707107,0.707107,0,−3.53553,−0.707107,0.707107,0,0.707107,0,0,1,0,0,0,0,1) |
```

And here as a gtest:

```
TEST(Transform44f, lookat3) {
Point2f eye{3.0f, 2.0f};
Point2f target{4.0f, 3.0f};
Transform44f transform = look_at(eye, target);
Matrix44f expectedMatrix;
expectedMatrix << 0.707107f, 0.707107f, 0.0f, -3.53553f,
-0.707107f, 0.707107f, 0.0f, 0.707107f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f;
ASSERT_TRUE(transform.getMatrix().isApprox(expectedMatrix));
}
```