ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/rt/pmapcontrib.c
Revision: 2.9
Committed: Tue Aug 18 18:45:55 2015 UTC (8 years, 9 months ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 2.8: +4 -1 lines
Log Message:
Added missing RCSid forgotten during initial check-in

File Contents

# Content
1 #ifndef lint
2 static const char RCSid[] = "$Id$";
3 #endif
4 /*
5 ==================================================================
6 Photon map support for light source contributions
7
8 Roland Schregle (roland.schregle@{hslu.ch, gmail.com})
9 (c) Lucerne University of Applied Sciences and Arts,
10 supported by the Swiss National Science Foundation (SNSF, #147053)
11 ==================================================================
12
13 $Id: pmapcontrib.c,v 2.8 2015/05/21 13:54:59 greg Exp $
14 */
15
16
17 #include "pmapcontrib.h"
18 #include "pmap.h"
19 #include "pmapmat.h"
20 #include "pmapsrc.h"
21 #include "pmaprand.h"
22 #include "pmapio.h"
23 #include "pmapdiag.h"
24 #include "rcontrib.h"
25 #include "otypes.h"
26
27
28
29 static void setPmapContribParams (PhotonMap *pmap, LUTAB *srcContrib)
30 /* Set parameters for light source contributions */
31 {
32 /* Set light source modifier list and appropriate callback to extract
33 * their contributions from the photon map */
34 if (pmap) {
35 pmap -> srcContrib = srcContrib;
36 pmap -> lookup = photonContrib;
37 /* Ensure we get all requested photon contribs during lookups */
38 pmap -> gatherTolerance = 1.0;
39 }
40 }
41
42
43
44 static void checkPmapContribs (const PhotonMap *pmap, LUTAB *srcContrib)
45 /* Check modifiers for light source contributions */
46 {
47 const PhotonPrimary *primary = pmap -> primary;
48 OBJREC *srcMod;
49 unsigned long i, found = 0;
50
51 /* Make sure at least one of the modifiers is actually in the pmap,
52 * otherwise findPhotons() winds up in an infinite loop! */
53 for (i = pmap -> primarySize; i; --i, ++primary) {
54 if (primary -> srcIdx < 0 || primary -> srcIdx >= nsources)
55 error(INTERNAL, "invalid light source index in photon map");
56
57 srcMod = findmaterial(source [primary -> srcIdx].so);
58 if ((MODCONT*)lu_find(srcContrib, srcMod -> oname) -> data)
59 ++found;
60 }
61
62 if (!found)
63 error(USER, "modifiers not in photon map");
64 }
65
66
67
68 void initPmapContrib (LUTAB *srcContrib, unsigned numSrcContrib)
69 {
70 unsigned t;
71
72 for (t = 0; t < NUM_PMAP_TYPES; t++)
73 if (photonMaps [t] && t != PMAP_TYPE_CONTRIB) {
74 sprintf(errmsg, "%s photon map does not support contributions",
75 pmapName [t]);
76 error(USER, errmsg);
77 }
78
79 /* Get params */
80 setPmapContribParams(contribPmap, srcContrib);
81
82 if (contribPhotonMapping) {
83 if (contribPmap -> maxGather < numSrcContrib) {
84 /* Adjust density estimate bandwidth if lower than modifier
85 * count, otherwise contributions are missing */
86 error(WARNING, "contrib density estimate bandwidth too low, "
87 "adjusting to modifier count");
88 contribPmap -> maxGather = numSrcContrib;
89 }
90
91 /* Sanity check */
92 checkPmapContribs(contribPmap, srcContrib);
93 }
94 }
95
96
97
98 void photonContrib (PhotonMap *pmap, RAY *ray, COLOR irrad)
99 /* Sum up light source contributions from photons in pmap->srcContrib */
100 {
101 unsigned i;
102 PhotonSQNode *sq;
103 float r, invArea;
104 RREAL rayCoeff [3];
105
106 setcolor(irrad, 0, 0, 0);
107
108 if (!pmap -> maxGather)
109 return;
110
111 /* Ignore sources */
112 if (ray -> ro)
113 if (islight(objptr(ray -> ro -> omod) -> otype))
114 return;
115
116 /* Get cumulative path
117 * coefficient up to photon lookup point */
118 raycontrib(rayCoeff, ray, PRIMARY);
119
120 /* Lookup photons */
121 pmap -> squeueEnd = 0;
122 findPhotons(pmap, ray);
123
124 /* Need at least 2 photons */
125 if (pmap -> squeueEnd < 2) {
126 #ifdef PMAP_NONEFOUND
127 sprintf(errmsg, "no photons found on %s at (%.3f, %.3f, %.3f)",
128 ray -> ro ? ray -> ro -> oname : "<null>",
129 ray -> rop [0], ray -> rop [1], ray -> rop [2]);
130 error(WARNING, errmsg);
131 #endif
132
133 return;
134 }
135
136 /* Average (squared) radius between furthest two photons to improve
137 * accuracy and get inverse search area 1 / (PI * r^2), with extra
138 * normalisation factor 1 / PI for ambient calculation */
139 sq = pmap -> squeue + 1;
140 r = max(sq -> dist, (sq + 1) -> dist);
141 r = 0.25 * (pmap -> maxDist + r + 2 * sqrt(pmap -> maxDist * r));
142 invArea = 1 / (PI * PI * r);
143
144 /* Skip the extra photon */
145 for (i = 1 ; i < pmap -> squeueEnd; i++, sq++) {
146 COLOR flux;
147
148 /* Get photon's contribution to density estimate */
149 getPhotonFlux(sq -> photon, flux);
150 scalecolor(flux, invArea);
151 #ifdef PMAP_EPANECHNIKOV
152 /* Apply Epanechnikov kernel to photon flux (dists are squared) */
153 scalecolor(flux, 2 * (1 - sq -> dist / r));
154 #endif
155 addcolor(irrad, flux);
156
157 if (pmap -> srcContrib) {
158 const PhotonPrimary *primary = pmap -> primary +
159 sq -> photon -> primary;
160 const SRCREC *sp = &source[primary -> srcIdx];
161 OBJREC *srcMod = findmaterial(sp -> so);
162 MODCONT *srcContrib = (MODCONT*)lu_find(pmap -> srcContrib,
163 srcMod -> oname) -> data;
164 if (!srcContrib)
165 continue;
166
167 /* Photon's emitting light source has modifier whose
168 * contributions are sought */
169 double srcBinReal;
170 int srcBin;
171 RAY srcRay;
172
173 if (srcContrib -> binv -> type != NUM) {
174 /* Use intersection function to set shadow ray parameters
175 * if it's not simply a constant
176 */
177 rayorigin(&srcRay, SHADOW, NULL, NULL);
178 srcRay.rsrc = primary -> srcIdx;
179 VCOPY(srcRay.rorg, primary -> pos);
180 decodedir(srcRay.rdir, primary -> dir);
181
182 if (!(sp->sflags & SDISTANT ? sourcehit(&srcRay)
183 : (*ofun[sp -> so -> otype].funp)(sp -> so, &srcRay)))
184 continue; /* XXX shouldn't happen! */
185
186 worldfunc(RCCONTEXT, &srcRay);
187 set_eparams((char *)srcContrib -> params);
188 }
189
190 if ((srcBinReal = evalue(srcContrib -> binv)) < -.5)
191 continue; /* silently ignore negative bins */
192
193 if ((srcBin = srcBinReal + .5) >= srcContrib -> nbins) {
194 error(WARNING, "bad bin number (ignored)");
195 continue;
196 }
197
198 if (!contrib) {
199 /* Ray coefficient mode; normalise by light source radiance
200 * after applying distrib pattern */
201 int j;
202 raytexture(ray, srcMod -> omod);
203 setcolor(ray -> rcol, srcMod -> oargs.farg [0],
204 srcMod -> oargs.farg [1], srcMod -> oargs.farg [2]);
205 multcolor(ray -> rcol, ray -> pcol);
206 for (j = 0; j < 3; j++)
207 flux [j] = ray -> rcol [j] ? flux [j] / ray -> rcol [j]
208 : 0;
209 }
210
211 multcolor(flux, rayCoeff);
212 addcolor(srcContrib -> cbin [srcBin], flux);
213 }
214 }
215
216 return;
217 }
218
219
220
221 void distribPhotonContrib (PhotonMap* pm)
222 {
223 EmissionMap emap;
224 char errmsg2 [128];
225 unsigned srcIdx;
226 double *srcFlux; /* Emitted flux per light source */
227 const double srcDistribTarget = /* Target photon count per source */
228 nsources ? (double)pm -> distribTarget / nsources : 0;
229
230 if (!pm)
231 error(USER, "no photon map defined");
232
233 if (!nsources)
234 error(USER, "no light sources");
235
236 /* Allocate photon flux per light source; this differs for every
237 * source as all sources contribute the same number of distributed
238 * photons (srcDistribTarget), hence the number of photons emitted per
239 * source does not correlate with its emitted flux. The resulting flux
240 * per photon is therefore adjusted individually for each source. */
241 if (!(srcFlux = calloc(nsources, sizeof(double))))
242 error(SYSTEM, "cannot allocate source flux");
243
244 /* ================================================================
245 * INITIALISASHUNN - Set up emisshunn and scattering funcs
246 * ================================================================ */
247 emap.samples = NULL;
248 emap.src = NULL;
249 emap.maxPartitions = MAXSPART;
250 emap.partitions = (unsigned char*)malloc(emap.maxPartitions >> 1);
251 if (!emap.partitions)
252 error(USER, "can't allocate source partitions");
253
254 initPhotonMap(pm, PMAP_TYPE_CONTRIB);
255 initPhotonEmissionFuncs();
256 initPhotonScatterFuncs();
257
258 /* Get photon ports if specified */
259 if (ambincl == 1)
260 getPhotonPorts();
261
262 /* Get photon sensor modifiers */
263 getPhotonSensors(photonSensorList);
264
265 /* Seed RNGs for photon distribution */
266 pmapSeed(randSeed, partState);
267 pmapSeed(randSeed, emitState);
268 pmapSeed(randSeed, cntState);
269 pmapSeed(randSeed, mediumState);
270 pmapSeed(randSeed, scatterState);
271 pmapSeed(randSeed, rouletteState);
272
273 /* Record start time and enable progress report signal handler */
274 repStartTime = time(NULL);
275 #ifdef SIGCONT
276 signal(SIGCONT, pmapDistribReport);
277 #endif
278
279 for (srcIdx = 0; srcIdx < nsources; srcIdx++) {
280 unsigned portCnt = 0, passCnt = 0, prePassCnt = 0;
281 double srcNumEmit = 0; /* # photons to emit from source */
282 unsigned long srcNumDistrib = pm -> heapEnd; /* # photons stored */
283
284 srcFlux [srcIdx] = repProgress = 0;
285 emap.src = source + srcIdx;
286
287 if (photonRepTime)
288 eputs("\n");
289
290 /* =============================================================
291 * FLUX INTEGRATION - Get total flux emitted from light source
292 * ============================================================= */
293 do {
294 emap.port = emap.src -> sflags & SDISTANT
295 ? photonPorts + portCnt : NULL;
296 photonPartition [emap.src -> so -> otype] (&emap);
297
298 if (photonRepTime) {
299 sprintf(errmsg, "Integrating flux from source %s (mod %s) ",
300 source [srcIdx].so -> oname,
301 objptr(source [srcIdx].so -> omod) -> oname);
302
303 if (emap.port) {
304 sprintf(errmsg2, "via port %s ",
305 photonPorts [portCnt].so -> oname);
306 strcat(errmsg, errmsg2);
307 }
308
309 sprintf(errmsg2, "(%lu partitions)...\n",
310 emap.numPartitions);
311 strcat(errmsg, errmsg2);
312 eputs(errmsg);
313 fflush(stderr);
314 }
315
316 for (emap.partitionCnt = 0;
317 emap.partitionCnt < emap.numPartitions;
318 emap.partitionCnt++) {
319 initPhotonEmission(&emap, pdfSamples);
320 srcFlux [srcIdx] += colorAvg(emap.partFlux);
321 }
322
323 portCnt++;
324 } while (portCnt < numPhotonPorts);
325
326 if (srcFlux [srcIdx] < FTINY) {
327 sprintf(errmsg, "source %s has zero emission",
328 source [srcIdx].so -> oname);
329 error(WARNING, errmsg);
330 }
331 else {
332 /* ==========================================================
333 * 2-PASS PHOTON DISTRIBUTION
334 * Pass 1 (pre): emit fraction of target photon count
335 * Pass 2 (main): based on outcome of pass 1, estimate
336 * remaining number of photons to emit to
337 * approximate target count
338 * ========================================================== */
339 do {
340 if (!passCnt) {
341 /* INIT PASS 1 */
342 if (++prePassCnt > maxPreDistrib) {
343 /* Warn if no photons contributed after sufficient
344 * iterations */
345 sprintf(errmsg, "too many prepasses, no photons "
346 "from source %s", source [srcIdx].so -> oname);
347 error(WARNING, errmsg);
348 break;
349 }
350
351 /* Num to emit is fraction of target count */
352 srcNumEmit = preDistrib * srcDistribTarget;
353 }
354
355 else {
356 /* INIT PASS 2 */
357 /* Based on the outcome of the predistribution we can now
358 * figure out how many more photons we have to emit from
359 * the current source to meet the target count,
360 * srcDistribTarget. This value is clamped to 0 in case
361 * the target has already been exceeded in pass 1.
362 * srcNumEmit and srcNumDistrib is the number of photons
363 * emitted and distributed (stored) from the current
364 * source in pass 1, respectively. */
365 srcNumDistrib = pm -> heapEnd - srcNumDistrib;
366 srcNumEmit *= srcNumDistrib
367 ? max(srcDistribTarget/srcNumDistrib, 1) - 1
368 : 0;
369
370 if (!srcNumEmit)
371 /* No photons left to distribute in main pass */
372 break;
373 }
374
375 /* Set completion count for progress report */
376 repComplete = srcNumEmit + repProgress;
377 portCnt = 0;
378
379 do {
380 emap.port = emap.src -> sflags & SDISTANT
381 ? photonPorts + portCnt : NULL;
382 photonPartition [emap.src -> so -> otype] (&emap);
383
384 if (photonRepTime) {
385 if (!passCnt)
386 sprintf(errmsg, "PREPASS %d on source %s (mod %s) ",
387 prePassCnt, source [srcIdx].so -> oname,
388 objptr(source[srcIdx].so->omod) -> oname);
389 else
390 sprintf(errmsg, "MAIN PASS on source %s (mod %s) ",
391 source [srcIdx].so -> oname,
392 objptr(source[srcIdx].so->omod) -> oname);
393
394 if (emap.port) {
395 sprintf(errmsg2, "via port %s ",
396 photonPorts [portCnt].so -> oname);
397 strcat(errmsg, errmsg2);
398 }
399
400 sprintf(errmsg2, "(%lu partitions)...\n",
401 emap.numPartitions);
402 strcat(errmsg, errmsg2);
403 eputs(errmsg);
404 fflush(stderr);
405 }
406
407 for (emap.partitionCnt = 0;
408 emap.partitionCnt < emap.numPartitions;
409 emap.partitionCnt++) {
410 double partNumEmit;
411 unsigned long partEmitCnt;
412
413 /* Get photon origin within current source partishunn
414 * and build emission map */
415 photonOrigin [emap.src -> so -> otype] (&emap);
416 initPhotonEmission(&emap, pdfSamples);
417
418 /* Number of photons to emit from ziss partishunn;
419 * scale according to its normalised contribushunn to
420 * the emitted source flux */
421 partNumEmit = srcNumEmit * colorAvg(emap.partFlux) /
422 srcFlux [srcIdx];
423 partEmitCnt = (unsigned long)partNumEmit;
424
425 /* Probabilistically account for fractional photons */
426 if (pmapRandom(cntState) < partNumEmit - partEmitCnt)
427 partEmitCnt++;
428
429 /* Integer counter avoids FP rounding errors */
430 while (partEmitCnt--) {
431 RAY photonRay;
432
433 /* Emit photon according to PDF (if any), allocate
434 * associated primary ray, and trace through scene
435 * until absorbed/leaked */
436 emitPhoton(&emap, &photonRay);
437 addPhotonPrimary(pm, &photonRay);
438 tracePhoton(&photonRay);
439
440 /* Record progress */
441 repProgress++;
442
443 if (photonRepTime > 0 &&
444 time(NULL) >= repLastTime + photonRepTime)
445 pmapDistribReport();
446 #ifdef SIGCONT
447 else signal(SIGCONT, pmapDistribReport);
448 #endif
449 }
450 }
451
452 portCnt++;
453 } while (portCnt < numPhotonPorts);
454
455 if (pm -> heapEnd == srcNumDistrib)
456 /* Double preDistrib in case no photons were stored
457 * for this source and redo pass 1 */
458 preDistrib *= 2;
459 else {
460 /* Now do pass 2 */
461 passCnt++;
462 if (photonRepTime)
463 eputs("\n");
464 }
465 } while (passCnt < 2);
466
467 /* Flux per photon emitted from this source; repProgress is the
468 * number of emitted photons after both passes */
469 srcFlux [srcIdx] = repProgress ? srcFlux [srcIdx] / repProgress
470 : 0;
471 }
472 }
473
474 /* ================================================================
475 * POST-DISTRIBUTION - Set photon flux and build kd-tree, etc.
476 * ================================================================ */
477 #ifdef SIGCONT
478 signal(SIGCONT, SIG_DFL);
479 #endif
480 free(emap.samples);
481
482 if (!pm -> heapEnd)
483 error(USER, "empty photon map");
484
485 /* Check for valid primary photon rays */
486 if (!pm -> primary)
487 error(INTERNAL, "no primary rays in contribution photon map");
488
489 if (pm -> primary [pm -> primaryEnd].srcIdx < 0)
490 /* Last primary ray is unused, so decrement counter */
491 pm -> primaryEnd--;
492
493 if (photonRepTime) {
494 eputs("\nBuilding contrib photon heap...\n");
495 fflush(stderr);
496 }
497
498 balancePhotons(pm, srcFlux);
499 }