CS 184: Computer Graphics and Imaging, Spring 2020

Project 3: PathTracer Part 2

Tianqi Yang & Sainan Chen

CS184-aak & CS184-ack



Overview

After implementing basic ray tracing, light sampling, direct & global illumination in last project, now we would like to further extended the pathtracer by adding reflection and refraction to simulate more realistic material textures.

In part 1, we will simulate basic mirror and glass textures which rely on reflection and refraction. While reflection changes the ray direction in a known way, refracted ray direction depends on the index of refraction (IOR) of incident and existing mediums, and it's not always happening (total internal reflection). In part 2, we will further simulate reflective microfacet materials by calculating microfacet bidirectional reflectance distribution function (BRDF). BRDF depends on parameters \(\eta\), \(k\), and \(\alpha\). The first two are material-related parameters, and smaller \(\alpha\) leads to smoother surface.

Part 1: Mirror and Glass Materials

Implementation details

In this part, we try to simulate the texture of glass and mirror by implementing reflection and refraction.

Reflection & Mirror Texture

Reflection is just negation of x and y with unchange z if we have the coordinate system that origin is the light intersection point and the z axis lies along the normal vector, so we can get \(w_i\) from \(w_o\).

Then MirrorBSDF just has pdf = 1 and value = reflectance / \(|cos(w_i)|\) to cancel out the cosine used in the at_least_one_bounce_radiance function.

Refraction

Refraction depends on index of refraction (IOR) of incident and existing mediums. As the image below shows, if \(w_o\) has z larger than 0, the left situation holds, and \(\eta = \frac{1}{ior}\). Otherwises, the right situation holds, and \(\eta = ior\).

Then by the following equation, we can find out \(w_i\). Notice that if \(1-\eta^2(1-w_o.z^2) \leq 0 \), there is no refraction, and we have total internal reflection. Also notice that z of \(w_i\) always have opposite sign with z of \(w_o\).

For RefractionBSDF, similarly, pdf = 1, and we need to cancel out cosine in the at_least_one_bounce_radiance function, so the sample value is transmittance / \(|cos(w_i)|\) / \(\eta^2\). \(\eta\) is also involved here becaue radiance concentrates when a ray enters a material with high IOR and disperses when it exits such a material. If there is total internal reflection, an empty is returned.

Glass Texture

Finally, for GlassBSDF, we use Schlick's approximation to decide either reflecting or refracting by coin-flip. If there is total internal reflection, the pdf and value will just be like MirrorBSDF (pdf = 1 and value = reflectance / \(|cos(w_i)|\)). Otherwises, we perform coin flip with head probability = R (Schlick's reflection coefficient), where R is defined as below. If we get a head, perform reflection with pdf = R and value = R * reflectance / \(|cos(w_i)|\). If we get a tail, perform refraction with pdf = 1-R and value = (1-R) * transmittance / \(|cos(w_i)|\) / \(\eta^2\).

In addition, all f functions will just return an empty, because of the assumption that \(\w_i) not created by sample_f() can never be reflection/refraction input of \(\w_o).


Screeenshots

Below are spheres rendered with both glass and mirror texture ordered by increasing max ray depth. We can see there are more and more features and details with the increase of ray depth.

Rendering with Different Max Ray Depth (1024 samples per pixel, 4 samples per light)

Max ray depth (m) = 0

Max ray depth (m) = 1

Max ray depth (m) = 2

Max ray depth (m) = 3

Max ray depth (m) = 4

Max ray depth (m) = 5

Max ray depth (m) = 100


Here are two more examples of lucy rendered with glass texture and dargon dendered with mirror texture.

Rendering with Different textures (1024 samples per pixel, 4 samples per light, max ray depth = 7)

lucy with glass texture

dragon with mirror texture


Part 2: Microfacet Material

Implementation details

In this part, we are going to implement microfacet model, which simulate isoreopic rough conductors that only reflect but don't refract.

Microfacet BRDF is calculated by the function below. F is the Fresnel term, G is the shadowing-masking term, D is the normal distribution function, n is the microsurface normal, which is always (0,0,1) in local coordinate, and h is half vector, which is a normalized bisector of incident ray \(w_i\) and exiting ray \(w_o\), calculated by \(\frac{w_i+w_o}{||w_i+w_o||}\).

For normal distribution function (NDF) D, we use Beckmann distribution defined as below. \(\alpha\) here represent the roughness of the surface, and it's a parameter set in dae files which is between 0.005 and 0.5. The smaller the \(\alpha\) is, the smoother the surface will be. \(\theta\) here represent the angle between the ray and the surface normal n.

The approximation for air-conductor Fresnel term is as below. \(\eta\) and \(k\) are two material-related parameters set in dae files, and they both are 3D, containing values at wavelengths 614nm (red), 549nm (green), and 466nm (blue). Values for specific materials can be found on https://refractiveindex.info. \(\theta_i\) here is the angle between the normal and \(w_i\).

To implement BRDF sampling function, first we need to sample normal h by sampling \(\theta_h\) and \(\phi_h\) according to pdfs \(p_{\theta}\) and \(p_{\phi}\). Because the normal h is also the bisector of \(w_i\) and \(w_o\), then we can get sampled \(w_i\). If we have two random samples \(r_1\) and \(r_2\) from standard uniform distribution unif[0,1), then we can get random samples for \(\theta_h\) and \(\phi_h\) using functions below, and h will just be (\(sin\theta_h*cos\phi_h, sin\theta_h*sin\phi_h, cos\theta_h\)), and \(w_i\) will be \(-w_o + 2 * (w_o.h)) * h\).

Finally, the sampling pdfs of h and wi will be calculated by functions below.

Screenshots

Below are some outputs rendered with different \(\alpha\).

Rendering with Different alpha value (1024 samples per pixel, 4 samples per light)

alpha = 0.005

alpha = 0.05

alpha = 0.25

alpha = 0.5


Below are outputs rendered with hemisphere sampling (left) and importance sampling (right). Obviously, the left output is more noisy. It's reasonable, because hemisphere sampling does sampling uniformly among all directions, while importance sampling samples more frequently at light sources which results in higher rendering quality.

Rendering with Different sampling methods (64 samples per pixel, 1 samples per light)

hemisphere sampling

importance sampling


Below is an image of the bunny rendered with silver (Ag) texture, and the table contains eta and k values for silver material.

eta k
614 nm (red) 0.059193 4.1283
549 nm (green) 0.059881 3.5892
466 nm (blue) 0.047366 2.8132