Playing Russian Roulette

[ LiB ]

Playing Russian Roulette

You must now create the logic behind how a photon reacts to a surface. If the surface is mostly diffuse, diffuse reflection can be easily simulated. If the surface is mostly specular, specular reflections can be utilized. But what if the surface is partially diffuse and specular? How do you determine the nature of the photon in this particular instance?

Determining whether the surface is specular or diffuse is called Russian roulette . The term is exactly what it states. Putting a gun to your head and playing with the probability of it shooting or not is a very dangerous game. If a photon interacts with a surface in a particular instance, you must randomly yet proportionally select whether the photons get reflected/transmitted or absorbed. This is a factor of the diffuse and specular coefficients of the material and the chance of absorption . Deriving an algorithm to randomly yet proportionally select whether a photon is absorbed or reflected/transmitted (diffuse or specular) is required.

Let's take a look at how you randomly yet proportionally select diffuse, specu-lar, and absorbed photons. The maximum range of the reflection/transmis-sion (diffuse and specular) and absorption components must be more than 0.0 and less than or equal to 1.0. The Russian roulette formula is as follows :

(Diffuse + Specular + Absorption) > 0 and < = 1.0

Let's take a look at some instances of the behavior a photon takes when it interacts with a point in the scene.

  • If the specular component is 1.0 (or 100%), the diffuse component must be 0 and there is no chance of random absorption into the material.

  • If the diffuse component is 1.0 (or 100%), the specular component must be 0 and there is no chance of random absorption into the material.

  • If the diffuse component is 0.4 (or 40%), the specular and absorption components can be the remaining 0.6 (or 60%) and there is a fairly good chance of random absorption into the material.

  • If the specular component is 0.2 (or 20%), the diffuse and absorption components can be the remaining 0.8 (or 80%) and there is a very good chance of random absorption into the material.

  • If the diffuse component is 0.25 (or 25%) and the specular component is 0.25 (or 25%), this means that the remaining 0.5 (or 50%) is the absorption component. There is an excellent chance of absorption into the material.

The application will determine the probability in each instance that a photon has intersected a point on an object (traced from the image plane). Let's cover the pseudocode on how to achieve this concept.

Interaction Logic

Implementing an algorithm to simulate Russian roulette on a photon that has intersected an object and is waiting to be handled can be a tricky task. The first thing you need to do in order to randomly yet proportionally select diffuse, specular, and absorbed photons is to use a random number generator. The random number generator generates a random number in a domain (0 to 1.0) that includes these three components. Now when a state is set for a specific photon from the random number generator, the specific logic for the type of lighting effect is executed. For example if the random state is diffuse, the diffuse lighting logic and code are executed.

The process to actually do this using C/C++ is very simple. All you need to do is randomly generate a number greater than 0.0 and less than or equal to 1.0.

The following code shows how to do this:

 // get number between 0 and 1 float random = -1 + 2*(float)rand() / (float)RAND_MAX; if(random < 0) random          = -random; 

The Diffuse, Specular, and Absorption Ranges

The three components share a domain. The diffuse component begins from zero to the diffuse factor of the material. The specular component begins from the diffuse factor and ends at the absorption factor of the material. The absorption factor is the remaining factor that begins from the specular factor and ends at one. The listing order of each component is listed as:

 Diffuse Range   =  Begin:  0.0  End:  Diffuse Factor of Material Specular Range  =  Begin:  Diffuse Factor of Material  End:  Diffuse Factor of Material + Specular Factor of Material Absorption Range   =  Begin:  Diffuse Factor of Material + Specular Factor of Material  End:  1.0 The following code shows how to branch the logic:    // if diffuse reflection    if(random < pObject->fDiffuseFactor)    {         // TODO: Store photon in photon map         // TODO: Add Diffuse Reflection Code    }    // if specular (mirror) reflection    else if (random >= pObject->fDiffuseFactor &&       random < pObject->fDiffuseFactor + pObject->fSpecularFactor)    {        // NOTE: Do nothing to photon map        // TODO: Add specular (mirror reflection or refraction) code    }    else    {      // TODO: Store photon in photon map, photon's life has ended    } 

