Implementing Photon Mapping

[ LiB ]

Implementing Photon Mapping

The major step when implementing photon mapping is to break each section into a component form. Let's take a look at what you need to do in order to implement the algorithm:

  1. Release photons from the light sources in the scene.

  2. Process the photons at the points of intersection.

  3. Store the photons in the photon map based on a conditional state.

  4. Render the scene using the photon map.

The Second Pass of the Photon-Mapping Process

The second pass of the photon mapping process is where the final image is rendered. The rendering process calculates the reflected radiance of each point on a surface using the photon map. The camera or observer is taken into account here. The process that's used is the same as in backward ray tracing.

You shoot rays from the view plane and find intersections; the point of interest is the point where the ray hits. Recall from Chapter 9 that you used the cosine shading method to calculate the radiance of a point projected on the image plane. In order to apply the irradiance (light to object) and the radiance (light to eye) to the point of interest, however, you must incorporate the photon map into the solution. Remember the photon map is a cache of light paths taken from the light sources to the objects in the scene but is then used again to find the reflected radiance of light leaving a specific area. This means that the photon map is used twice. You apply the irradiance to the point of interest by adding something called the radiance estimate.

NOTE

NOTE

The photon rendering process uses backward ray tracing to render the final image to the image plane using photon mapping.

The radiance estimate finds the closest photons around the point of intersection (irradiance) and calculates the reflected radiance for the point of interest using these photons. These are neighboring photons that are in close vicinity to each other. The amount of neighboring photons that are returned is a factor of a pre-defined distance. One photon by itself cannot determine the radiance of a point in 3D space, but grouping a set of photons together and finding the average in a small area yields a good estimation of how much light is arriving and leaving the point of interest. See Figure 12.1.

Figure 12.1. The closest photons around the point of intersection are used to deter mine the amount of light arriv ing and leaving a specific area on a surface.

graphic/12fig01.gif


You gather the neighboring photons at the point of intersection by locating the plane the point sits upon. The tangent plane for a point on a sphere is along its curve, whereas the tangent plane for a flat surface (such as a triangle or polygon) is the surface normal. You need to search for the closest pho-tons that sit in the tangent plane of the point. This is how you'll match the illumination information with the geometry of the scene. You'll use the pho-tons sitting across the surfaces in the scene when rendering using the photon map. See Figure12.2.

Figure 12.2. The more pho- tons you search for,the better the radiance estimation for each point on a surface.

graphic/12fig02.gif


So how do you find the closest neighbors? The process is very simple. If you think about it for a moment, all you need to do is use a geometric volume such as a cube or a sphere to encompass the closest photons around the point of intersection. See Figure 12.3. You can use other volumes as well, such as cylinders or pyramids . Choosing the ideal bounding volume is a choice of selection, meaning you can increase better accuracy and speed for photons that are returned.

Figure 12.3. A bounding vol ume such as a cube or sphere is used to encompass the clos est photons around the point of intersection.

graphic/12fig03.gif


The examples in this chapter use a sphere to find the radiance estimate for every point on a surface. However, the sphere that you initially start out with is going to turn into a circle. To find the area of a circle, you simply multiply pi by the radius squared. The area of the circle is the number of squared units that make up the circle.

Photon Mapping Example

Now let's get back to the example of the polygon sitting 30 units in the screen. You are going to shade this polygon using the radiance estimate. The observer is located at (0,0,-10); the light source is located at (0,0,-5). See Figure 12.4.

Figure 12.4. This example scene shows an observer located behind the light source.The observer is in front of the polygon sit ting 30 units into the screen.

graphic/12fig04.gif


You might be wondering why a circle is being used, because a circle represents 2D, rather than 3D, space. Before I address this issue, I must propose a few more examples. Remember, the objective is to shade every point for every object in the observer's field of view using photon mapping. In the polygon example's case, you cast rays using the local coordinate system towards the polygon. See Figure 12.5.

Figure 12.5. Photon tracing begins by shooting photons to the object in the form of rays from the light source in a spi ral fashion.

graphic/12fig05.gif


The polygon's material diffuse coefficient is 1.0. This means that most of the photons that intersect the object will randomly reflect photons in arbitrary directions as well as save local copies in the photon map at the points of intersection. See Figure 12.6.

Figure 12.6. The pho- tons are shot from the light source and are stored and reflected (dif- fusively) on the object surface at different points of intersection.

graphic/12fig06.gif


To render the polygon projected on the image plane, you must find the radiance estimate for each point on the polygon. But before you can find the radiance estimate, you must find the closest photons around the point of intersection, also known as the photon contribution . This process is as follows :

  1. Place a sphere around the point of intersection.

  2. Expand the sphere until it includes enough photons as you initially requested . For example, if you searched for 50 photons, you expand the sphere until it returns 50 photons.

  3. Expand the sphere that's placed around the point of intersection until it returns with the farthest photon found.

