Path tracer: thin lens, texture mapping, Fresnel equations, and smooth shading

1800 samples. A render illustrating Fresnel reflection and texture mapping.

A few new features have been added to our path tracer. The depth of field extension has been reworked slightly using the thin lens equation allowing us to specify a focal length and aperture. Fresnel equations have been added to more accurately model the behavior of light at the interface between media of different refractive indices. Textures can be applied to the plane primitive, and normals can be interpolated across the triangle primitive allowing for smooth shading. Below are three renders depicting these features.

1,800 samples.  A render illustrating Fresnel reflection and texture mapping.
1,800 samples. A render illustrating Fresnel reflection and texture mapping.
1,610 samples.  A render illustrating smooth shading.
1,610 samples. A render illustrating smooth shading.
An example render illustrating depth of field.
An example render illustrating depth of field.

Thin lens
We first reworked the camera model using the thin lens equation. Below, \(f\) is the focal length, \(d\) is the distance to the focal plane, and \(i\) is the distance to the image plane.

\begin{align}
\frac{1}{f} &= \frac{1}{d} + \frac{1}{i} \\
i &= \frac{1}{\frac{1}{f} - \frac{1}{d}} = \frac{fd}{d-f} \\
\end{align}

For a 50mm lens focused at 10m the image plane is located at approximately 5.025mm. For a lens set to f/8 this yields a radius, \(r\), of the entrance pupil of,

\begin{align}
r &= \frac{1}{2} \cdot \frac{f}{8} \\
&= \frac{1}{2} \cdot \frac{50mm}{8} &= 3.125mm \\
\end{align}

In the code we specify the focal length, aperture, and distance to the focal plane. From this we evaluate the distance to the image plane and the aperture size. The kernel is set to simulate a a 36mm wide sensor by a height evaluated appropriately for the given ratio. We fire rays from a location on the sensor through the origin to a point, \(\vec{p}\), on the focal plane. We then jitter the origin within the disc defined by the aperture radius. If the new offset is \(\vec{o}\), the ray direction is \(\vec{r}=\vec{p}-\vec{o}\), and we sample the ray \(\vec{o} + t\vec{r}\).

Fresnel reflection
Next, we added support for Fresnel reflection. This was a straightforward modification to our refractive material. We simply find the reflection coefficient, \(R\), for unpolarized light given below,

\begin{align}
R &= \frac{R_s+R_p}{2} \\
R_s &= \left( \frac{-n_1 \hat{r} \cdot \hat {n} - n_2 \sqrt{1 - \frac{n_1^2}{n_2^2} \left[1 - (\hat{n} \cdot \hat{r})^2 \right]}}{-n_1 \hat{r} \cdot \hat {n} + n_2 \sqrt{1 - \frac{n_1^2}{n_2^2} \left[1 - (\hat{n} \cdot \hat{r})^2 \right]}} \right)^2\\
R_p &= \left( \frac{n_1 \sqrt{1 - \frac{n_1^2}{n_2^2} \left[1 - (\hat{n} \cdot \hat{r})^2 \right]} - -n_2 \hat{r} \cdot \hat {n}}{n_1 \sqrt{1 - \frac{n_1^2}{n_2^2} \left[1 - (\hat{n} \cdot \hat{r})^2 \right]} + -n_2 \hat{r} \cdot \hat {n}} \right)^2\\
\end{align}

Note that all vectors above are unit vectors. We next generate a uniform random variable on the interval \([0,1]\) and reflect the ray if this number is less than \(R\). We refract and transmit otherwise.

Smooth shading
In this post we discussed triangle intersections, so we have \(s\) and \(t\) for our point of intersection, \(\vec{p}\),

\begin{align}
\vec{p} &= \vec{p}_0 + s(\vec{p}_1 - \vec{p}_0) + t(\vec{p}_2 - \vec{p}_0) \\
\end{align}

Provided we have a normal for each vertex, we can exploit the \(s\) and \(t\) evaluations and use them for interpolating the normals,

\begin{align}
\vec{n} &= \vec{n}_0 + s(\vec{n}_1 - \vec{n}_0) + t(\vec{n}_2 - \vec{n}_0) \\
\hat{n} &= \frac{\vec{n}}{\left|\left|\vec{n}\right|\right|}
\end{align}

Texture mapping the plane primitive
The last addition this time around was to add texture mapping support for the plane primitive. The general idea was to define two linearly-independent vectors that span the plane. With those two vectors and a point on the plane, we can find the \(s\) and \(t\) coordinates for our point of intersection as we do for the triangle primitive. Since the texture is repeating we find the coordinates, \(s'\) and \(t'\),

\begin{align}
s' &= s - \lfloor s \rfloor \\
t' &= t - \lfloor t \rfloor \\
\end{align}

We now have appropriate texture coordinates, \(s'\) and \(t'\), that both belong to the interval \([0,1]\). These are used as offsets into our texture.

Download this project: pathtracer_dof_triangles_fresnel_texture_smooth.tar.bz2

Leave a Reply

Your email address will not be published.