static void tcoord_to_pcoord(struct vec3 * pcoord, float x, float y, mat4x4 * M) { vec4 tc, pc; tc.x = x; tc.y = y; tc.z = 1.0; tc.w = 0.0; mulmv(&pc, M, &tc); pcoord->x = pc.x; pcoord->y = pc.y; pcoord->z = pc.z; TRACE(OPENGL, "tcoord (%f, %f) to pcoord (%f, %f, %f)\n", x, y, pcoord->x, pcoord->y, pcoord->z); }
/////////////////////////////////////////////////////////////////////// // Class : CIrradianceCache // Method : sample // Description : Sample the occlusion // Return Value : // Comments : void CIrradianceCache::sample(float *C,const float *P,const float *dPdu,const float *dPdv,const float *N,CShadingContext *context) { CCacheSample *cSample; int i,j; float coverage; vector irradiance; vector envdir; float rMean; CRay ray; vector X,Y; CCacheNode *cNode; int depth; // Allocate memory const CShadingScratch *scratch = &(context->currentShadingState->scratch); const int nt = (int) (sqrtf(scratch->traceParams.samples / (float) C_PI) + 0.5); const int np = (int) (C_PI*nt + 0.5); const int numSamples = nt*np; CHemisphereSample *hemisphere = (CHemisphereSample *) alloca(numSamples*sizeof(CHemisphereSample)); // initialize texture lookups if needed if (scratch->occlusionParams.environment) { CTextureLookup::staticInit(&(context->currentShadingState->scratch)); } // Create an orthanormal coordinate system if (dotvv(dPdu,dPdu) > 0) { normalizevf(X,dPdu); crossvv(Y,N,X); } else if (dotvv(dPdv,dPdv) > 0) { normalizevf(X,dPdv); crossvv(Y,N,X); } else { // At this point, we're pretty screwed, so why not use the P normalizevf(X,P); crossvv(Y,N,X); } // Sample the hemisphere coverage = 0; initv(irradiance,0); initv(envdir,0); rMean = C_INFINITY; // Calculate the ray differentials (use average spread in theta and phi) const float da = tanf((float) C_PI/(2*(nt+np))); const float db = (lengthv(dPdu) + lengthv(dPdv))*0.5f; if (scratch->occlusionParams.occlusion == TRUE) { // We're shading for occlusion context->numOcclusionRays += numSamples; context->numOcclusionSamples++; for (i=0;i<nt;i++) { for (j=0;j<np;j++,hemisphere++) { float rv[2]; context->random2d.get(rv); float tmp = sqrtf((i+context->urand()) / (float) nt); const float phi = (float) (2*C_PI*(j+context->urand()) / (float) np); const float cosPhi = (cosf(phi)*tmp); const float sinPhi = (sinf(phi)*tmp); tmp = sqrtf(1 - tmp*tmp); ray.dir[0] = X[0]*cosPhi + Y[0]*sinPhi + N[0]*tmp; ray.dir[1] = X[1]*cosPhi + Y[1]*sinPhi + N[1]*tmp; ray.dir[2] = X[2]*cosPhi + Y[2]*sinPhi + N[2]*tmp; const float originJitterX = (rv[0] - 0.5f)*scratch->traceParams.sampleBase; const float originJitterY = (rv[1] - 0.5f)*scratch->traceParams.sampleBase; ray.from[COMP_X] = P[COMP_X] + originJitterX*dPdu[0] + originJitterY*dPdv[0]; ray.from[COMP_Y] = P[COMP_Y] + originJitterX*dPdu[1] + originJitterY*dPdv[1]; ray.from[COMP_Z] = P[COMP_Z] + originJitterX*dPdu[2] + originJitterY*dPdv[2]; ray.flags = ATTRIBUTES_FLAGS_DIFFUSE_VISIBLE; ray.tmin = scratch->traceParams.bias; ray.t = scratch->traceParams.maxDist; ray.time = 0; ray.da = da; ray.db = db; // Transform the ray into the right coordinate system mulmp(ray.from,from,ray.from); mulmv(ray.dir,from,ray.dir); context->trace(&ray); // Do we have an intersection ? if (ray.object != NULL) { const float *color = ray.object->attributes->surfaceColor; // Yes coverage++; addvv(irradiance,color); hemisphere->coverage = 1; initv(hemisphere->envdir,0); movvv(hemisphere->irradiance,color); } else { // No hemisphere->coverage = 0; addvv(envdir,ray.dir); movvv(hemisphere->envdir,ray.dir); // GSH : Texture lookup for misses if(scratch->occlusionParams.environment != NULL){ CEnvironment *tex = scratch->occlusionParams.environment; vector D0,D1,D2,D3; vector color; // GSHTODO: Add in the dCosPhi and dSinPhi movvv(D0,ray.dir); movvv(D1,ray.dir); movvv(D2,ray.dir); movvv(D3,ray.dir); float savedSamples = scratch->traceParams.samples; context->currentShadingState->scratch.traceParams.samples = 1; tex->lookup(color,D0,D1,D2,D3,context); context->currentShadingState->scratch.traceParams.samples = savedSamples; addvv(irradiance,color); movvv(hemisphere->irradiance,color); } else{ initv(hemisphere->irradiance,0); } } hemisphere->depth = ray.t; hemisphere->invDepth = 1 / ray.t; if (tmp > horizonCutoff) rMean = min(rMean,ray.t); movvv(hemisphere->dir,ray.dir); assert(hemisphere->invDepth > 0); } } } else { // We're shading for indirectdiffuse context->numIndirectDiffuseRays += numSamples; context->numIndirectDiffuseSamples++; for (i=0;i<nt;i++) { for (j=0;j<np;j++,hemisphere++) { float rv[2]; context->random2d.get(rv); float tmp = sqrtf((i+context->urand()) / (float) nt); const float phi = (float) (2*C_PI*(j+context->urand()) / (float) np); const float cosPhi = (cosf(phi)*tmp); const float sinPhi = (sinf(phi)*tmp); tmp = sqrtf(1 - tmp*tmp); ray.dir[0] = X[0]*cosPhi + Y[0]*sinPhi + N[0]*tmp; ray.dir[1] = X[1]*cosPhi + Y[1]*sinPhi + N[1]*tmp; ray.dir[2] = X[2]*cosPhi + Y[2]*sinPhi + N[2]*tmp; const float originJitterX = (rv[0] - 0.5f)*scratch->traceParams.sampleBase; const float originJitterY = (rv[1] - 0.5f)*scratch->traceParams.sampleBase; ray.from[COMP_X] = P[COMP_X] + originJitterX*dPdu[0] + originJitterY*dPdv[0]; ray.from[COMP_Y] = P[COMP_Y] + originJitterX*dPdu[1] + originJitterY*dPdv[1]; ray.from[COMP_Z] = P[COMP_Z] + originJitterX*dPdu[2] + originJitterY*dPdv[2]; ray.flags = ATTRIBUTES_FLAGS_DIFFUSE_VISIBLE; ray.tmin = scratch->traceParams.bias; ray.t = scratch->traceParams.maxDist; ray.time = 0; ray.da = da; ray.db = db; // Transform the ray into the right coordinate system mulmp(ray.from,from,ray.from); mulmv(ray.dir,from,ray.dir); context->trace(&ray); // Do we have an intersection ? if (ray.object != NULL) { vector P,N,C; CAttributes *attributes = ray.object->attributes; CPhotonMap *globalMap; if ((globalMap = attributes->globalMap) != NULL) { normalizev(N,ray.N); mulvf(P,ray.dir,ray.t); addvv(P,ray.from); if(dotvv(ray.dir,N) > 0) mulvf(N,-1); globalMap->lookup(C,P,N,attributes->photonEstimator); // HACK: Avoid too bright spots tmp = max(max(C[0],C[1]),C[2]); if (tmp > scratch->occlusionParams.maxBrightness) mulvf(C,scratch->occlusionParams.maxBrightness/tmp); mulvv(C,attributes->surfaceColor); addvv(irradiance,C); movvv(hemisphere->irradiance,C); context->numIndirectDiffusePhotonmapLookups++; } else { initv(hemisphere->irradiance,0); } // Yes coverage++; hemisphere->coverage = 1; initv(hemisphere->envdir,0); } else { // No hemisphere->coverage = 0; addvv(envdir,ray.dir); movvv(hemisphere->envdir,ray.dir); // GSH : Texture lookup for misses if(scratch->occlusionParams.environment != NULL){ CEnvironment *tex = scratch->occlusionParams.environment; vector D0,D1,D2,D3; vector color; // GSHTODO: Add in the dCosPhi and dSinPhi movvv(D0,ray.dir); movvv(D1,ray.dir); movvv(D2,ray.dir); movvv(D3,ray.dir); float savedSamples = scratch->traceParams.samples; context->currentShadingState->scratch.traceParams.samples = 1; tex->lookup(color,D0,D1,D2,D3,context); context->currentShadingState->scratch.traceParams.samples = savedSamples; addvv(irradiance,color); movvv(hemisphere->irradiance,color); } else{ movvv(hemisphere->irradiance,scratch->occlusionParams.environmentColor); addvv(irradiance,scratch->occlusionParams.environmentColor); } } hemisphere->depth = ray.t; hemisphere->invDepth = 1 / ray.t; if (tmp > horizonCutoff) rMean = min(rMean,ray.t); movvv(hemisphere->dir,ray.dir); assert(hemisphere->invDepth > 0); } } } hemisphere -= np*nt; // Normalize const float tmp = 1 / (float) numSamples; coverage *= tmp; mulvf(irradiance,tmp); normalizevf(envdir); // Record the value C[0] = irradiance[0]; C[1] = irradiance[1]; C[2] = irradiance[2]; C[3] = coverage; C[4] = envdir[0]; C[5] = envdir[1]; C[6] = envdir[2]; // Should we save it ? if ((scratch->occlusionParams.maxError != 0) && (coverage < 1-C_EPSILON)) { // We're modifying, lock the thing osLock(mutex); // Create the sample cSample = (CCacheSample *) memory->alloc(sizeof(CCacheSample)); // Compute the gradients of the illumination posGradient(cSample->gP,np,nt,hemisphere,X,Y); rotGradient(cSample->gR,np,nt,hemisphere,X,Y); // Compute the radius of validity rMean *= 0.5f; // Clamp the radius of validity rMean = min(rMean,db*scratch->occlusionParams.maxPixelDist); // Record the data (in the target coordinate system) movvv(cSample->P,P); movvv(cSample->N,N); cSample->dP = rMean; cSample->coverage = coverage; movvv(cSample->envdir,envdir); movvv(cSample->irradiance,irradiance); // Do the neighbour clamping trick clamp(cSample); rMean = cSample->dP; // copy dP back so we get the right place in the octree // The error multiplier const float K = 0.4f / scratch->occlusionParams.maxError; rMean /= K; // Insert the new sample into the cache cNode = root; depth = 0; while(cNode->side > (2*rMean)) { depth++; for (j=0,i=0;i<3;i++) { if (P[i] > cNode->center[i]) { j |= 1 << i; } } if (cNode->children[j] == NULL) { CCacheNode *nNode = (CCacheNode *) memory->alloc(sizeof(CCacheNode)); for (i=0;i<3;i++) { if (P[i] > cNode->center[i]) { nNode->center[i] = cNode->center[i] + cNode->side*0.25f; } else { nNode->center[i] = cNode->center[i] - cNode->side*0.25f; } } cNode->children[j] = nNode; nNode->side = cNode->side*0.5f; nNode->samples = NULL; for (i=0;i<8;i++) nNode->children[i] = NULL; } cNode = cNode->children[j]; } cSample->next = cNode->samples; cNode->samples = cSample; maxDepth = max(depth,maxDepth); osUnlock(mutex); } }
/////////////////////////////////////////////////////////////////////// // Class : CIrradianceCache // Method : lookup // Description : Lookup da cache // Return Value : // Comments : void CIrradianceCache::lookup(float *C,const float *cP,const float *cdPdu,const float *cdPdv,const float *cN,CShadingContext *context) { const CShadingScratch *scratch = &(context->currentShadingState->scratch); // Is this a point based lookup? if ((scratch->occlusionParams.pointbased) && (scratch->occlusionParams.pointHierarchy != NULL)) { int i; for (i=0;i<7;i++) C[i] = 0; scratch->occlusionParams.pointHierarchy->lookup(C,cP,cdPdu,cdPdv,cN,context); } else { CCacheSample *cSample; CCacheNode *cNode; float totalWeight = 0; CCacheNode **stackBase = (CCacheNode **) alloca(maxDepth*sizeof(CCacheNode *)*8); CCacheNode **stack; int i; float coverage; vector irradiance,envdir; vector P,N; // A small value for discard-smoothing of irradiance const float smallSampleWeight = (flags & CACHE_SAMPLE) ? 0.1f : 0.0f; // Transform the lookup point to the correct coordinate system mulmp(P,to,cP); mulmn(N,from,cN); // Init the result coverage = 0; initv(irradiance,0); initv(envdir,0); // The weighting algorithm is that described in [Tabellion and Lamorlette 2004] // We need to convert the max error as in Wald to Tabellion // The default value of maxError is 0.4f const float K = 0.4f / scratch->occlusionParams.maxError; // Note, we do not need to lock the data for reading // if word-writes are atomic // Prepare for the non recursive tree traversal stack = stackBase; *stack++ = root; while(stack > stackBase) { cNode = *(--stack); // Sum the values in this level for (cSample=cNode->samples;cSample!=NULL;cSample=cSample->next) { vector D; // D = vector from sample to query point subvv(D,P,cSample->P); // Ignore sample in the front float a = dotvv(D,cSample->N); if ((a*a / (dotvv(D,D) + C_EPSILON)) > 0.1) continue; // Positional error float e1 = sqrtf(dotvv(D,D)) / cSample->dP; // Directional error float e2 = 1 - dotvv(N,cSample->N); if (e2 < 0) e2 = 0; e2 = sqrtf(e2*weightNormalDenominator); // Compute the weight float w = 1 - K*max(e1,e2); if (w > context->urand()*smallSampleWeight) { vector ntmp; crossvv(ntmp,cSample->N,N); // Sum the sample totalWeight += w; coverage += w*(cSample->coverage + dotvv(cSample->gP+0*3,D) + dotvv(cSample->gR+0*3,ntmp)); irradiance[0] += w*(cSample->irradiance[0] + dotvv(cSample->gP+1*3,D) + dotvv(cSample->gR+1*3,ntmp)); irradiance[1] += w*(cSample->irradiance[1] + dotvv(cSample->gP+2*3,D) + dotvv(cSample->gR+2*3,ntmp)); irradiance[2] += w*(cSample->irradiance[2] + dotvv(cSample->gP+3*3,D) + dotvv(cSample->gR+3*3,ntmp)); envdir[0] += w*(cSample->envdir[0] + dotvv(cSample->gP+4*3,D) + dotvv(cSample->gR+4*3,ntmp)); envdir[1] += w*(cSample->envdir[1] + dotvv(cSample->gP+5*3,D) + dotvv(cSample->gR+5*3,ntmp)); envdir[2] += w*(cSample->envdir[2] + dotvv(cSample->gP+6*3,D) + dotvv(cSample->gR+6*3,ntmp)); } } // Check the children for (i=0;i<8;i++) { CCacheNode *tNode; if ((tNode = cNode->children[i]) != NULL) { const float tSide = tNode->side; if ( ((tNode->center[0] + tSide) > P[0]) && ((tNode->center[1] + tSide) > P[1]) && ((tNode->center[2] + tSide) > P[2]) && ((tNode->center[0] - tSide) < P[0]) && ((tNode->center[1] - tSide) < P[1]) && ((tNode->center[2] - tSide) < P[2])) { *stack++ = tNode; } } } } // Do we have anything ? if (totalWeight > C_EPSILON) { double normalizer = 1 / totalWeight; normalizevf(envdir); C[0] = (float) (irradiance[0]*normalizer); C[1] = (float) (irradiance[1]*normalizer); C[2] = (float) (irradiance[2]*normalizer); C[3] = (float) (coverage*normalizer); mulmv(C+4,from,envdir); // envdir is stored in the target coordinate system } else { // Are we sampling the cache ? if (flags & CACHE_SAMPLE) { vector dPdu,dPdv; // Convert the tangent space mulmv(dPdu,to,cdPdu); mulmv(dPdv,to,cdPdv); // Create a new sample sample(C,P,dPdu,dPdv,N,context); mulmv(C+4,from,C+4); // envdir is stored in the target coordinate system } else { // No joy C[0] = 0; C[1] = 0; C[2] = 0; C[3] = 1; C[4] = 0; C[5] = 0; C[6] = 0; } } // Make sure we don't have NaNs assert(dotvv(C,C) >= 0); } }