NOTE

NOTE

Sometimes the initial request for the closest photons may return with fewer photons found if the photon map is small and the initial search request is greater than all the photons in the photon map.

The process of expanding a sphere around the point of intersection gives you a good estimation of the overall incoming energy (flux) at the surface. Figure 12.7 shows an expanding sphere.

Figure 12.7. The sphere expands based on the location of the farthest photon found.

graphic/12fig07.gif


So, why is the sphere going to turn into a circle? Recall that polygons and triangles are flat primitives. The points that make up these primitives lie within one tangent plane. A sphere is a little more complicated because of the large number of tangent planes. But this doesn't matter because the expanding sphere at the point of interest is infinitely small on the tangent plane. The unit sphere's infinite angles are controlled by 0 < a < 1 . In the current example, the polygon is flat and the sphere is going to turn into a circle. The circle is what you're interested in. The radius of the sphere is from the center point of the circle to the farthest photon found. The diameter of the sphere is the radius squared. See Figure 12.8.

Figure 12.8. The sphere turns into a circle because the surface is flat.

graphic/12fig08.gif


By finding the closest set of photons around the point of interest, you'll get a pretty good estimation of the radiance estimate. The radiance estimate is deter mined by summing the closest photons found and dividing them by the area of the circle found . On large surfaces with slow changing lighting, some photons that lie in close vicinity at the surface are not very different in intensity. This means that when shading each point on the surface, the returned radiance estimate will be a slow changing value. This is a benefit when rendering because the final image will have very little noise.

Upgrading the PhotonMap Class

You can now add a variable to the PhotonMap class that counts the number of photons to search for when finding the radiance estimate:

 long      lMaxPhotonNeighbors; 

