Part 7: Object Lights & Path Tracing

In the last (and the most challenging) part of this ray tracer, two main features are added:

1. Object Lights

Until now, the ray tracer was only supporting lights that are not visible in the scene such as PointLight, AreaLight etc. No ray intersection tests were being performed for the light objects. In this version, two types of object lights (LightMesh and LightSphere) are added. These lights provide illumination for the scene and they are also objects that are included in the BVH and being tested against intersections with the sent rays.

For the implementation, there are a few points to note:

- For object lights, I used multiple inheritance. LightMeshes inherit from both Light and Mesh classes, while LightSpheres inherit from both Light and Sphere classes. These classes also have a property keeping the radiance of the light object defined in the xml.

- Two new attributes are added to the Hit class: isLight to indicate whether the object hit is a light source, radiance to keep the radiance of the object light hit. These are set in the ray-intersection methods of the LightMesh and LightSphere classes.

- If the ray hits an object light, the radiance stored in the Hit object is directly returned without computing the diffuse and specular shading contributions and without any shadow check.

- The point from which the illumination is obtained on the LightMesh is computed randomly by first selecting a triangle based on its area, then uniformly sampling a point inside this triangle in order to ensure that the point is selected not just randomly but also uniformly.

- The point from which the illumination is obtained on the LightSphere is computed by uniformly sampling an illumination direction on the sphere and then sending a ray to the LightSphere object from the hit point along the sampled direction.

2. Path Tracing

In this version of the ray tracer, support for Path Tracing algorithm is also added. Until now, only the illumination coming from direct lighting was being computed. Now, the illumination coming from indirect lighting also contributes to the final color of a pixel. In the xml input files, two new elements (Renderer and RendererParams) are added to specify whether the rendering will be performed with Ray Tracing or Path Tracing. RendererParams can include NextEventEstimation, RussianRoulette and ImportanceSampling, indicating that whether these algorithms will be used or not if the Renderer type is Path Tracing.

NextEventEstimation: Besides the indirect lighting, also get the contribution from direct lighting.

RussianRoulette: Terminate the rays based on RussianRoulette algorithm, instead of terminating them when the recursion depth reaches to a fixed number in order to prevent introducing bias.

ImportanceSampling: While sampling directions over the hemisphere, instead of sampling directions uniformly, sample them more as you get closer to the surface normal.

For the implementation, there are a few points to note:

 - For russian roulette, q is chosen as (1 - cosTheta) where theta is the angle between the surface normal and sampled wi.

- When next event estimation is enabled, since the light sources are explicitly sampled for direct lighting contribution, for indirect lighting contribution, if the global illumination vector hits a light source, it is ignored.

Problems & Fixes

The most time consuming problem I encountered implementing object lights can be seen in the below images. The scenes were being rendered in a way that either the light object is correct or the other objects are correct. The problem is about the following: Once the illumination points on the object lights are sampled, a shadow check is performed to find whether there is an object between the light and the hit point, as always. At this point, the shadow ray might hit the object light itself and result with the hit point being in shadow. I am not sure why, but moving the origin in shadowRayEpsilon amount in the wi or normal direction did not help me this time. After many trials for hours, I decided to keep a pointer to the light object hit in the Hit class. Then, in the shadow check part, if the ray hits an object light, I compared the light object and the object hit to ensure that the shadow ray is not hitting the same object light. Finally, this helped me fix the problem.




For the next event estimation feature in part tracing, the below image, where the light seems black, is produced. I quickly realized that there was something wrong with the part where if the ray hits a light object and next event estimation is enabled, the color is returned as black (0,0,0). I realized that I was returning this without checking if the current ray is direct or indirect. Thus, it was returning black for direct rays as well, instead of just for indirect rays. I added a bool variable called isIndirect to the Ray class, performed the necessary check and the problem was solved.


diffuse_importance.png were consisting of too much noise as can be seen below. In order to correct this I have changed so many parameters that I could not keep track of each one of them, but the final change I applied was that while calculating indirect lighting contributions, I forgot to check the recursion depth. Once I add the check for the recursion depth, the image was corrected.


Resulting Images

1. Direct Lighting

The scenes rendered with direct lighting can be found below. The timing data are obtained when running with 8 threads.

cornellbox_jaroslav_diffuse.png: 10 secs 466 msecs 48 usecs

cornellbox_jaroslav_diffuse_area.png: 13 secs 409 msecs 477 usecs

cornellbox_jaroslav_glossy.png: 10 secs 668 msecs 519 usecs

cornellbox_jaroslav_glossy_area.png: 13 secs 344 msecs 988 usecs

cornellbox_jaroslav_glossy_area_ellipsoid.png: 17 secs 138 msecs 784 usecs

cornellbox_jaroslav_glossy_area_small.png: 17 secs 917 msecs 306 usecs

cornellbox_jaroslav_glossy_area_sphere.png: 15 secs 254 msecs 355 usecs

2. Path Tracing

The scenes rendered with path tracing can be found below. The timing data are obtained when running with 8 threads.

diffuse_next.png: 1 min 19 secs 607 msecs 588 usecs

diffuse_next_russian.png: 43 secs 627 msecs 212 usecs
diffuse_next_importance.png: 1 min 20 secs 566 msecs 610 usecs

diffuse_next_importance_russian.png: 57 secs 816 msecs 609 usecs

diffuse.png: 24 secs 592 msecs 982 usecs

diffuse_russian.png: 14 secs 524 msecs 812 usecs

diffuse_importance.png: 26 secs 53 msecs 300 usecs

diffuse_importance_russian.png: 19 secs 479 msecs 290 usecs

glass_next.png: 1 min 41 secs 611 msecs 419 usecs

glass_next_russian.png: 1 min 7 secs 880 msecs 896 usecs

glass_next_importance.png: 1 min 49 secs 343 msecs 252 usecs

glass_next_importance_russian.png: 1 min 32 secs 519 msecs 209 usecs


glass.png: 35 secs 871 msecs 170 usecs

glass_russian.png: 25 secs 738 msecs 965 usecs

glass_importance.png: 38 secs 259 msecs 766 usecs

glass_importance_russian.png: 34 secs 712 msecs 831 usecs

I did not have enough time to render the more advanced scenes such as the Veach Ajar scene and the Sponza scene since they take very long time.

Comments

Popular posts from this blog

Part 3: Transformations, Instancing, Multisampling & Distribution

Part 2: Recursive Ray Tracing & Bounding Volume Hierarchy

Part 5: HDR Imaging & Advanced Lighting