ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/rt/source.c
Revision: 1.33
Committed: Wed Jun 19 16:36:44 1991 UTC (32 years, 10 months ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 1.32: +93 -59 lines
Log Message:
added virtual sources

File Contents

# Content
1 /* Copyright (c) 1990 Regents of the University of California */
2
3 #ifndef lint
4 static char SCCSid[] = "$SunId$ LBL";
5 #endif
6
7 /*
8 * source.c - routines dealing with illumination sources.
9 *
10 * 8/20/85
11 */
12
13 #include "ray.h"
14
15 #include "octree.h"
16
17 #include "source.h"
18
19 #include "otypes.h"
20
21 #include "cone.h"
22
23 #include "face.h"
24
25 #include "random.h"
26
27
28 extern double dstrsrc; /* source distribution amount */
29 extern double shadthresh; /* relative shadow threshold */
30 extern double shadcert; /* shadow testing certainty */
31
32 SRCREC *source = NULL; /* our list of sources */
33 int nsources = 0; /* the number of sources */
34
35 static CONTRIB *srccnt; /* source contributions in direct() */
36 static CNTPTR *cntord; /* source ordering in direct() */
37
38
39 marksources() /* find and mark source objects */
40 {
41 int i;
42 register OBJREC *o, *m;
43 register SRCREC *ns;
44
45 for (i = 0; i < nobjects; i++) {
46
47 o = objptr(i);
48
49 if (o->omod == OVOID)
50 continue;
51
52 m = objptr(o->omod);
53
54 if (!islight(m->otype))
55 continue;
56
57 if (m->oargs.nfargs != (m->otype == MAT_GLOW ? 4 :
58 m->otype == MAT_SPOT ? 7 : 3))
59 objerror(m, USER, "bad # arguments");
60
61 if (m->otype == MAT_GLOW &&
62 o->otype != OBJ_SOURCE &&
63 m->oargs.farg[3] <= FTINY)
64 continue; /* don't bother */
65
66 if ((ns = newsource()) == NULL)
67 goto memerr;
68
69 setsource(ns, o);
70
71 if (m->otype == MAT_GLOW) {
72 ns->sflags |= SPROX;
73 ns->sl.prox = m->oargs.farg[3];
74 if (o->otype == OBJ_SOURCE)
75 ns->sflags |= SSKIP;
76 } else if (m->otype == MAT_SPOT) {
77 ns->sflags |= SSPOT;
78 if ((ns->sl.s = makespot(m)) == NULL)
79 goto memerr;
80 }
81 }
82 if (nsources <= 0) {
83 error(WARNING, "no light sources found");
84 return;
85 }
86 markvirtuals(); /* find and add virtual sources */
87 srccnt = (CONTRIB *)malloc(nsources*sizeof(CONTRIB));
88 cntord = (CNTPTR *)malloc(nsources*sizeof(CNTPTR));
89 if (srccnt != NULL && cntord != NULL)
90 goto memerr;
91 return;
92 memerr:
93 error(SYSTEM, "out of memory in marksources");
94 }
95
96
97 SRCREC *
98 newsource() /* allocate new source in our array */
99 {
100 if (nsources == 0)
101 source = (SRCREC *)malloc(sizeof(SRCREC));
102 else
103 source = (SRCREC *)realloc((char *)source,
104 (unsigned)(nsources+1)*sizeof(SRCREC));
105 if (source == NULL)
106 return(NULL);
107 source[nsources].sflags = 0;
108 source[nsources].nhits = 1;
109 source[nsources].ntests = 2; /* initial hit probability = 1/2 */
110 return(&source[nsources++]);
111 }
112
113
114 setsource(src, so) /* add a source to the array */
115 register SRCREC *src;
116 register OBJREC *so;
117 {
118 double cos(), tan(), sqrt();
119 double theta;
120 FACE *f;
121 CONE *co;
122 int j;
123 register int i;
124
125 src->sa.success = 2*AIMREQT-1; /* bitch on second failure */
126 src->so = so;
127
128 switch (so->otype) {
129 case OBJ_SOURCE:
130 if (so->oargs.nfargs != 4)
131 objerror(so, USER, "bad arguments");
132 src->sflags |= SDISTANT;
133 VCOPY(src->sloc, so->oargs.farg);
134 if (normalize(src->sloc) == 0.0)
135 objerror(so, USER, "zero direction");
136 theta = PI/180.0/2.0 * so->oargs.farg[3];
137 if (theta <= FTINY)
138 objerror(so, USER, "zero size");
139 src->ss = theta >= PI/4 ? 1.0 : tan(theta);
140 src->ss2 = 2.0*PI * (1.0 - cos(theta));
141 break;
142 case OBJ_SPHERE:
143 VCOPY(src->sloc, so->oargs.farg);
144 src->ss = so->oargs.farg[3];
145 src->ss2 = PI * src->ss * src->ss;
146 break;
147 case OBJ_FACE:
148 /* get the face */
149 f = getface(so);
150 /* find the center */
151 for (j = 0; j < 3; j++) {
152 src->sloc[j] = 0.0;
153 for (i = 0; i < f->nv; i++)
154 src->sloc[j] += VERTEX(f,i)[j];
155 src->sloc[j] /= (double)f->nv;
156 }
157 if (!inface(src->sloc, f))
158 objerror(so, USER, "cannot hit center");
159 src->sflags |= SFLAT;
160 VCOPY(src->snorm, f->norm);
161 src->ss = sqrt(f->area / PI);
162 src->ss2 = f->area;
163 break;
164 case OBJ_RING:
165 /* get the ring */
166 co = getcone(so, 0);
167 VCOPY(src->sloc, CO_P0(co));
168 if (CO_R0(co) > 0.0)
169 objerror(so, USER, "cannot hit center");
170 src->sflags |= SFLAT;
171 VCOPY(src->snorm, co->ad);
172 src->ss = CO_R1(co);
173 src->ss2 = PI * src->ss * src->ss;
174 break;
175 default:
176 objerror(so, USER, "illegal material");
177 }
178 }
179
180
181 SPOT *
182 makespot(m) /* make a spotlight */
183 register OBJREC *m;
184 {
185 extern double cos();
186 register SPOT *ns;
187
188 if ((ns = (SPOT *)malloc(sizeof(SPOT))) == NULL)
189 return(NULL);
190 ns->siz = 2.0*PI * (1.0 - cos(PI/180.0/2.0 * m->oargs.farg[3]));
191 VCOPY(ns->aim, m->oargs.farg+4);
192 if ((ns->flen = normalize(ns->aim)) == 0.0)
193 objerror(m, USER, "zero focus vector");
194 return(ns);
195 }
196
197
198 double
199 srcray(sr, r, sn) /* send a ray to a source, return domega */
200 register RAY *sr; /* returned source ray */
201 RAY *r; /* ray which hit object */
202 register int sn; /* source number */
203 {
204 double ddot; /* (distance times) cosine */
205 FVECT vd;
206 double d;
207 register int i;
208
209 if (source[sn].sflags & SSKIP)
210 return(0.0); /* skip this source */
211
212 rayorigin(sr, r, SHADOW, 1.0); /* ignore limits */
213
214 sr->rsrc = sn; /* remember source */
215 /* get source direction */
216 if (source[sn].sflags & SDISTANT) {
217 if (source[sn].sflags & SSPOT) { /* check location */
218 for (i = 0; i < 3; i++)
219 vd[i] = sr->rorg[i] - source[sn].sl.s->aim[i];
220 d = DOT(source[sn].sloc,vd);
221 d = DOT(vd,vd) - d*d;
222 if (PI*d > source[sn].sl.s->siz)
223 return(0.0);
224 }
225 /* constant direction */
226 VCOPY(sr->rdir, source[sn].sloc);
227 } else { /* compute direction */
228 for (i = 0; i < 3; i++)
229 sr->rdir[i] = source[sn].sloc[i] - sr->rorg[i];
230
231 if (source[sn].sflags & SFLAT &&
232 (ddot = -DOT(sr->rdir, source[sn].snorm)) <= FTINY)
233 return(0.0); /* behind surface! */
234 }
235 if (dstrsrc > FTINY) {
236 /* distribute source direction */
237 dimlist[ndims++] = sn;
238 for (i = 0; i < 3; i++) {
239 dimlist[ndims] = i + 8831;
240 vd[i] = dstrsrc * source[sn].ss *
241 (1.0 - 2.0*urand(ilhash(dimlist,ndims+1)+samplendx));
242 }
243 ndims--;
244 if (source[sn].sflags & SFLAT) { /* project offset */
245 d = DOT(vd, source[sn].snorm);
246 for (i = 0; i < 3; i++)
247 vd[i] -= d * source[sn].snorm[i];
248 }
249 for (i = 0; i < 3; i++) /* offset source direction */
250 sr->rdir[i] += vd[i];
251
252 } else if (source[sn].sflags & SDISTANT)
253 /* already normalized */
254 return(source[sn].ss2);
255
256 if ((d = normalize(sr->rdir)) == 0.0)
257 /* at source! */
258 return(0.0);
259
260 if (source[sn].sflags & SDISTANT)
261 /* domega constant */
262 return(source[sn].ss2);
263
264 /* check proximity */
265 if (source[sn].sflags & SPROX &&
266 d > source[sn].sl.prox)
267 return(0.0);
268 /* compute dot product */
269 if (source[sn].sflags & SFLAT)
270 ddot /= d;
271 else
272 ddot = 1.0;
273 /* check angle */
274 if (source[sn].sflags & SSPOT) {
275 if (source[sn].sl.s->siz < 2.0*PI *
276 (1.0 + DOT(source[sn].sl.s->aim,sr->rdir)))
277 return(0.0);
278 d += source[sn].sl.s->flen; /* adjust length */
279 }
280 /* compute domega */
281 return(ddot*source[sn].ss2/(d*d));
282 }
283
284
285 sourcehit(r) /* check to see if ray hit distant source */
286 register RAY *r;
287 {
288 int first, last;
289 register int i;
290
291 if (r->rsrc >= 0) { /* check only one if aimed */
292 first = last = r->rsrc;
293 } else { /* otherwise check all */
294 first = 0; last = nsources-1;
295 }
296 for (i = first; i <= last; i++)
297 if (source[i].sflags & SDISTANT)
298 /*
299 * Check to see if ray is within
300 * solid angle of source.
301 */
302 if (2.0*PI * (1.0 - DOT(source[i].sloc,r->rdir))
303 <= source[i].ss2) {
304 r->ro = source[i].so;
305 if (!(source[i].sflags & SSKIP))
306 break;
307 }
308
309 if (r->ro != NULL) {
310 for (i = 0; i < 3; i++)
311 r->ron[i] = -r->rdir[i];
312 r->rod = 1.0;
313 r->rox = NULL;
314 return(1);
315 }
316 return(0);
317 }
318
319
320 static int
321 cntcmp(sc1, sc2) /* contribution compare (descending) */
322 register CNTPTR *sc1, *sc2;
323 {
324 if (sc1->brt > sc2->brt)
325 return(-1);
326 if (sc1->brt < sc2->brt)
327 return(1);
328 return(0);
329 }
330
331
332 direct(r, f, p) /* add direct component */
333 RAY *r; /* ray that hit surface */
334 int (*f)(); /* direct component coefficient function */
335 char *p; /* data for f */
336 {
337 extern double pow();
338 register int sn;
339 int nshadcheck, ncnts;
340 int nhits;
341 double prob, ourthresh, hwt;
342 RAY sr;
343 /* NOTE: srccnt and cntord global so no recursion */
344 if (nsources <= 0)
345 return; /* no sources?! */
346 /* compute number to check */
347 nshadcheck = pow((double)nsources, shadcert) + .5;
348 /* modify threshold */
349 ourthresh = shadthresh / r->rweight;
350 /* potential contributions */
351 for (sn = 0; sn < nsources; sn++) {
352 cntord[sn].sno = sn;
353 cntord[sn].brt = 0.0;
354 /* get source ray */
355 if ((srccnt[sn].dom = srcray(&sr, r, sn)) == 0.0)
356 continue;
357 VCOPY(srccnt[sn].dir, sr.rdir);
358 /* compute coefficient */
359 (*f)(srccnt[sn].val, p, srccnt[sn].dir, srccnt[sn].dom);
360 cntord[sn].brt = bright(srccnt[sn].val);
361 if (cntord[sn].brt <= 0.0)
362 continue;
363 /* compute contribution */
364 srcvalue(&sr);
365 multcolor(srccnt[sn].val, sr.rcol);
366 cntord[sn].brt = bright(srccnt[sn].val);
367 }
368 /* sort contributions */
369 qsort(cntord, nsources, sizeof(CNTPTR), cntcmp);
370 { /* find last */
371 register int l, m;
372
373 sn = 0; ncnts = l = nsources;
374 while ((m = (sn + ncnts) >> 1) != l) {
375 if (cntord[m].brt > 0.0)
376 sn = m;
377 else
378 ncnts = m;
379 l = m;
380 }
381 }
382 /* accumulate tail */
383 for (sn = ncnts-1; sn > 0; sn--)
384 cntord[sn-1].brt += cntord[sn].brt;
385 /* test for shadows */
386 nhits = 0;
387 for (sn = 0; sn < ncnts; sn++) {
388 /* check threshold */
389 if ((sn+nshadcheck>=ncnts ? cntord[sn].brt :
390 cntord[sn].brt-cntord[sn+nshadcheck].brt)
391 < ourthresh*bright(r->rcol))
392 break;
393 /* get statistics */
394 source[cntord[sn].sno].ntests++;
395 /* test for hit */
396 rayorigin(&sr, r, SHADOW, 1.0);
397 VCOPY(sr.rdir, srccnt[cntord[sn].sno].dir);
398 sr.rsrc = cntord[sn].sno;
399 if (localhit(&sr, &thescene) &&
400 ( sr.ro != source[cntord[sn].sno].so ||
401 source[cntord[sn].sno].sflags & SFOLLOW )) {
402 /* follow entire path */
403 raycont(&sr);
404 if (bright(sr.rcol) <= FTINY)
405 continue; /* missed! */
406 (*f)(srccnt[cntord[sn].sno].val, p,
407 srccnt[cntord[sn].sno].dir,
408 srccnt[cntord[sn].sno].dom);
409 multcolor(srccnt[cntord[sn].sno].val, sr.rcol);
410 }
411 /* add contribution if hit */
412 addcolor(r->rcol, srccnt[cntord[sn].sno].val);
413 nhits++;
414 source[cntord[sn].sno].nhits++;
415 }
416 /* surface hit rate */
417 if (sn > 0)
418 hwt = (double)nhits / (double)sn;
419 else
420 hwt = 0.5;
421 #ifdef DEBUG
422 sprintf(errmsg, "%d tested, %d untested, %f hit rate\n",
423 sn, ncnts-sn, hwt);
424 eputs(errmsg);
425 #endif
426 /* add in untested sources */
427 for ( ; sn < ncnts; sn++) {
428 prob = hwt * (double)source[cntord[sn].sno].nhits /
429 (double)source[cntord[sn].sno].ntests;
430 scalecolor(srccnt[cntord[sn].sno].val, prob);
431 addcolor(r->rcol, srccnt[cntord[sn].sno].val);
432 }
433 }
434
435
436 srcvalue(r) /* punch ray to source and compute value */
437 RAY *r;
438 {
439 register SRCREC *sp;
440
441 sp = &source[r->rsrc];
442 if (sp->sflags & SVIRTUAL) { /* virtual source */
443 RAY nr;
444 /* check intersection */
445 if (!(*ofun[sp->so->otype].funp)(sp->so, r))
446 return;
447 /* relay ray to source */
448 vsrcrelay(&nr, r);
449 srcvalue(&nr);
450 return;
451 }
452 /* compute intersection */
453 if (sp->sflags & SDISTANT ? sourcehit(r) :
454 (*ofun[sp->so->otype].funp)(sp->so, r)) {
455 if (sp->sa.success >= 0)
456 sp->sa.success++;
457 raycont(r); /* compute contribution */
458 return;
459 }
460 if (sp->sa.success < 0)
461 return; /* bitched already */
462 sp->sa.success -= AIMREQT;
463 if (sp->sa.success >= 0)
464 return; /* leniency */
465 sprintf(errmsg, "aiming failure for light source \"%s\"",
466 sp->so->oname);
467 error(WARNING, errmsg); /* issue warning */
468 }
469
470
471 #define wrongsource(m, r) (m->otype!=MAT_ILLUM && \
472 r->rsrc>=0 && \
473 source[r->rsrc].so!=r->ro)
474
475 #define badambient(m, r) ((r->crtype&(AMBIENT|SHADOW))==AMBIENT && \
476 !(m->otype==MAT_GLOW&&r->rot>m->oargs.farg[3]))
477
478 #define passillum(m, r) (m->otype==MAT_ILLUM && \
479 !(r->rsrc>=0&&source[r->rsrc].so==r->ro))
480
481
482 m_light(m, r) /* ray hit a light source */
483 register OBJREC *m;
484 register RAY *r;
485 {
486 /* check for over-counting */
487 if (wrongsource(m, r) || badambient(m, r))
488 return;
489 /* check for passed illum */
490 if (passillum(m, r)) {
491
492 if (m->oargs.nsargs < 1 || !strcmp(m->oargs.sarg[0], VOIDID))
493 raytrans(r);
494 else
495 rayshade(r, modifier(m->oargs.sarg[0]));
496
497 /* otherwise treat as source */
498 } else {
499 /* check for behind */
500 if (r->rod < 0.0)
501 return;
502 /* get distribution pattern */
503 raytexture(r, m->omod);
504 /* get source color */
505 setcolor(r->rcol, m->oargs.farg[0],
506 m->oargs.farg[1],
507 m->oargs.farg[2]);
508 /* modify value */
509 multcolor(r->rcol, r->pcol);
510 }
511 }