You can also write a set method to define the scattering and rendering parameters of photons in the PhotonMap class. The member variable lMaxPhotonPerObject is responsible for the maximum photons shot per object in the scene. The variable lMaxPhotonNeighbors is now responsible for finding the closest neighbors at the point of intersection. The code is as follows

 void PhotonMap::SetupPhotonMapping(long max_photons, int max_friends) {    // max photons per object   lMaxPhotonPerObject = max_photons;    // max neighbors to search for when finding the radiance estimate    lMaxPhotonNeighbors = max_friends; } 

Creating a Photon List Structure

To implement the logic of finding the radiance estimate, you need to traverse the photon map array (or search the photon map) to find the closest set of photons for the point of intersection. In order to keep track of each photon that was found with its distance relative to the point of interest, you need a new structure that's to be used when searching for photons. This new structure is composed of the photon structure added to a distance scalar. The distance scalar determines how far each neighboring photon is away from the point of intersection.

 typedef struct {    photon_t tPhoton;    float dDistance; } photonlist_t; 

Finding the Closest Photons

You can now write a method that will search through your photon map and gather photons based on a pre-defined number. This method passes three parameters:

  • The first parameter ( tList[] ) is a storage array that holds the returned photons closest to the point of intersection.

  • The second parameter ( lNumber_Of_Friends ) is the number of neighboring photons to search for.

  • The third parameter ( tInter ) passes the intersection point from where all searching will originate.

The function searches through the photon map, finds the closest number of photons (based on their distance to the intersection point), and saves them in the storage array. The function finally returns with the number of photons found. When the function completes, the storage array is filled with the closest photons around the point of intersection.

The method can also list the photons in distance order. For example, the nearest photons to the point of intersection are listed first in the array and farther photons are listed towards the end of the array. The farthest photon is listed as the last element in the array; this is what creates the expanding sphere around the intersection point. The radius of the expanding sphere is the distance from the sphere's center to the last photon in the array. This photon is used to calculate the area of the bounding sphere.

 long PhotonMap::GetNearestPhotons(            photonlist_t tList[],            long lNumber_Of_Friends,            cVector3 tInter ) {    // Note the function's job is to find closest photons using distance    long i,j;    float dMaxDist = 1000000000; // bogus distance    float dCurrDist;    long lCount = 0;    cVector3 Dummy;    // for all the photons in photon map    for (i = 0; i < lNumGlobalPhotons; i++)    {       // find distance between photon and intersection point       Dummy = pGlobalPhoton[i].tPosition - tInter;      // find magnitude of the ray      dCurrDist = Dummy.Mag();      // if current squared distance is more than last max distance      if (dCurrDist > dMaxDist)         continue;       // order found photons in distance order for the number       // of requested photons      for (j = ((lCount < lNumber_Of_Friends-1)                 ? lCount : lNumber_Of_Friends-1); (j > 0)                 && (tList[j-1].dDistance > dCurrDist); j)          tList[j] = tList[j-1];      // assign returned array with close photon      tList[j].dDistance = dCurrDist;      tList[j].tPhoton = pGlobalPhoton[i];      // the maximum request is found,      // the farthest photon is the maximum distance      if (lCount == lNumber_Of_Friends)         dMaxDist = tList[lNumber_Of_Friends-1].dDistance;      else         lCount ++;    }    // return the count of found photons    return lCount; } 

The radiance estimate is finally found by adding the returned photons and dividing them by the area of the circle found. See Figure 12.9. The amount of photons that are included within this circle represents the overall incoming illumination for the point of interest. Even though this is one point you are interested in shading, other neighboring points across each scan line of the surface will share the same neighboring photons. Therefore, the illumination information is separated from the geometry and preserved in a small, compact, and clever manner. This makes things much easier because reusing neighboring photons saves memory.

Figure 12.9. The radiance estimate is determined by summing the found photons at the point of intersection and dividing them by the area of the circle found.

graphic/12fig09.gif


Determining the Photon Contribution

In this section, you write a method that will find the radiance estimate of a point in 3D space. The function is going to incorporate the material type and the incoming and outgoing lighting into one solution.

This method passes three parameters:

  • The first parameter ( iObjectIndex ) is the object index of the object that was intersected first by the ray from the image plane.

  • The second parameter ( tDirection ) is the direction of the ray that was spawned from the image plane.

  • The third parameter ( tInter ) is the intersection point of the ray.

The method creates a local array to store the nearest photons found. It continues by calling the GetNearestPhotons() method. The local list is updated with the closest photons around the point of intersection after GetNearestPhotons() is executed. The number of photons found is tallied by lNumPhotons . If no photons were found by GetNearestPhotons() , it will return with the radiance estimate set to black (or no illumination for the point).

To add the irradiance at the point of intersection, the function utilizes the number of found photons. The function will cycle through each photon to find the overall density. This process continues for every photon found in a list returned by GetNearestPhotons() . It calculates the angle between the pho-ton's direction and the normal of the surface. This is done to ensure that the two vectors are facing each other, which in turn ensures that the photon contributes to the surface lighting (also the indirect illumination). You then need to add the sample estimate (or direct illumination) of the ray taken from the eye (image plane) to the point of interest. This combines the direct and indirect illumination into one solution. After the closest photons are found they are added together and divided by the area of the circle found. This is the final solution. Once you have the radiance estimate, you apply the color of the object to the solution. Finally, the estimate is saturated and the local photon array is unallocated . The method returns with the radiance estimate for x hr space :

 color3 cScene::PhotonContribution(                int iObjectIndex,                cVector3 tDirection,                cVector3 tInter) {     // allocate memory     photonlist_t *pList = new photonlist_t[lMaxPhotonNeighbors];     long lNumPhotons;     long i;     color3 tColor, tTmp;     float dDot;     float dPiR2;     // this is returned for no illumination     tColor = color3::Black;     // get nearest photons around the point of intersection     lNumPhotons  = GetNearestPhotons(pList,lMaxPhotonNeighbors,tInter);     // no photons found, omg very bad     if (lNumPhotons == 0)     return tColor;     // for the amount of friends found     for (i = 0; i < lNumPhotons; i++)     {         // is this friend facing the point of intersection         dDot = pObjectList[ iObjectIndex ].Normal(tInter)                        *  pList[i].tPhoton.tDirection;            // if not, let's go to next array node            if (dDot <= 0.0)               continue;            // add the power of the photon and the cosine shading method            tTmp = pList[i].tPhoton.tPower *                       (float) dDot *                       pObjectList[iObjectIndex ].fDiffuseFactor;            tColor.r = tColor.r + tTmp.r;            tColor.g = tColor.g + tTmp.g;            tColor.b = tColor.b + tTmp.b;         }         // get area of circle found using last photon in the found list         dPiR2 = 3.1415926f *                 pList[lNumPhotons-1].dDistance *                 pList[lNumPhotons-1].dDistance;        // after summing up everything divide it by the area of the circle        tColor.r = tColor.r / (float) dPiR2;        tColor.g = tColor.g / (float) dPiR2;        tColor.b = tColor.b / (float) dPiR2;       // used to color the photon with the object       tColor.r *= pObjectList[ iObjectIndex ].Color.r;       tColor.g *= pObjectList[ iObjectIndex ].Color.g;       tColor.b *= pObjectList[ iObjectIndex ].Color.b;      // saturate the colors that are out of range      tColor.Sat();     // free memory     if(pList) delete [] pList;     // return final illuminated color using photon mapping     return tColor; } 

[ LiB ]


Focus On Photon Mapping
Focus On Photon Mapping (Premier Press Game Development)
ISBN: 1592000088
EAN: 2147483647
Year: 2005
Pages: 128
Authors: Marlon John

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net