Ray Tracing From Scratch: Multisampling & Distribution Ray Tracing

Muhammed Can Erbudak
9 min readNov 30, 2022

--

The previous versions of the ray tracer have good-looking and realistic results. However, they do not have some details such as soft shadows. In addition, they use single point for a pixel, which might not be enough for sampling correctly. In this blog post, I will briefly mention about multisampling and some effects increasing realism, namely distribution ray tracing.

Multisampling

Sampling is the technique used for representing a continuous signal as a discrete one. It is commonly used in image processing, music industry, computer graphics, and telecommunication. The quality of the discrete signal depends on sampling positions and sampling frequency. If the discrete representation is not produced correctly, it will produce noticeable errors, such as aliasing, in the process of reconstructing the continuous signal from the discrete representation .

Sampling and reconstruction. Retrieved from [1].
Aliasing example. Retrieved from [2].

We used a single point, pixel center, for each pixel in ray tracing. Obviously, it is not enough for sampling correctly a continuous signal. Thus, some of the sampling methods and filtering methods will be introduced.

Uniform Sampling

Uniform sampling is the sampling process producing uniformly distributed sampling points in a pixel by dividing the pixel into equal parts.

Uniform sampling. Retrieved from [1].

The effect of a uniform sampling is equivalent to increasing pixel count. Thus, it might not be good choice for sampling in ray tracing as given below since sampling positions are important as sampling frequency.

Aliasing in uniform sampling. Retrieved from [1].

Random Sampling

Sample points within the pixel are chosen randomly in random sampling.

Random sampling. Retrieved from [1].

The random sampling might produce good results. However, it has a chance producing bad sample points since the process is fully random. For example, it might sample points clumped together in a subregion of a pixel.

Clumping error in random sampling. Retrieved from [1].

Jittered Sampling

A better approach for the sampling process would be combining aforementioned approaches so that samples are randomly selected while they have uniformity between other sample points. This can be implemented using jittered sampling: dividing the pixel into uniform areas and randomly picking a point within each area.

Jittered sampling. Retrieved from [1].

This approach might produce erroneous results for some cases, but still it is one of the best sampling methods in simple ones. More advanced sampling techniques could be implemented for better results.

Clumping error in jittered sampling. Retrieved from [1].

Filtering

Filtering is the process of constructing the final results, which is the pixel color, by combining sampling outputs.

One of the simplest approaches is box filter. In box filtering, the color results for each ray sent from sample points within a pixel are summed up and divided into the sample count similar to averaging.

Box filtering. Retrieved from [1].

If box filtering is not enough, Gauss filtering can be applied. The weighted average of sample points are calculated using Gaussian curve so that the sample points near the pixel center affects the result more than the samples near edges.

Gauss curve. Retrieved from [1].
Gauss weight calculation. Retrieved from [1].
Gauss filtering. Retrieved from [1].

The sampling results for an area light are given below. Increasing the sigma resulted in more sharp and noisy outputs as expected. However, the results are more noisy than expected especially when the sigma is chosen as the suggested value. Thus, there might be an implementation error resulting in more noise.

Box filter
Gauss filter with sigma = 1/6
Gauss filter with sigma = 1/3
Gauss filter with sigma = 1

Implementation

In the implementation, the following code block is used for thread safe random sampling as explained in https://stackoverflow.com/questions/21237905/how-do-i-generate-thread-safe-uniform-random-numbers.

Thread safe random number generation

The static keyword provides that the random generator is created once. Otherwise, the same number will generated since the seed is the same for each call. In addition, thread local keyword provides that the static keyword is applied for each thread separately so that each thread will have different random generator object. However, each thread will produce numbers which are the same ones in other threads via this approach. One solution for that might be using a single random generator object which is called in a thread safe manner. This approach would be very slow. A better approach would be assigning different seed for each random generator object so that threads will have different random numbers.

Distribution Ray Tracing

Distribution ray tracing is a technique for creating more realistic scenes with some visual effects. It uses sampled primary rays and sampled object points to combine them into a single pixel color.

Depth of Field

Depth of field is the effect of focusing only some part of the image, which can be mostly seen in camera photographs.

The ray tracing model we implemented has sharp focus since it uses rays originating from a 1D hole unrealistically. The solution is implementing a pinhole camera model. However, pinhole camera model will distribute ray which will result in non-focused outputs. The solution for the focus problem is using a lens.

1D hole. Retrieved from [1].
Pinhole camera model. Retrieved from [1].
Pinhole camera model with a lens. Retrieved from [1].

In order to simulate rays going through lens, a sampling point within the lens needed for each primary ray sample. Since uniformly sampling points using disk lenses is difficult, the lens is assumed to be a rectangular lens so that the same sampling method used for pixel sampling can be applied.

