1 //////////////////////////////////////////////////////////////////////
2 //
3 // Pixie
4 //
5 // Copyright � 1999 - 2003, Okan Arikan
6 //
7 // Contact: okan@cs.utexas.edu
8 //
9 // This library is free software; you can redistribute it and/or
10 // modify it under the terms of the GNU Lesser General Public
11 // License as published by the Free Software Foundation; either
12 // version 2.1 of the License, or (at your option) any later version.
13 //
14 // This library is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 // Lesser General Public License for more details.
18 //
19 // You should have received a copy of the GNU Lesser General Public
20 // License along with this library; if not, write to the Free Software
21 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22 //
23 ///////////////////////////////////////////////////////////////////////
24 ///////////////////////////////////////////////////////////////////////
25 //
26 // File : raytracer.cpp
27 // Classes : CRaytracer
28 // Description :
29 //
30 ////////////////////////////////////////////////////////////////////////
31 #include <math.h>
32
33 #include "raytracer.h"
34 #include "shading.h"
35 #include "random.h"
36 #include "memory.h"
37 #include "error.h"
38 #include "renderer.h"
39
40 ///////////////////////////////////////////////////////////////////////
41 // Class : CPrimaryBundle
42 // Method : CPrimaryBundle
43 // Description : Ctor
44 // Return Value : -
45 // Comments :
CPrimaryBundle(int mr,int numSamples,int nExtraChans,int * sampOrder,int numExtraSamp,float * sampDefaults)46 CPrimaryBundle::CPrimaryBundle(int mr,int numSamples,int nExtraChans,int *sampOrder,int numExtraSamp,float *sampDefaults) {
47 maxPrimaryRays = mr;
48 numExtraChannels = 0; // These will be filled in after constuction if needed
49 sampleOrder = NULL;
50 rayBase = new CPrimaryRay[maxPrimaryRays];
51 rays = new CRay*[maxPrimaryRays];
52 last = 0;
53 depth = 0;
54 numRays = 0;
55 allSamples = new float[numSamples*maxPrimaryRays];
56
57 float *src = allSamples;
58 for (int i=0;i<maxPrimaryRays;i++,src+=numSamples) {
59 rayBase[i].samples = src;
60 }
61
62 numExtraChannels = nExtraChans;
63 sampleOrder = sampOrder;
64 numExtraSamples = numExtraSamp;
65 sampleDefaults = sampDefaults;
66
67 }
68
69 ///////////////////////////////////////////////////////////////////////
70 // Class : CPrimaryBundle
71 // Method : ~CPrimaryBundle
72 // Description : Dtor
73 // Return Value : -
74 // Comments :
~CPrimaryBundle()75 CPrimaryBundle::~CPrimaryBundle() {
76 delete [] rayBase;
77 delete [] rays;
78 delete [] allSamples;
79 }
80
81 ///////////////////////////////////////////////////////////////////////
82 // Class : CRaytracer
83 // Method : postTraceAction
84 // Description : Post trace action, force shading
85 // Return Value : -
86 // Comments :
postTraceAction()87 int CPrimaryBundle::postTraceAction() {
88 return TRUE;
89 }
90
91 ///////////////////////////////////////////////////////////////////////
92 // Class : CRaytracer
93 // Method : postShade
94 // Description : Record the raytracing results
95 // Return Value : -
96 // Comments :
postShade(int nr,CRay ** r,float ** varying)97 void CPrimaryBundle::postShade(int nr,CRay **r,float **varying) {
98 float *Ci = varying[VARIABLE_CI];
99 float *Oi = varying[VARIABLE_OI];
100 const int *cOrder = sampleOrder;
101
102 // FIXME: make this deal with comp and non comp channels properly
103 if (depth == 0) {
104
105 // First hit
106 for (int i=0;i<nr;i++,Ci+=3,Oi+=3) {
107 CPrimaryRay *cRay = (CPrimaryRay *) r[i];
108
109 // Is this a matte surface
110 if (cRay->object->attributes->flags & ATTRIBUTES_FLAGS_MATTE) {
111 initv(cRay->color,0);
112 initv(cRay->opacity,0);
113 cRay->ropacity[0] = 1-Oi[0];
114 cRay->ropacity[1] = 1-Oi[1];
115 cRay->ropacity[2] = 1-Oi[2];
116 } else {
117 movvv(cRay->color,Ci);
118 movvv(cRay->opacity,Oi);
119 cRay->ropacity[0] = 1-Oi[0];
120 cRay->ropacity[1] = 1-Oi[1];
121 cRay->ropacity[2] = 1-Oi[2];
122 }
123
124 if ((Oi[0] < CRenderer::opacityThreshold[0]) ||
125 (Oi[1] < CRenderer::opacityThreshold[1]) ||
126 (Oi[2] < CRenderer::opacityThreshold[2])) {
127 rays[last++] = cRay;
128 } else {
129 movvv(cRay->samples,cRay->color);
130 }
131
132 cRay->samples[3] = (float) ((cRay->opacity[0] + cRay->opacity[1] + cRay->opacity[2])*0.333333333);
133 cRay->samples[4] = cRay->t;
134 }
135
136 // Copy the extra samples
137 int k = 5;
138 for (int i=0;i<numExtraChannels;i++) {
139 const int outType = *cOrder++;
140 const int channelSamples = *cOrder++;
141 const float *s = varying[outType];
142 float *d;
143
144 switch(channelSamples) {
145 case 0:
146 break;
147 case 1:
148 for (int j=0;j<nr;j++) {
149 d = ((CPrimaryRay *) r[j])->samples + k;
150 *d++ = *s++;
151 }
152 k++;
153 break;
154 case 2:
155 for (int j=0;j<nr;j++) {
156 d = ((CPrimaryRay *) r[j])->samples + k;
157 *d++ = *s++;
158 *d++ = *s++;
159 }
160 k += 2;
161 break;
162 case 3:
163 for (int j=0;j<nr;j++) {
164 d = ((CPrimaryRay *) r[j])->samples + k;
165 *d++ = *s++;
166 *d++ = *s++;
167 *d++ = *s++;
168 }
169 k += 3;
170 break;
171 case 4:
172 for (int j=0;j<nr;j++) {
173 d = ((CPrimaryRay *) r[j])->samples + k;
174 *d++ = *s++;
175 *d++ = *s++;
176 *d++ = *s++;
177 *d++ = *s++;
178 }
179 k += 4;
180 break;
181 default:
182 for (int j=0;j<nr;j++) {
183 d = ((CPrimaryRay *) r[j])->samples + k;
184 for (int l=channelSamples;l>0;l--) {
185 *d++ = *s++;
186 }
187 }
188 k += channelSamples;
189 }
190 }
191 } else {
192 // Transparency hit
193 for (int i=0;i<nr;i++,Ci+=3,Oi+=3) {
194 CPrimaryRay *cRay = (CPrimaryRay *) r[i];
195
196 const int transparent = ( (Oi[0] < CRenderer::opacityThreshold[0]) ||
197 (Oi[1] < CRenderer::opacityThreshold[1]) ||
198 (Oi[2] < CRenderer::opacityThreshold[2]));
199
200 if (cRay->object->attributes->flags & ATTRIBUTES_FLAGS_MATTE) {
201 cRay->ropacity[0] *= 1-Oi[0];
202 cRay->ropacity[1] *= 1-Oi[1];
203 cRay->ropacity[2] *= 1-Oi[2];
204 } else {
205 vector t;
206 movvv(t,Oi);
207 mulvv(Ci,cRay->ropacity);
208 mulvv(Oi,cRay->ropacity);
209
210 addvv(cRay->color,Ci);
211 addvv(cRay->opacity,Oi);
212 cRay->ropacity[0] *= 1-t[0];
213 cRay->ropacity[1] *= 1-t[1];
214 cRay->ropacity[2] *= 1-t[2];
215 }
216
217 if (transparent) {
218 rays[last++] = cRay;
219 } else {
220 movvv(cRay->samples,cRay->color);
221 }
222
223 cRay->samples[3] = (float) ((cRay->opacity[0] + cRay->opacity[1] + cRay->opacity[2])*0.333333333);//1;
224
225 }
226
227 // The extra samples are copied from the closest intersection
228 }
229 }
230
231 ///////////////////////////////////////////////////////////////////////
232 // Class : CRaytracer
233 // Method : CRaytracer
234 // Description : Ctor
235 // Return Value : -
236 // Comments :
postShade(int nr,CRay ** r)237 void CPrimaryBundle::postShade(int nr,CRay **r) {
238 if (depth == 0) {
239 for (int i=0;i<nr;i++) {
240 CPrimaryRay *cRay = (CPrimaryRay *) r[i];
241
242 cRay->samples[0] = 0;
243 cRay->samples[1] = 0;
244 cRay->samples[2] = 0;
245 cRay->samples[3] = 0;
246 cRay->samples[4] = C_INFINITY;
247 }
248
249 // zero the extra samples
250 if (numExtraSamples > 0) {
251 for (int j=0;j<nr;j++) {
252 float *d = ((CPrimaryRay *) r[j])->samples + 5;
253 const float *src = sampleDefaults;
254 for (int i=0;i<numExtraSamples;i++)
255 *d++ = *src++;
256 }
257 }
258 } else {
259 for (int i=0;i<nr;i++) {
260 CPrimaryRay *cRay = (CPrimaryRay *) r[i];
261
262 movvv(cRay->samples,cRay->color);
263 }
264 }
265 }
266
267 ///////////////////////////////////////////////////////////////////////
268 // Class : CRaytracer
269 // Method : CRaytracer
270 // Description : Ctor
271 // Return Value : -
272 // Comments :
post()273 void CPrimaryBundle::post() {
274 numRays = last;
275 last = 0;
276 depth++;
277 }
278
279
280 ///////////////////////////////////////////////////////////////////////
281 // Class : CRaytracer
282 // Method : CRaytracer
283 // Description : Ctor
284 // Return Value : -
285 // Comments :
CRaytracer(int thread)286 CRaytracer::CRaytracer(int thread) : CShadingContext(thread), primaryBundle(CRenderer::shootStep,CRenderer::numSamples,CRenderer::numExtraChannels,CRenderer::sampleOrder,CRenderer::numExtraSamples,CRenderer::sampleDefaults) {
287 CRenderer::raytracingFlags |= ATTRIBUTES_FLAGS_PRIMARY_VISIBLE;
288
289 const int xoffset = (int) ceil((CRenderer::pixelFilterWidth - 1)*0.5f);
290 const int yoffset = (int) ceil((CRenderer::pixelFilterHeight - 1)*0.5f);
291 const int xpixels = CRenderer::bucketWidth + 2*xoffset;
292 const int ypixels = CRenderer::bucketHeight + 2*yoffset;
293
294 fbContribution = new float[xpixels*ypixels];
295 fbPixels = new float[xpixels*ypixels*CRenderer::numSamples];
296 }
297
298 ///////////////////////////////////////////////////////////////////////
299 // Class : CRaytracer
300 // Method : ~CRaytracer
301 // Description : Dtor
302 // Return Value : -
303 // Comments :
~CRaytracer()304 CRaytracer::~CRaytracer() {
305 delete [] fbContribution;
306 delete [] fbPixels;
307 }
308
309
310 ///////////////////////////////////////////////////////////////////////
311 // Class : CRaytracer
312 // Method : replace
313 // Description : Replace the occurance of a pointer with another
314 // Return Value : -
315 // Comments :
renderingLoop()316 void CRaytracer::renderingLoop() {
317 int left;
318 int top;
319 int width;
320 int height;
321 CRenderer::CJob job;
322
323 memBegin(threadMemory);
324
325 // While not done
326 while(TRUE) {
327
328 // Get the job from the renderer
329 CRenderer::dispatchJob(thread,job);
330
331 // Process the job
332 if (job.type == CRenderer::CJob::TERMINATE) {
333
334 // End the context
335 break;
336 } else if (job.type == CRenderer::CJob::BUCKET) {
337 const int x = job.xBucket;
338 const int y = job.yBucket;
339
340 assert(x < CRenderer::xBuckets);
341 assert(y < CRenderer::yBuckets);
342
343 currentXBucket = x;
344 currentYBucket = y;
345
346 left = x*CRenderer::bucketWidth;
347 top = y*CRenderer::bucketHeight;
348 width = min(CRenderer::bucketWidth,CRenderer::xPixels-left);
349 height = min(CRenderer::bucketHeight,CRenderer::yPixels-top);
350
351 // Sample the framebuffer
352 sample(left,top,width,height);
353
354 // Flush the data to the out devices
355 CRenderer::commit(left,top,width,height,fbPixels);
356
357 // Send bucket data if we're a netrender
358 if (CRenderer::netClient != INVALID_SOCKET) {
359 CRenderer::sendBucketDataChannels(currentXBucket,currentYBucket);
360 }
361
362 currentXBucket++;
363 if (currentXBucket == CRenderer::xBuckets) {
364 currentXBucket = 0;
365 currentYBucket++;
366 }
367
368 } else {
369 error(CODE_BUG,"Invalid job for the hider\n");
370 }
371 }
372
373 memEnd(threadMemory);
374 }
375
376
377
378
379 ///////////////////////////////////////////////////////////////////////
380 // Class : CRaytracer
381 // Method : sampleFi
382 // Description : Samples a rectangular array of pixels
383 // Return Value : -
384 // Comments :
sample(int left,int top,int xpixels,int ypixels)385 void CRaytracer::sample(int left,int top,int xpixels,int ypixels) {
386 int maxShading = primaryBundle.maxPrimaryRays;
387 int i,j;
388 const int xsamples = xpixels*CRenderer::pixelXsamples + 2*CRenderer::xSampleOffset;
389 const int ysamples = ypixels*CRenderer::pixelYsamples + 2*CRenderer::ySampleOffset;
390 CPrimaryRay *rays = primaryBundle.rayBase;
391 CRay **rayPointers = primaryBundle.rays;
392 CPrimaryRay *cRay;
393 const float invXsamples = 1 / (float) CRenderer::pixelXsamples;
394 const float invYsamples = 1 / (float) CRenderer::pixelYsamples;
395
396 // Clear the framebuffer
397 for (i=0;i<(xpixels*ypixels);i++) {
398 fbContribution[i] = 0;
399 fbPixels[i] = 0;
400 }
401 for (;i<(xpixels*ypixels*CRenderer::numSamples);i++) {
402 fbPixels[i] = 0;
403 }
404
405 // Generate the image
406 {
407 int numShading = 0;
408 int x,y;
409 cRay = rays;
410
411 for (j=0;j<ysamples;j+=8) {
412 for (i=0;i<xsamples;i+=8) {
413 const int my = min(ysamples-j,8);
414 const int mx = min(xsamples-i,8);
415
416 for (y=0;y<my;y++) {
417 for (x=0;x<mx;x++) {
418 cRay->x = (float) left + (float) (i+x-CRenderer::xSampleOffset+CRenderer::jitter*(urand()-(float) 0.5) + (float) 0.5)*invXsamples; // Center the sample location in the pixel
419 cRay->y = (float) top + (float) (j+y-CRenderer::ySampleOffset+CRenderer::jitter*(urand()-(float) 0.5) + (float) 0.5)*invYsamples;
420
421 rayPointers[numShading++] = cRay;
422 cRay++;
423
424 if (numShading >= maxShading) {
425 computeSamples(rays,numShading);
426 splatSamples(rays,numShading,left,top,xpixels,ypixels);
427 cRay = rays;
428 numShading = 0;
429 }
430 }
431 }
432 }
433 }
434
435 // Shade the leftover samples
436 if (numShading > 0) {
437 computeSamples(rays,numShading);
438 splatSamples(rays,numShading,left,top,xpixels,ypixels);
439 }
440 }
441
442 // At this point, fb should contain the framebuffer, normalize the framebuffer
443 for (i=0;i<xpixels*ypixels;i++) {
444 const float invContribution = 1 / fbContribution[i];
445
446 for (int k=0;k<CRenderer::numSamples;k++) {
447 fbPixels[i*CRenderer::numSamples+k] *= invContribution;
448 }
449 }
450 }
451
452
453
454 ///////////////////////////////////////////////////////////////////////
455 // Class : CRaytracer
456 // Method : computeSamples
457 // Description : Raytrace the sample locations
458 // Return Value : -
459 // Comments :
computeSamples(CPrimaryRay * rays,int numShading)460 void CRaytracer::computeSamples(CPrimaryRay *rays,int numShading) {
461 int i;
462 float x,y;
463 CPrimaryRay *cRay = rays;
464
465 if (CRenderer::aperture == 0) {
466 // No Depth of field effect
467
468 for (i=numShading;i>0;i--,cRay++) {
469 // First two numbers in the samples list are the x/y coordinates in pixels of the sample location
470 x = cRay->x;
471 y = cRay->y;
472
473 vector from,to;
474 pixels2camera(from,x,y,0);
475 pixels2camera(to,x,y,CRenderer::imagePlane);
476
477 movvv(cRay->from,from);
478 subvv(cRay->dir,to,from);
479 normalizev(cRay->dir);
480
481 cRay->time = ((CRenderer::flags & OPTIONS_FLAGS_SAMPLEMOTION) ? urand() : 0);
482 cRay->t = C_INFINITY;
483 cRay->flags = ATTRIBUTES_FLAGS_PRIMARY_VISIBLE;
484 cRay->tmin = 0;
485 }
486 } else {
487 for (i=numShading;i>0;i--,cRay++) {
488 // First two numbers in the samples list are the x/y coordinates in pixels of the sample location
489 x = cRay->x;
490 y = cRay->y;
491
492 vector from,to;
493 pixels2camera(from,x,y,0);
494 pixels2camera(to,x,y,CRenderer::focaldistance);
495
496 const float theta = (float) (urand()*2*C_PI);
497 const float r = urand()*CRenderer::aperture;
498 from[COMP_X] += cosf(theta) * r;
499 from[COMP_Y] += sinf(theta) * r;
500
501 movvv(cRay->from,from);
502 subvv(cRay->dir,to,from);
503 normalizev(cRay->dir);
504
505 cRay->time = ((CRenderer::flags & OPTIONS_FLAGS_SAMPLEMOTION) ? urand() : 0);
506 cRay->t = C_INFINITY;
507 cRay->flags = ATTRIBUTES_FLAGS_PRIMARY_VISIBLE;
508 cRay->tmin = 0;
509 }
510 }
511
512 // Setup the ray differentials
513 if (CRenderer::projection == OPTIONS_PROJECTION_PERSPECTIVE) {
514 const float a = CRenderer::dxdPixel/CRenderer::imagePlane;
515
516 cRay = rays;
517 for (i=numShading;i>0;i--,cRay++) {
518 cRay->db = 0;
519 cRay->da = a;
520 }
521 } else{
522 const float b = CRenderer::dxdPixel;
523
524 cRay = rays;
525 for (i=numShading;i>0;i--,cRay++) {
526 cRay->da = 0;
527 cRay->db = b;
528 }
529 }
530
531 // Actually raytrace
532 primaryBundle.numRays = numShading;
533 primaryBundle.last = 0;
534 primaryBundle.depth = 0;
535 trace(&primaryBundle);
536
537 numRaytraceRays += numShading;
538 }
539
540 ///////////////////////////////////////////////////////////////////////
541 // Class : CRaytracer
542 // Method : splatSamples
543 // Description : Splat the samples on the image
544 // Return Value : -
545 // Comments :
splatSamples(CPrimaryRay * samples,int numShading,int left,int top,int xpixels,int ypixels)546 void CRaytracer::splatSamples(CPrimaryRay *samples,int numShading,int left,int top,int xpixels,int ypixels) {
547 const int pw = (int) ceil((CRenderer::pixelFilterWidth-1)*0.5f);
548 const int ph = (int) ceil((CRenderer::pixelFilterHeight-1)*0.5f);
549 const int filterWidth = CRenderer::pixelXsamples + 2*CRenderer::xSampleOffset;
550 const int filterHeight = CRenderer::pixelYsamples + 2*CRenderer::ySampleOffset;
551
552 // Process each sample
553 for (int i=0;i<numShading;i++,samples++) {
554 const float x = samples->x;
555 const float y = samples->y;
556 float *fbs = samples->samples;
557 int pixelX,pixelY;
558 int ix = (int) floor(x);
559 int iy = (int) floor(y);
560 int pl = ix - pw;
561 int pr = ix + pw;
562 int pt = iy - ph;
563 int pb = iy + ph;
564
565 pl = max(pl,left);
566 pt = max(pt,top);
567 pr = min(pr,left + xpixels - 1);
568 pb = min(pb,top + ypixels - 1);
569
570 /*
571 for (pixelY=pt;pixelY<=pb;pixelY++) {
572 for (pixelX=pl;pixelX<=pr;pixelX++) {
573 const int px = (int) floor((x - (pixelX + 0.5f))*filterWidth) + filterWidth>>1;
574 const int py = (int) floor((y - (pixelY + 0.5f))*filterWidth) + filterHeight>>1;
575 const float contribution = CRenderer::pixelFilterKernel[py*filterWidth+px];
576 float *dest = &fbPixels[((pixelY-top)*xpixels+pixelX-left)*CRenderer::numSamples];
577 const float *src = fbs;
578
579 assert((top+ypixels) > pixelY);
580 assert((left+xpixels) > pixelX);
581
582 for (int j=CRenderer::numSamples;j>0;j--) {
583 *dest++ += (*src++)*contribution;
584 }
585 }
586 }
587 */
588
589 float cx,cy;
590 for (cy=pt + 0.5f - y,pixelY=pt;pixelY<=pb;pixelY++,cy++) {
591 for (cx=pl + 0.5f - x,pixelX=pl;pixelX<=pr;pixelX++,cx++) {
592 const float contribution = CRenderer::pixelFilter(cx,cy,CRenderer::pixelFilterWidth,CRenderer::pixelFilterHeight);
593 float *dest = &fbPixels[((pixelY-top)*xpixels+pixelX-left)*CRenderer::numSamples];
594 const float *src = fbs;
595
596 assert((top+ypixels) > pixelY);
597 assert((left+xpixels) > pixelX);
598
599 // Save the contribution for later normalization
600 fbContribution[((pixelY-top)*xpixels+pixelX-left)] += contribution;
601
602 // Accumulate the pixel filter results
603 for (int j=CRenderer::numSamples;j>0;j--) {
604 *dest++ += (*src++)*contribution;
605 }
606 }
607 }
608 }
609 }
610
611