Progressive Photon Mapping
Photon Mapping is a global illumination algorithm developed by Henrik Wann Jensen. It is a two pass algorithm and thus its flow is a bit different than ray tracing as we know. In the first pass, photons are traced from the light sources into the scene and photon map is built. This is followed by a ray tracing pass where radiance is estimated using the photon map. It is stated that photon mapping is faster compared to monte carlo ray tracing methods, however uses more memory to store the photon map [1]. Also, error is low frequency and less noticeable compared to high frequency noise of monte carlo methods.Progressive Photon Mapping (PPM) is also a global illumination algorithm. It differs a bit from photon mapping in that passes are reversed. The first pass of PPM is a ray tracing pass and hitpoints are stored. This may be followed by any number of photon tracing passes in order to achieve the desired accuracy. I will go into implementation details, share my outputs and my notes on the algorithm.
Ray Tracing Pass
In this pass, we trace rays from the camera as we did before. Only difference is that, on diffuse surfaces, we need to store the hitpoint. The following is the hitpoint structure that I have used in my implementation and also very similar to the one explained in the paper [2].![]() |
HitPoint Structure |
Rx is used in radiance estimation. We simply look at photons inside the radius. Rx will get smaller and smaller with each photon pass so that radiance estimate may converge to the correct solution.
Nx is the currently accumulated number of photons in the radius. It gets updated with each photon tracing pass.
Mx will get incremented at each photon hit inside the radius. It is used for radius reduction and flux correction.
Lastly, each hitpoint stores the unnormalized total flux received multiplied by brdf which is called tau.
Efficient Lookup Strategy
In photon tracing pass, we will need to perform lookups to locate the nearest hitpoints the photon contributes to. In the sample code for PPM written by T. Hachisuka, the author of the paper, this is done by a simple hash table. In his Photon Mapping works, to store the photon map, Jensen suggests using a kd-tree [3]. Thus, I implemented a kd-tree to store the hitpoints and perform fast lookups. In ray tracing pass, hitpoints are stored in an array like structure and then, kd-tree is built using that array. This is because kd-tree needs to be built only once and before the photon tracing passes.Photon Tracing Pass
Firstly, we need photons! A photon is a struct of a ray, an rgb representing its power and a float for weight. We sample photons from light sources. For that, I added a virtual samplePhoton(...) method to the light base class. Each type of light should implement this method. I implemented this for point lights and area lights. For point lights, we generate a random direction over an imaginary sphere. Also, as we define point lights by intensity, we convert it to power by multiplying it with 4*pi as photons carry power. Area lights need us to generate both a point on the square and a direction.Now that photons are sampled, we trace them into the scene. When they hit a diffuse surface, we traverse the kd-tree to check if the photon is in radius of a previously recorded hitpoint. If that is the case, photons power is multiplied by the brdf and added to tau variable of the hitpoint. Mx is also incremented by 1. Lastly, photon may be reflected from the hitpoint and traced into the scene. This is similar to path tracing. The paper suggests the use of russian roulette to decide if a photon should be absorbed or reflected. I instead used max recursion depth approach. It seems to work fine, however there is one bug that I will mention later on and this may be caused by this approach.
When photons hit a specular surface such as mirror or glass, they are not stored. Instead, a new photon is created and traced into the scene. I multiply the reflectance of the surface by the power of the photon to find the power of new photon, however, I am not sure if this is 100% correct.
Radiance Estimate
This is essentially the last step of a single photon tracing pass. There are 3 points that need to be handled.Firstly, we need to reduce the radius. We calculate (Nx + aMx) / (Nx + Mx) and name it factor. The a in factor stands for an alpha variable that determines the ratio of the photons that should be kept. It is usually set to 0.7 - 0.8. In order to find the next value of radius, current radius is multiplied by sqrt(factor).
Secondly, flux correction should be applied. Because the radius is reduced, some of the photons will be left outside and the tau variable should be updated accordingly. This is done by multiplying tau with factor.
Lastly, we evaluate the radiance at the hitpoint. N_emitted is the amount of photons emitted into the scene. When we have multiple photon tracing passes, N_emitted increases as well. We can evaluate the radiance as follows:
- L = tau / (pi * (Rx^2) * N_emitted)
Before we take a look at the outputs, there are some renderer parameters that we will inspect and these are integrated into scene files as follows:
<Renderer>ProgressivePhotonMapping</Renderer>
<RendererParams>
<Alpha>0.8</Alpha>
<ProgressiveCount>32</ProgressiveCount>
<NumPhotons>1000000</NumPhotons>
<R_Initial>0.003</R_Initial>
</RendererParams>
I will try to show how changing them affects the outputs.
Outputs / Notes
For the first four images, only change is NumPhotons. The others are constant and set as Alpha = 0.8, ProgressiveCount = 4, R_Initial = 0.001. NumPhotons increase as 10K, 100K, 1M, 10M. Also sample per pixel is 16.![]() |
cornellbox_jaroslav_diffuse.exr saved in 14,823s |
![]() |
cornellbox_jaroslav_diffuse.exr saved in 15,3s |
![]() |
cornellbox_jaroslav_diffuse.exr saved in 19,782s |
![]() |
cornellbox_jaroslav_diffuse.exr saved in 1m6,356s |
If we look at the first 3 images, we see that their render times are extremely close while their quality differ by a lot. This is because the photon tracing passes are quite fast but building the kd-tree takes some time especially at higher sample per pixel rates. I feel like progressive photon mapping prefers lower spp. Because more hitpoints means more time spent building and traversing the kd-tree. However, extremely close points don't really help with the radiance estimates. More photons would be preferred instead.
Next, lets inspect R_initial. In the first image it is set to 0.1. In the second one it is 0.4. When we set it to a rather small value but do not send enough photons, we get interesting results. If however we set it to a large value, we get a biased image. Also, large radius values cause rendering time to increase by a lot.
![]() |
cornellbox_jaroslav_diffuse.exr saved in 31,781s |
![]() |
cornellbox_jaroslav_diffuse.exr saved in 1m19,259s |
When I increased the ProgressiveCount to 64 and send 10M photons at each pass, the resulting image is closer to the actual solution of the rendering equation.
![]() |
cornellbox_jaroslav_diffuse.exr saved in 13m48,887s |
Next, we add specular surfaces. ProgressiveCount = 8, 10M each.
![]() |
cornellbox_jaroslav_glass.exr saved in 3m4,593s |
I have had some trouble with glass objects. Firstly, it appears brighter than I expected. Secondly, caustics aren't as apparent as I hoped they would be. I have spent time debugging however could not figure out the problem.
![]() | |
cornellbox_jaroslav_glass.exr saved in 3m1,393s |
Lastly, I changed the material of the blue sphere to mirror. Now, you are able to see the photons reflected from the mirror into the glass causing a bright caustic on the green wall.
With this, I conclude this project. Progressive Photon Mapping turned out to be an interesting algorithm and allows you to really experiment with the variables to see interesting results. I intend on finding and fixing the bug related to refractive objects at a later time.
References
- "A Practical Guide to Global Illumination using Photon Mapping", Henrik Wann Jensen, Frank Suykens, and Per Christensen, SIGGRAPH'2001 Course 38, Los Angeles, August 2001
- Toshiya Hachisuka, Shinji Ogaki, and Henrik Wann Jensen. 2008. Progressive photon mapping. In ACM SIGGRAPH Asia 2008 papers (SIGGRAPH Asia '08), John C. Hart (Ed.). ACM, New York, NY, USA, Article 130, 8 pages. DOI: https://doi.org/10.1145/1457515.1409083
- Realistic Image Synthesis using Photon Mapping, Henrik Wann Jensen, ISBN: 1-56881-140-7. AK Peters, 2001
Comments
Post a Comment