Reporting Effects

Because the nature of a photon is based on a series of situations, it would be wise to track the lighting effects that are simulated in the photon-tracing process. If the light is absorbed, mirror reflected, refracted, and diffuse reflected then it is incremented by the application. In order to do this, four variables are required for each effect.

These variables are as follows:

 long      nAbsorbed; long      nRefracted; long      nSpecularReflected; long      nDiffuseReflected; 

To utilize these counter variables, they must be placed at specific points in the tracing code and finally reported after the tracing process has commenced. These statistics are printed at the bottom of CreatePhotons() method.

 void cScene::CreatePhotons() {    // Previous Code is Assumed    // Report Information    fprintf(stderr,"\n[STATS]\nActual Stored Photons =                     %d\n",lNumGlobalPhotons);    fprintf(stderr,"Absorbed Hits: %d\n",            nAbsorbed);    fprintf(stderr,"Specular Reflected hits: %d\n",  nSpecularReflected);    fprintf(stderr,Diffuse Reflected hits: %d\n",   nDiffuseReflected);    fprintf(stderr,"Refracted Hits: %d\n",           nRefracted);    } 

A Simple Example

Pretend you have an object that has a specular component of 0.2 (or 20%) and diffuse component of 0.5 (or 50%). This means the remaining absorption component is 0.3 (or 30%). Now you can shoot a few photons towards the object and see how the nature of the photon will behave based on the material's properties and the probabilistic solution. Let's see how the algorithm would perform on a few materials with specific types of factors assigned for the diffuse, specular, and absorbed components.

In the first instance, 0.3 is generated. This means that the photon is diffu-sively reflected and stored when the random number (0.3) is less than the diffuse component (0.5) or falls inside the diffuse range. This means that the photon should be stored in the global photon map and a new diffuse reflected photon should be launched. The logic is as follows:

 Random Number is 0.3 // Meaning the photon is diffusively reflected and stored If 0.3 is less than 0.5    Store Photon in Global Photon Map    Randomly Reflect Photon 

In the second instance, 0.6 is generated. This means that the photon is spec-ularly reflected but not stored. In this case, the random number (0.6) is more than the diffuse component (0.5) and is less than the absorption (0.7) component, meaning it falls inside the specular range. This means that a new photon is to be launched in the mirror direction but the photon is not stored in the global photon map. The logic is as follows:

 Random Number is 0.6 // Meaning the photon is specularly reflected but not stored If 0.6 is less than ((D<0.5> + S<0.2>) = 0.7) and more than or equal to D<0.5>    Do Nothing to Global Photon  Map    If Object is Transparent Refract Photon between Two Mediums (air to water)    Else Perfectly reflect photon in mirror direction    End If 

In the third instance, 0.45 is generated. This means that the photon is diffu-sively reflected and stored. In this case, the random number (0.45) is less than the diffuse component (0.5) or falls inside the diffuse range. This means that the photon should be stored in the global photon map and a new diffuse reflected photon should be launched. The logic is as follows:

 Random Number is 0.45 // Meaning the photon is diffusively reflected and stored If 0.45 is less than 0.5    Store Photon in Global Photon Map    Randomly Reflect Photon 

In the fourth instance, 0.9 is generated. In this case, the random number (0.9) is less than 1.0 and is more than the diffuse and specular ranges, which means it falls in the absorbed range. This means that the photon is absorbed and its life has ended. The logic is as follows:

 Random Number is 0.9 // Meaning the photon is absorbed and it's life has ended If 0.9 is more than or equal ((D<0.5> + S<0.2>) = 0.7) and less than or equal 1.0    Store Photon in Global Photon Map 

As you can see, the algorithm focuses on the component that carries the highest weight of the three. The range of each component is as follows:

  • From 0 to 50% is the diffuse component.

  • From (50% to 70%) or 20% is the specular component.

  • The remaining 30 % is the absorption component.

Using this method, how photons scatter is determined by the material type. The numbers that are randomly generated mean that 50% of them will be targeted towards diffuse reflections. This is a probabilistic method of efficiently choosing photons on different types of materials. Russian roulette is a very important way of distributing photons. If 50 photons were shot at the surface, 70% of them will be reflected either as diffuse or specular reflections. The remaining 30% will be terminated and absorbed into the object. This way, fewer photons are reflected, which simulates the concept of energy being turned into heat.

Using the Chapter's Source Code

You can now write a method that processes photons when object intersections occur. The method passes the object index, photon direction, intersection point, maximum trace depth, and a Boolean variable to scatter the light in or out of the object. The method's primary purpose is to scatter photons based on the probabilistic solution. Photons will either be absorbed or reflected/transmitted. You also want to add the rendering settings parameters so you can be selective about how you construct the photon map. As you build the ProcessPhotonHit() method, you therefore will also apply the rendering settings parameters to each section of the code.

 void cScene::ProcessPhotonHit(int iObjIndex,                  cVector3 tPhotonDir,                  cVector3                  tInter,                  photon_t tPhoton,                  int depth,                  BOOL insideobj) {    cVector3 tNormal;    cVector3 tReflected,tRefracted;    color3 tColor;    int iRecurseIndex;    cVector3 tRecurseInter;    photon_t tRecursePhoton;  cObject *pObject; photon_t tPhotonCopy; cVector3 Dummy; tPhotonCopy = tPhoton; pObject     = &pObjectList[iObjIndex]; tNormal     = pObject->Normal(tInter); tColor      = pObject->Color; // MAX DEPTH if (depth <=0) return; else depth; // Do probability of how much energy should // either be Reflecteed/Transmitted or Absorbed // get number between 0 and 1 float random = -1 + 2*(float)rand() / (float)RAND_MAX; if(random < 0) random = -random; // if diffuse if(random < pObject->fDiffuseFactor) {    // absorbing photon (diffuse interaction)    if (RenderSetting.use_diffuse_shading &&        (pObject->fDiffuseFactor >  0.0) &&        (pObject->fOpacity > 0.0))    {       this->nAbsorbed++;       //fprintf(stderr,"[a]");       tPhoton.tPosition = tInter;       // photons are stored pointing opposite on the surface       Dummy = tPhotonDir;       Dummy.Invert();       tPhoton.tDirection = Dummy;       tPhoton.tPower = tPhoton.tPower * pObject->fOpacity;       AddToPhotonList(&pGlobalPhoton,&lNumGlobalPhotons,tPhoton);    }    // diffuse reflection for photon    if (RenderSetting.use_diffuse_reflection &&        (pObject->fDiffuseFactor > 0.0) &&(depth != 0))    {       tReflected = tPhotonDir.Diffuse_Reflection(tNormal);              cRay Bullet;             Bullet.origin      = tInter;             Bullet.direction   = tReflected;             iRecurseIndex = Find_Closest_Object                      (Bullet, &tRecurseInter);             if (iRecurseIndex > -1)             {                this->nDiffuseReflected++;                //fprintf(stderr,"[d]");                tRecursePhoton.tPower.r = tPhotonCopy.tPower.r *                     pObject->Color.r * pObject->fDiffuseFactor ;               tRecursePhoton.tPower.g = tPhotonCopy.tPower.g *                  pObject->Color.g * pObject->fDiffuseFactor ;             tRecursePhoton.tPower.b = tPhotonCopy.tPower.b *                  pObject->Color.b * pObject->fDiffuseFactor ;             ProcessPhotonHit(iRecurseIndex,                                    tReflected,                                tRecurseInter,                                tRecursePhoton,                                          depth,                                     insideobj);          }       }    }    // if specular    else if ((random >= pObject->fDiffuseFactor)        && (random < pObject->fDiffuseFactor+pObject->fSpecularFactor))    {       // if object is transparent to some degree       if (RenderSetting.use_refraction &&            (pObject->fOpacity < 1.0f) && (depth != 0))       {            // refracted hits         if(insideobj == FALSE)            tPhotonDir.Vector_Refraction(tNormal, 1.003f, pObjectList[iObjIndex].fIndex_Of_Refraction,                       &tRefracted);            else            tPhotonDir.Vector_Refraction(tNormal,                         pObjectList[iObjIndex].fIndex_Of_Refraction,                       1.003f, &tRefracted);             cRay Bullet;             Bullet.origin    = tInter;             Bullet.direction = tRefracted;             iRecurseIndex = Find_Closest_Object                          (Bullet, &tRecurseInter);             if (iRecurseIndex > -1)             {                this->nRefracted++;                //fprintf(stderr,"[r]");                tRecursePhoton.tPower.r =                     tPhotonCopy.tPower.r *                     pObjectList[iObjIndex].Color.r *                     1.0f - pObject->fOpacity;                tRecursePhoton.tPower.g =                    tPhotonCopy.tPower.g *                    pObjectList[iObjIndex].Color.g *                    1.0f - pObject->fOpacity;                tRecursePhoton.tPower.b =                    tPhotonCopy.tPower.b *                    pObjectList[iObjIndex].Color.b *                    1.0f - pObject->fOpacity;                ProcessPhotonHit(iRecurseIndex,                                 tRefracted,                              tRecurseInter,                              tRecursePhoton,                                        depth,                      (insideobj == TRUE) ? FALSE : TRUE);              }          }          else          {   Not You don't have to use photon mapping           //or  specular mirror reflections. You should use           //ray  tracing instead.           // specular mirror  reflected          if (RenderSetting.use_specular_reflection &&            (pObject->fSpecularFactor > 0.0) && (depth != 0))          {             tReflected = tPhotonDir.Vector_Reflection(tNormal);             cRay Bullet;             Bullet.origin = tInter;             Bullet.direction = tReflected;             iRecurseIndex =  Find_Closest_Object                       (Bullet, &tRecurseInter);             if (iRecurseIndex > -1)             {                this->nSpecularReflected++;                //fprintf(stderr,"[s]");                tRecursePhoton.tPower.r =                       tPhotonCopy.tPower.r *                       pObject->Color.r *                       pObject->fSpecularFactor;                tRecursePhoton.tPower.g =                       tPhotonCopy.tPower.g *                       pObject->Color.g *                       pObject->fSpecularFactor;                tRecursePhoton.tPower.b =                     tPhotonCopy.tPower.b *                    pObject->Color.b *                    pObject->fSpecularFactor;               ProcessPhotonHit(iRecurseIndex,                            tReflected,                        tRecurseInter,                        tRecursePhoton,                       depth,insideobj);              }           }        }     }     else     {      // absorbing photon and kill it     if (RenderSetting.use_diffuse_shading &&         (pObject->fDiffuseFactor > 0.0) && (pObject->fOpacity > 0.0))     {        this->nAbsorbed++;        //fprintf(stderr,"[a]");        tPhoton.tPosition = tInter;        // photons are stored pointing opposite on the surface        Dummy = tPhotonDir;        Dummy.Invert();        tPhoton.tDirection = Dummy;        tPhoton.tPower = tPhoton.tPower * pObject->fOpacity;        AddToPhotonList(&pGlobalPhoton,&lNumGlobalPhotons,tPhoton);     }   } } 

Figure 11.13 attempts to convey the concept of photons being distributed and stored in the global photon map.

Figure 11.13. A left view of the room of photons stored in the global photon map.

graphic/11fig13.gif


By now you should have an idea of how photons are stored in the global pho-ton map. The behavior of photons that intersect objects in the scene is the result of a probabilistic solution. The photons that are stored at surfaces in the photon map represent the incoming energy from the light sources. The next process is to add the observer into the algorithm. The observer is going to perceive the reflected radiance of each point on a surface. Calculating how much light is leaving in conjunction with how much light is arriving at each point on every surface in the observer's field of view is the final part of the photon-mapping two-pass process.

[ 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