int OOC_FilterPhoton (void *p, void *fd) /* Filter callback for photon kNN search, used by OOC_FindNearest() */ { const Photon *photon = p; const OOC_FilterData *filtData = fd; const PhotonMap *pmap = filtData -> pmap; /* Reject photon if normal faces away (ignored for volume photons) with * tolerance to account for perturbation; note photon normal is coded * in range [-127,127], hence we factor this in */ if (filtData -> norm && DOT(filtData->norm, photon->norm) <= PMAP_NORM_TOL * 127 * frandom()) return 0; if (isContribPmap(pmap)) { /* Lookup in contribution photon map; filter according to emitting * light source if contrib list set, else accept all */ if (pmap -> srcContrib) { OBJREC *srcMod; const int srcIdx = photonSrcIdx(pmap, photon); if (srcIdx < 0 || srcIdx >= nsources) error(INTERNAL, "invalid light source index in photon map"); srcMod = findmaterial(source [srcIdx].so); /* Reject photon if contributions from light source which emitted * it are not sought */ if (!lu_find(pmap -> srcContrib, srcMod -> oname) -> data) return 0; } /* Reject non-caustic photon if lookup for caustic contribs */ if (pmap -> lookupCaustic && !photon -> caustic) return 0; } /* Accept photon */ return 1; }
void buildPhotonMap (PhotonMap *pmap, double *photonFlux, PhotonPrimaryIdx *primaryOfs, unsigned nproc) { unsigned long n, nCheck = 0; unsigned i; Photon *p; COLOR flux; char nuHeapFname [sizeof(PMAP_TMPFNAME)]; FILE *nuHeap; /* Need double here to reduce summation errors */ double avgFlux [3] = {0, 0, 0}, CoG [3] = {0, 0, 0}, CoGdist = 0; FVECT d; if (!pmap) error(INTERNAL, "undefined photon map in buildPhotonMap"); /* Get number of photons from heapfile size */ if (fseek(pmap -> heap, 0, SEEK_END) < 0) error(SYSTEM, "failed seek to end of photon heap in buildPhotonMap"); pmap -> numPhotons = ftell(pmap -> heap) / sizeof(Photon); if (!pmap -> numPhotons) error(INTERNAL, "empty photon map in buildPhotonMap"); if (!pmap -> heap) error(INTERNAL, "no heap in buildPhotonMap"); #ifdef DEBUG_PMAP eputs("Checking photon heap consistency...\n"); checkPhotonHeap(pmap -> heap); sprintf(errmsg, "Heap contains %ld photons\n", pmap -> numPhotons); eputs(errmsg); #endif /* Allocate heap buffa */ if (!pmap -> heapBuf) { pmap -> heapBufSize = PMAP_HEAPBUFSIZE; pmap -> heapBuf = calloc(pmap -> heapBufSize, sizeof(Photon)); if (!pmap -> heapBuf) error(SYSTEM, "failed to allocate postprocessed photon heap in" "buildPhotonMap"); } /* We REALLY don't need yet another @%&*! heap just to hold the scaled * photons, but can't think of a quicker fix... */ mktemp(strcpy(nuHeapFname, PMAP_TMPFNAME)); if (!(nuHeap = fopen(nuHeapFname, "w+b"))) error(SYSTEM, "failed to open postprocessed photon heap in " "buildPhotonMap"); rewind(pmap -> heap); #ifdef DEBUG_PMAP eputs("Postprocessing photons...\n"); #endif while (!feof(pmap -> heap)) { #ifdef DEBUG_PMAP printf("Reading %lu at %lu... ", pmap -> heapBufSize, ftell(pmap->heap)); #endif pmap -> heapBufLen = fread(pmap -> heapBuf, sizeof(Photon), pmap -> heapBufSize, pmap -> heap); #ifdef DEBUG_PMAP printf("Got %lu\n", pmap -> heapBufLen); #endif if (ferror(pmap -> heap)) error(SYSTEM, "failed to read photon heap in buildPhotonMap"); for (n = pmap -> heapBufLen, p = pmap -> heapBuf; n; n--, p++) { /* Update min and max pos and set photon flux */ for (i = 0; i <= 2; i++) { if (p -> pos [i] < pmap -> minPos [i]) pmap -> minPos [i] = p -> pos [i]; else if (p -> pos [i] > pmap -> maxPos [i]) pmap -> maxPos [i] = p -> pos [i]; /* Update centre of gravity with photon position */ CoG [i] += p -> pos [i]; } if (primaryOfs) /* Linearise photon primary index from subprocess index using the * per-subprocess offsets in primaryOfs */ p -> primary += primaryOfs [p -> proc]; /* Scale photon's flux (hitherto normalised to 1 over RGB); in * case of a contrib photon map, this is done per light source, * and photonFlux is assumed to be an array */ getPhotonFlux(p, flux); if (photonFlux) { scalecolor(flux, photonFlux [isContribPmap(pmap) ? photonSrcIdx(pmap, p) : 0]); setPhotonFlux(p, flux); } /* Update average photon flux; need a double here */ addcolor(avgFlux, flux); } /* Write modified photons to new heap */ fwrite(pmap -> heapBuf, sizeof(Photon), pmap -> heapBufLen, nuHeap); if (ferror(nuHeap)) error(SYSTEM, "failed postprocessing photon flux in " "buildPhotonMap"); nCheck += pmap -> heapBufLen; } #ifdef DEBUG_PMAP if (nCheck < pmap -> numPhotons) error(INTERNAL, "truncated photon heap in buildPhotonMap"); #endif /* Finalise average photon flux */ scalecolor(avgFlux, 1.0 / pmap -> numPhotons); copycolor(pmap -> photonFlux, avgFlux); /* Average photon positions to get centre of gravity */ for (i = 0; i < 3; i++) pmap -> CoG [i] = CoG [i] /= pmap -> numPhotons; rewind(pmap -> heap); /* Compute average photon distance to centre of gravity */ while (!feof(pmap -> heap)) { pmap -> heapBufLen = fread(pmap -> heapBuf, sizeof(Photon), pmap -> heapBufSize, pmap -> heap); for (n = pmap -> heapBufLen, p = pmap -> heapBuf; n; n--, p++) { VSUB(d, p -> pos, CoG); CoGdist += DOT(d, d); } } pmap -> CoGdist = CoGdist /= pmap -> numPhotons; /* Swap heaps, discarding unscaled photons */ fclose(pmap -> heap); unlink(pmap -> heapFname); pmap -> heap = nuHeap; strcpy(pmap -> heapFname, nuHeapFname); #ifdef PMAP_OOC OOC_BuildPhotonMap(pmap, nproc); #else kdT_BuildPhotonMap(pmap); #endif /* Trash heap and its buffa */ free(pmap -> heapBuf); fclose(pmap -> heap); unlink(pmap -> heapFname); pmap -> heap = NULL; pmap -> heapBuf = NULL; }
int newPhoton (PhotonMap* pmap, const RAY* ray) { unsigned i; Photon photon; COLOR photonFlux; /* Account for distribution ratio */ if (!pmap || pmapRandom(pmap -> randState) > pmap -> distribRatio) return -1; /* Don't store on sources */ if (ray -> robj > -1 && islight(objptr(ray -> ro -> omod) -> otype)) return -1; /* if modifier in include/exclude set */ if (ambincl != -1 && ray -> ro && ambincl != inset(ambset, ray -> ro -> omod)) return -1; if (pmapNumROI && pmapROI) { unsigned inROI = 0; /* Store photon if within a region of interest (for ze Ecksperts!) */ for (i = 0; !inROI && i < pmapNumROI; i++) inROI = (ray -> rop [0] >= pmapROI [i].min [0] && ray -> rop [0] <= pmapROI [i].max [0] && ray -> rop [1] >= pmapROI [i].min [1] && ray -> rop [1] <= pmapROI [i].max [1] && ray -> rop [2] >= pmapROI [i].min [2] && ray -> rop [2] <= pmapROI [i].max [2]); if (!inROI) return -1; } /* Adjust flux according to distribution ratio and ray weight */ copycolor(photonFlux, ray -> rcol); scalecolor(photonFlux, ray -> rweight / (pmap -> distribRatio ? pmap -> distribRatio : 1)); setPhotonFlux(&photon, photonFlux); /* Set photon position and flags */ VCOPY(photon.pos, ray -> rop); photon.flags = 0; photon.caustic = PMAP_CAUSTICRAY(ray); /* Set contrib photon's primary ray and subprocess index (the latter * to linearise the primary ray indices after photon distribution is * complete). Also set primary ray's source index, thereby marking it * as used. */ if (isContribPmap(pmap)) { photon.primary = pmap -> numPrimary; photon.proc = PMAP_GETRAYPROC(ray); pmap -> lastPrimary.srcIdx = ray -> rsrc; } else photon.primary = 0; /* Set normal */ for (i = 0; i <= 2; i++) photon.norm [i] = 127.0 * (isVolumePmap(pmap) ? ray -> rdir [i] : ray -> ron [i]); if (!pmap -> heapBuf) { /* Lazily allocate heap buffa */ #if NIX /* Randomise buffa size to temporally decorellate flushes in * multiprocessing mode */ srandom(randSeed + getpid()); pmap -> heapBufSize = PMAP_HEAPBUFSIZE * (0.5 + frandom()); #else /* Randomisation disabled for single processes on WIN; also useful * for reproducability during debugging */ pmap -> heapBufSize = PMAP_HEAPBUFSIZE; #endif if (!(pmap -> heapBuf = calloc(pmap -> heapBufSize, sizeof(Photon)))) error(SYSTEM, "failed heap buffer allocation in newPhoton"); pmap -> heapBufLen = 0; } /* Photon initialised; now append to heap buffa */ memcpy(pmap -> heapBuf + pmap -> heapBufLen, &photon, sizeof(Photon)); if (++pmap -> heapBufLen >= pmap -> heapBufSize) /* Heap buffa full, flush to heap file */ flushPhotonHeap(pmap); pmap -> numPhotons++; return 0; }