ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/rt/pmapcontrib.c
Revision: 2.11
Committed: Thu Feb 4 11:37:00 2016 UTC (8 years, 4 months ago) by rschregle
Content type: text/plain
Branch: MAIN
Changes since 2.10: +16 -16 lines
Log Message:
Minor pmapcontrib cleanup, revised photon normal test in pmapdata

File Contents

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