The following methods can be used for calculating the effective ray resulting from the lens. Note that the lens is assumed to behind the pixels so that the resulting image is not reverted.

Ray direction calculating for DoF. Retrieved from [1].
Ray direction calculating for DoF. Retrieved from [1].

After that, the resulting ray can be traced so that the only focused objects are the ones in the depth of field region. The resulting output is:

DoF output

Area Lights

Another unrealistic part of the current implementation of the ray tracing is lighting. While the current lighting model represents lights as points, lights resulting from an area is more common in the physical world. In addition, point lights result in shadows with sharp details while real shadows are soft. To sum up, area lights provide more realistic lighting results such as soft shadows.

Area lights are defined with their intensity, extent (square root of the area), normal and position. Since the boundaries of the area are not defined, it is necessary to create an orthonormal basis defining the area boundaries. There are infinitely many orthonormal basis around the normal, but one of the most commonly used is as follows:

  • Generate a vector different than normal. This can be done by assigning one to the vector element with the lowest value in absolute sense.
  • Take the cross product of the generated vector and the normal vector to create u vector.
  • Take the cross product of the normal vector and u vector to produce v vector.
  • The resulting u and v vectors define a orthonormal basis.
Orthonormal basis of normal n. Retrieved from [1].

After that, the orthonormal basis can be used for sampling a light point within the area for each primary ray sample. Note that random generated numbers in the range of [-0.5, 0.5].

Sampling area light points. Retrieved from [1].

By using sample points, light calculations can be done as follows:

Area light irradiance calculations. Retrieved from [1].
Area light rays used for calculations. Retrieved from [1].

Note that area lights illuminates all directions. Thus, calculation of the cosine requires a little attention. If the calculation is done calling max with 0 and the dot product results, the output is erroneous since directions with negative cosine values (when the angle is more than 90 degrees.) are discarded as follows:

Area light discarded backward light bug

In order to fix that bug, one can just calculate the dot product. However, this results in black since negative cosine values cause colors with negative values. After that, those values are clamped to 0. Thus, the resulting erroneous output with black backward lighting is as follows:

Area light black backward light bug

The solution that I applied is using absolute value to calculate cosine so that only the absolute value of the dot product affects the final color calculations. The resulting output is:

Area light output with the correct backward lighting

Motion Blur

Motion blur is the effect seen in photographs when somebody moves during the exposure time of the camera. Thus, some blur might be observed around the moving objects.

Motion blur is an important effect for ray tracing since it provides feel of movement for the output scene. The current ray tracing produces static outputs and it is not able to provide real time ray tracing due to limitations of CPUs (if you are interested, you can read my blog post explaining the real time ray tracing implementation in GPUs using Vulkan). Thus, it is one of the common ways providing dynamism to static rendering outputs.

Motion blur is defined with a translation matrix. Each primary sample ray samples a time parameter between 0 and 1 inclusive for motion blur. The time parameter defines the motion of the object in the current time. If the the time sample parameter is 0, it is in the position before translation, and if the the time sample parameter is 1, it is in the position after translation applied. Otherwise, the object in between. The effective translation matrix can be calculate by simply multiplying translation parameters with the time sample parameter.

Motion blur definition. Retrieved from [1].

The object position after the translation applied is tested for intersection. One important point for the intersection test is that bounding boxes for objects with motion blur must include every possible translated position of the objects. This can be implemented by generating bounding boxes for the initial (t=0) position and the final position (t=1). After that, these bounding boxes might be merged to define the bounding box of the object including all possible translations.

Initial and final bounding boxes
The bounding box after merge

By using that approach, the following output is rendered:

Motion blur output

Glossy Reflections

Another interesting effect using samples is that combining reflections with randomness. It is possible to perturb reflection directions with given roughness parameter. In order to do that, two random numbers can be sampled for each material with roughness material. This sampling is done for each primary ray sample.

Similar to area lights, an orthonormal basis is required for the surface normal in order to apply glossy reflections. The same process used in area lights to create orthonormal basis can be applied for the surface normal. After that, the orthonormal basis vectors can be used for perturbing the original reflection ray proportional to roughness parameter. The calculation is as follows:

Glossy reflection. Retrieved from [1].
Glossy reflection calculation. Retrieved from [1].

After the process is applied, the resulting output is:

Glossy reflection output

Sources

  1. Akyüz, A. O. (2022). CENG 795 Special Topics: Advanced Ray Tracing. Middle East Technical University.
  2. Séquin, C. H. (2009). CS 184: Computer Graphics. University of California, Berkeley.

--

--