/******************************************************************/ /* BRDFMaterial.cpp */ /* ----------------------- */ /* */ /* The file defines a simple material class for path tracing. It */ /* allows a BRDF class to be passed in. This class's shade */ /* function then samples the BRDF according to it's members */ /* and modulates the color by a fired ray. This class is here */ /* to make development of new BRDFs easy (so the Shade() func */ /* need not be rewritten). */ /* */ /* Chris Wyman (03/30/2007) */ /******************************************************************/ #include "Materials/BRDFMaterial.h" #include "Materials/BRDF.h" #include "Materials/LambertianBRDF.h" #include "Core/Ray.h" #include "Objects/Primitive.h" #include "Core/Scene.h" #include "Lights/Light.h" #include "Utils/Random.h" #include "Thread/Thread.h" Color BRDFMaterial::Shade( Ray &ray, Scene &scene ) const { if (ray.depth > scene.GetMaxRayDepth()) return Color::Black(); if (useExplicitDirectLighting) return ExplicitDirectLight( ray, scene ); return ImplicitDirectLight( ray, scene ); } Color BRDFMaterial::ImplicitDirectLight( Ray &ray, Scene &scene ) const { // Get the hit position, and compute the surface normal Point hitPos = ray.GetHitPoint(); if (!ray.HaveSurfaceNormal()) ray.hitObj->ComputeNormal( ray ); // Find BRDF vectors Vector randomVector = brdf->RandomlySelectOutgoingVector( ray.thread->RandGenerator(), ray.surfNorm ); Vector viewVector = -ray.direction; viewVector.Normalize(); // Find the probability of sampling that random vector float oneOverProb = 1.0f/brdf->GetProbabilityOfSampling( ray.surfNorm, randomVector ); // Compute N dot randVector float cosine = randomVector.Dot( ray.surfNorm ); // Create our random ray Ray bouncedRay( ray.thread, hitPos, randomVector, ray.depth+1 ); // Shoot our random ray, use Monte Carlo sampling (1/N * sum[ brdf(w_o,w_i)*L(x,w_i)*(N . w_i) / prob ] ) // Here we assume the summation and the 1/N is done by the image display function (or somewhere else). return scene.TraceRay( bouncedRay ) * brdf->SampleBRDF( ray, viewVector, randomVector ) * cosine * oneOverProb; } Color BRDFMaterial::ExplicitDirectLight( Ray &ray, Scene &scene ) const { // Get the hit position, and compute the surface normal Point hitPos = ray.GetHitPoint(); if (!ray.HaveSurfaceNormal()) ray.hitObj->ComputeNormal( ray ); Vector viewVector = -ray.direction; viewVector.Normalize(); // Shoot direct lighting rays Color directLightResult(0,0,0); for (int i=0; i< scene.GetNumLights(); i++) { // Get information about this particular light Light *lght = scene.GetLight( i ); float lightArea = lght->GetLightPrimitive()->GetArea(); // Find a random point on this light Point randomLightPoint = lght->GetRandomPoint( ray.thread->RandGenerator() ); Vector toLight( randomLightPoint-hitPos ); float distToLight = toLight.Normalize(); // Check cosines to see if these points are mutually above their horizons. float cosine1 = toLight.Dot( ray.surfNorm ); float cosine2 = lght->GetLightPrimitive() ? -toLight.Dot( lght->GetLightPrimitive()->ComputeNormal( randomLightPoint ) ) : 1; if (cosine1 < 0 || cosine2 < 0) continue; // Shoot the ray to determine visibility. Ray randShadowRay( ray.thread, hitPos, toLight, ray.depth+1 ); // ( My TraceShadowRay() returns a true/false depending on if an object is // encountered between t=EPSILON and the second argument (distToLight-EPSILON) ) if (!scene.TraceShadowRay( randShadowRay, distToLight-ray.EPSILON )) { // We hit the light (this ray was not in shadow)! Color lghtColor = lght->GetColor(); // GetColor() returns the point-light color. if (lght->IsAreaLight()) // But maybe the light's color varies over the surface? { randShadowRay.hitDist = MAXFLOAT; // So, we need to intersect the light to find the color randShadowRay.hitObj = 0; // at this particular intersection point lght->GetLightPrimitive()->Intersect( randShadowRay ); if (!randShadowRay.hitObj) // Error checking -- we didn't hit the object??? (Why?) lghtColor = Color::Black(); // In that case, say we didn't -- shadow ray = black else // No, we hit the light, so get the color at the hitpoint (i.e., call Shade()) lghtColor = lght->GetLightPrimitive()->GetMaterial()->Shade( randShadowRay, scene ); } // Compute the contribution from our explicit light ray sample directLightResult += brdf->SampleBRDF( ray, viewVector, toLight ) * lghtColor * ( cosine1 * cosine2 * lightArea / ( distToLight * distToLight )); } } // Find random outgoing vector for the indirect bounce Vector randomVector = brdf->RandomlySelectOutgoingVector( ray.thrd->RandGenerator(), ray.surfNorm ); // Find the probability of sampling that random vector float oneOverProb = 1.0f/brdf->GetProbabilityOfSampling( ray.surfNorm, randomVector ); // Compute N dot randVector float cosine = randomVector.Dot( ray.surfNorm ); // Create our random ray Ray bouncedRay( ray.thrd, hitPos, randomVector, ray.depth+1 ); bouncedRay.DisableCountingEmittedLight(); // Make sure we don't double count the light source. // Shoot our random ray, use Monte Carlo sampling (1/N * sum[ brdf(w_o,w_i)*L(x,w_i)*(N . w_i) / prob ] ) // Here we assume the summation and the 1/N is done by the image display function (or somewhere else). return directLightResult + (scene.TraceRay( bouncedRay ) * brdf->SampleBRDF( ray, viewVector, randomVector ) * cosine * oneOverProb); } BRDFMaterial::BRDFMaterial( FILE *f, Scene *s ) { // Setup default values, in case the scene file is defective... useExplicitDirectLighting = false; brdf = 0; // Search the scene file. char buf[ MAXLINELENGTH ], token[256], *ptr; while( fgets(buf, MAXLINELENGTH, f) != NULL ) { ptr = StripLeadingWhiteSpace( buf ); // Find the first token on the line if (ptr[0] == '#') continue; // Check if it's a comment ptr = StripLeadingTokenToBuffer( ptr, token ); // Grab the command to deal with MakeLower( token ); // Make sure it's consistantly lower case if (!strcmp(token,"end")) break; // Done reading the material? else if ( !strcmp(token,"explicit") ) // Should we sample the direct light explicitly? useExplicitDirectLighting = true; else if ( !strcmp(token, "lambertian") || !strcmp(token, "diffuse") ) brdf = new LambertianBRDF( f, s ); //else if (!strcmp(token,"ashikhminshirley") || !strcmp(token,"ashikhmin")) // brdf = new AshikhminShirleyBRDF( f, s ); else printf("Error: Unknown command '%s' when loading BRDFMaterial\n", token); } // Make sure we have a BRDF to sample! if (!brdf) { printf("Error: No BRDF specified for BRDFMaterial when reading from file!!\n"); exit(0); } }