////////////////////////////////////////////////////////////////////// // // Pixie // // Copyright © 1999 - 2003, Okan Arikan // // Contact: okan@cs.berkeley.edu // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public // License as published by the Free Software Foundation; either // version 2 of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // /////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////// // // File : photon.cpp // Classes : CPhotonHider // Description : // //////////////////////////////////////////////////////////////////////// #include #include "photon.h" #include "memory.h" #include "object.h" #include "random.h" #include "photonMap.h" #include "stats.h" #include "error.h" /////////////////////////////////////////////////////////////////////// // Class : CPhotonHider // Method : CPhotonHider // Description : Ctor // Return Value : - // Comments : // Date last edited : 3/7/2003 CPhotonHider::CPhotonHider(COptions *o,CXform *x,SOCKET s,CAttributes *a) : CShadingContext(o,x,s,HIDER_NEEDS_RAYTRACING | HIDER_NODISPLAY | HIDER_ILLUMINATIONHOOK | HIDER_PHOTONMAP_OVERWRITE) { bias = a->shadowBias; phony = new CSurface(a,x); phony->attach(); } /////////////////////////////////////////////////////////////////////// // Class : CPhotonHider // Method : ~CPhotonHider // Description : Dtor // Return Value : - // Comments : // Date last edited : 3/7/2003 CPhotonHider::~CPhotonHider() { CPhotonMap *cMap; // Balance the maps that have been modified while((cMap = balanceList.pop()) != NULL) { cMap->modifying = FALSE; cMap->write(world); } phony->detach(); } /////////////////////////////////////////////////////////////////////// // Class : CPhotonHider // Method : renderFrame // Description : Renders the frame // Return Value : - // Comments : // Date last edited : 3/7/2003 void CPhotonHider::renderFrame(){ memBegin(); const int numLights = allLights->numItems; CShaderInstance **lights = allLights->array; int i,j; vector tmp; float totalPower = 0; const char *previousActivity = stats.activity; stats.activity = "Photonmap construction"; // Compute the world bounding sphere addvv(worldCenter,worldBmin,worldBmax); mulvf(worldCenter,1 / (float) 2); subvv(tmp,worldBmax,worldCenter); worldRadius = lengthv(tmp); // The actual photon tracing stage stage = PHOTON_TRACE; for (i=0;ilights; CShadedLight **alights = ¤tShadingState->alights; CShadedLight **currentLight = ¤tShadingState->currentLight; CShadedLight **freeLights = ¤tShadingState->freeLights; int *tags = currentShadingState->tags; int emit; float *Clsave; // Figure out how much we want to emit emit = numEmitPhotons; if (emit > 0) { photonPower = 1 / (float) emit; while(emit > 0) { const int numVertices = min(maxGridSize,emit); currentShadingState->numVertices = numVertices; currentShadingState->numRealVertices = numVertices; currentShadingState->numUvertices = -1; currentShadingState->numVvertices = -1; currentShadingState->numActive = numVertices; currentShadingState->numPassive = 0; currentShadingState->shadingDim = SHADING_0D; currentShadingState->currentObject = phony; currentShadingState->lightCategory = 0; // Ensure there's space for the ambient lights *alights = (CShadedLight*) ralloc(sizeof(CShadedLight)); (*alights)->savedState = (float**) ralloc(2*sizeof(CShadedLight)); (*alights)->savedState[1] = (float*) ralloc(3*sizeof(float)*numVertices); (*alights)->savedState[0] = NULL; /* ambient lights do not use tags or save L */ (*alights)->lightTags = NULL; (*alights)->instance = NULL; (*alights)->next = NULL; // Reset the state *lights = NULL; // Lights : Cool *currentLight = NULL; // Light iterator : Cool *freeLights = NULL; // Free light lits : Cool Clsave = (*alights)->savedState[1]; for (j=0;jilluminate(this); memEnd(); emit -= numVertices; } } } stats.activity = previousActivity; memEnd(); } /////////////////////////////////////////////////////////////////////// // Class : CPhotonHider // Method : solarBegin // Description : Solar begin hook // Return Value : - // Comments : // Date last edited : 3/7/2003 void CPhotonHider::solarBegin(const float *L,const float *theta) { if (L == NULL) { // FIXME: Don't know how to handle this } else { float **varying = currentShadingState->varying; float *shaderL = varying[VARIABLE_L]; float *shaderPs = varying[VARIABLE_PS]; int numVertices = currentShadingState->numVertices; float xy[2]; vector cX; double r; double theta; vector X,Y; powerScale = (float) (C_PI*worldRadius*worldRadius); initv(Y,1,2,3); crossvv(X,L,Y); crossvv(Y,X,L); normalizev(X); normalizev(Y); mulvf(X,worldRadius); mulvf(Y,worldRadius); // Generate the solar samples for (;numVertices>0;numVertices--,shaderL+=3,shaderPs+=3) { gen2.get(xy); r = sqrtf(xy[0]); theta = xy[1]*2*C_PI; mulvf(cX,X,(float) (r*cos(theta))); mulvf(shaderPs,Y,(float) (r*sin(theta))); addvv(shaderPs,cX); addvv(shaderPs,worldCenter); normalizev(shaderL,L); } } } /////////////////////////////////////////////////////////////////////// // Class : CPhotonHider // Method : solarEnd // Description : Solar end hook // Return Value : - // Comments : // Date last edited : 3/7/2003 void CPhotonHider::solarEnd() { int numVertices = currentShadingState->numVertices; float **varying = currentShadingState->varying; if (stage == PHOTON_ESTIMATE) { // Estimation const float *Cl = varying[VARIABLE_CL]; // Solar lights are easy, just do the averaging for (;numVertices>0;numVertices--,Cl+=3) { const float power = (Cl[0] + Cl[1] + Cl[2]) / (float) 3; minPower = min(minPower,power); maxPower = max(maxPower,power); avgPower += power; numPower++; } } else { // Photon casting float *Ps = varying[VARIABLE_PS]; float *L = varying[VARIABLE_L]; float *Cl = varying[VARIABLE_CL]; int i; for (i=numVertices;i>0;i--,Ps+=3,L+=3,Cl+=3) { vector T; mulvf(T,L,worldRadius); subvv(Ps,T); mulvf(Cl,powerScale*photonPower); tracePhoton(Ps,L,Cl); } } } /////////////////////////////////////////////////////////////////////// // Class : CPhotonHider // Method : illuminateBegin // Description : Illuminate begin hook // Return Value : - // Comments : // Date last edited : 3/7/2003 void CPhotonHider::illuminateBegin(const float *P,const float *N,const float *theta) { float **varying = currentShadingState->varying; int numVertices = currentShadingState->numVertices; float *shaderPs = varying[VARIABLE_PS]; float *shaderL = varying[VARIABLE_L]; if (theta == NULL) { // We must be a point light source powerScale = (float) (4*C_PI); for (;numVertices>0;numVertices--,shaderPs+=3,shaderL+=3) { // Sample a direction in the unit sphere do { gen3.get(shaderL); shaderL[0] = 2*shaderL[0] - 1; shaderL[1] = 2*shaderL[1] - 1; shaderL[2] = 2*shaderL[2] - 1; } while(dotvv(shaderL,shaderL) > 1); // Compute the point being shaded normalizev(shaderL); addvv(shaderPs,P,shaderL); } } else { // We must be an area light source powerScale = (float) (2*C_PI); assert(N != NULL); if (theta == NULL) { // FIXME: Don't know how to handle this } else { for (;numVertices>0;numVertices--,shaderPs+=3,shaderL+=3) { // Sample a direction in the unit sphere sampleHemisphere(shaderL,N,theta[0],gen4); // Compute the point being shaded normalizev(shaderL); addvv(shaderPs,P,shaderL); } } } } /////////////////////////////////////////////////////////////////////// // Class : CPhotonHider // Method : illuminateEnd // Description : Illuminate end hook // Return Value : - // Comments : // Date last edited : 3/7/2003 void CPhotonHider::illuminateEnd() { int numVertices = currentShadingState->numVertices; float **varying = currentShadingState->varying; if (stage == PHOTON_ESTIMATE) { // Estimation const float *Cl = varying[VARIABLE_CL]; const float *Ps = varying[VARIABLE_PS]; const float *L = varying[VARIABLE_L]; CRay ray; for (;numVertices>0;numVertices--,Cl+=3,L+=3,Ps+=3) { movvv(ray.dir,L); subvv(ray.from,Ps,L); movvv(ray.to,Ps); ray.flags = ATTRIBUTES_FLAGS_PRIMARY_VISIBLE; ray.tmin = bias; ray.invDir[COMP_X] = 1/ray.dir[COMP_X]; ray.invDir[COMP_Y] = 1/ray.dir[COMP_Y]; ray.invDir[COMP_Z] = 1/ray.dir[COMP_Z]; ray.t = C_INFINITY; ray.time = 0; ray.lastXform = NULL; ray.object = NULL; hierarchy->intersect(&ray); if (ray.object != NULL) { const float power = (Cl[0] + Cl[1] + Cl[2]) / (3*ray.t*ray.t); minPower = min(minPower,power); maxPower = max(maxPower,power); avgPower += power; numPower++; } } } else { float *Ps = varying[VARIABLE_PS]; float *L = varying[VARIABLE_L]; float *Cl = varying[VARIABLE_CL]; int i; for (i=numVertices;i>0;i--,Ps+=3,L+=3,Cl+=3) { subvv(Ps,L); mulvf(Cl,powerScale*photonPower); tracePhoton(Ps,L,Cl); } } } /////////////////////////////////////////////////////////////////////// // Class : CPhotonHider // Method : tracePhotons // Description : Trace the photons generated by a light source // Return Value : - // Comments : // Date last edited : 3/11/2003 void CPhotonHider::tracePhoton(float *P,float *L,float *C) { CRay ray; vector Cl,Pl,Nl; CAttributes *attributes; const float *surfaceColor; int numDiffuseBounces,numSpecularBounces,lastBounceSpecular; ray.flags = ATTRIBUTES_FLAGS_PRIMARY_VISIBLE; // The L must be unit vector assert((dotvv(L,L) - 1)*(dotvv(L,L) - 1) < 0.00001); //if (dotvv(C,C) < EPSILON) return; movvv(ray.from,P); movvv(ray.dir,L); addvv(ray.to,P,L); movvv(Cl,C); // The maximum number of diffuse and specular bounces numDiffuseBounces = 0; numSpecularBounces = 0; lastBounceSpecular = FALSE; // The intersection bias ray.tmin = bias; processBounce:; // Set misc variables ray.invDir[COMP_X] = 1/ray.dir[COMP_X]; ray.invDir[COMP_Y] = 1/ray.dir[COMP_Y]; ray.invDir[COMP_Z] = 1/ray.dir[COMP_Z]; ray.t = C_INFINITY; ray.time = 0; ray.lastXform = NULL; ray.object = NULL; // Trace the ray in the scene hierarchy->intersect(&ray); // Do we have an intersection ? if (ray.object != NULL) { attributes = ray.object->attributes; surfaceColor = attributes->surfaceColor; if (attributes->flags & ATTRIBUTES_FLAGS_ILLUMINATE_FRONT_ONLY) { if (dotvv(ray.N,ray.dir) > 0) return; } mulvf(Pl,ray.dir,ray.t); addvv(Pl,ray.from); // Process this hit switch(attributes->shadingModel) { // Matte surface case SM_MATTE: { CPhotonMap *globalMap; CPhotonMap *causticMap; // Compute the normalized normal vector at the intersection normalizev(Nl,ray.N); if (dotvv(ray.dir,Nl) > 0) { mulvf(Nl,-1); } // Save the photon if ((globalMap=attributes->globalMap) != NULL) { if (globalMap->modifying == FALSE) { globalMap->modifying = TRUE; globalMap->reset(); balanceList.push(globalMap); } globalMap->store(Pl,Nl,ray.dir,Cl); } if ((causticMap=attributes->causticMap) != NULL) { if (lastBounceSpecular) { if (causticMap->modifying == FALSE) { causticMap->modifying = TRUE; causticMap->reset(); balanceList.push(causticMap); } causticMap->store(Pl,Nl,ray.dir,Cl); return; } } // Check if we hit the maximum number of bounces if (numDiffuseBounces >= attributes->maxDiffuseDepth) return; numDiffuseBounces++; // Sample the reflection direction sampleCosineHemisphere(ray.dir,Nl,(float) (C_PI / 2.0),gen4); assert(dotvv(Nl,ray.dir) > 0); normalizev(ray.dir); // Bounce the photon mulvv(Cl,surfaceColor); // Process the current hit movvv(ray.from,Pl); addvv(ray.to,ray.from,ray.dir); ray.tmin = attributes->shadowBias; lastBounceSpecular = FALSE; goto processBounce; } break; case SM_TRANSLUCENT: break; case SM_CHROME: { vector Ll; // Check if we hit the maximum number of bounces if (numSpecularBounces >= attributes->maxSpecularDepth) return; numSpecularBounces++; // Sample the reflection direction normalizev(Nl,ray.N); if (dotvv(ray.dir,Nl) > 0) { mulvf(Nl,-1); } movvv(Ll,ray.dir); reflect(ray.dir,Ll,Nl); // Bounce the photon movvv(ray.from,Pl); addvv(ray.to,ray.from,ray.dir); ray.tmin = attributes->shadowBias; lastBounceSpecular = TRUE; goto processBounce; } break; case SM_GLASS: { float eta; float Kr,Kt; vector R,T; // Check if we hit the maximum number of bounces if (numSpecularBounces >= attributes->maxSpecularDepth) return; numSpecularBounces++; // Sample the reflection direction normalizev(Nl,ray.N); if (dotvv(ray.dir,Nl) > 0) { mulvf(Nl,-1); eta = 1 / (float) 1.5; } else { eta = (float) 1.5; } assert(dotvv(ray.dir,Nl) < 0); fresnel(ray.dir,Nl,eta,Kr,Kt,R,T); // Decide on reflection or refraction if (urand() <= Kr) { assert(dotvv(R,R) > 0); normalizev(ray.dir,R); } else { assert(dotvv(T,T) > 0); normalizev(ray.dir,T); } // Bounce the photon movvv(ray.from,Pl); addvv(ray.to,ray.from,ray.dir); ray.tmin = attributes->shadowBias; lastBounceSpecular = TRUE; goto processBounce; } break; case SM_WATER: { float eta; float Kr,Kt; vector R,T; // Check if we hit the maximum number of bounces if (numSpecularBounces >= attributes->maxSpecularDepth) return; numSpecularBounces++; // Sample the reflection direction normalizev(Nl,ray.N); if (dotvv(ray.dir,Nl) > 0) { mulvf(Nl,-1); eta = 1 / (float) 1.3333333; } else { eta = (float) 1.3333333; } assert(dotvv(ray.dir,Nl) < 0); fresnel(ray.dir,Nl,eta,Kr,Kt,R,T); // Decide on reflection or refraction if (urand() <= Kr) { assert(dotvv(R,R) > 0); normalizev(ray.dir,R); } else { assert(dotvv(T,T) > 0); normalizev(ray.dir,T); } // Bounce the photon movvv(ray.from,Pl); addvv(ray.to,ray.from,ray.dir); ray.tmin = attributes->shadowBias; lastBounceSpecular = TRUE; goto processBounce; } break; case SM_DIELECTRIC: { float eta; float Kr,Kt; vector R,T; // Check if we hit the maximum number of bounces if (numSpecularBounces >= attributes->maxSpecularDepth) return; numSpecularBounces++; // Sample the reflection direction normalizev(Nl,ray.N); if (dotvv(ray.dir,Nl) > 0) { mulvf(Nl,-1); eta = 1 / attributes->photonIor; } else { eta = attributes->photonIor; } assert(dotvv(ray.dir,Nl) < 0); fresnel(ray.dir,Nl,eta,Kr,Kt,R,T); // Decide on reflection or refraction if (urand() <= Kr) { assert(dotvv(R,R) > 0); normalizev(ray.dir,R); } else { assert(dotvv(T,T) > 0); normalizev(ray.dir,T); } // Bounce the photon movvv(ray.from,Pl); addvv(ray.to,ray.from,ray.dir); ray.tmin = attributes->shadowBias; lastBounceSpecular = TRUE; goto processBounce; } break; case SM_TRANSPARENT: { vector tmp; // Just keep on tracing the ray mulvf(tmp,ray.dir,ray.t); addvv(ray.from,tmp); Cl[0] *= (1-attributes->surfaceOpacity[0]); Cl[1] *= (1-attributes->surfaceOpacity[1]); Cl[2] *= (1-attributes->surfaceOpacity[2]); } break; } } }