////////////////////////////////////////////////////////////////////// // // Pixie // // Copyright © 1999 - 2003, Okan Arikan // // Contact: okan@cs.berkeley.edu // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public // License as published by the Free Software Foundation; either // version 2 of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // /////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////// // // File : raytracer.cpp // Classes : CRaytracer // Description : // //////////////////////////////////////////////////////////////////////// #include #include "raytracer.h" #include "renderer.h" #include "shading.h" #include "stats.h" #include "random.h" #include "memory.h" #include "error.h" /////////////////////////////////////////////////////////////////////// // Class : CPrimaryBundle // Method : CPrimaryBundle // Description : Ctor // Return Value : - // Comments : // Date last edited : 3/20/2003 CPrimaryBundle::CPrimaryBundle(int mr,int numSamples) { int i; float *src; maxPrimaryRays = mr; rayBase = new CPrimaryRay[maxPrimaryRays]; rays = new CRay*[maxPrimaryRays]; last = 0; depth = 0; numRays = 0; allSamples = new float[numSamples*maxPrimaryRays]; src = allSamples; for (i=0;iobject->attributes->flags & ATTRIBUTES_FLAGS_MATTE) { initv(cRay->color,0); initv(cRay->opacity,0); cRay->ropacity[0] = 1-Oi[0]; cRay->ropacity[1] = 1-Oi[1]; cRay->ropacity[2] = 1-Oi[2]; } else { movvv(cRay->color,Ci); movvv(cRay->opacity,Oi); cRay->ropacity[0] = 1-Oi[0]; cRay->ropacity[1] = 1-Oi[1]; cRay->ropacity[2] = 1-Oi[2]; } opacity = (T32 *) Oi; if ( (opacity[0].integer ^ one.integer) | (opacity[1].integer ^ one.integer) | (opacity[2].integer ^ one.integer)) { rays[last++] = cRay; } else { movvv(cRay->samples,cRay->color); } cRay->samples[3] = (float) ((cRay->opacity[0] + cRay->opacity[1] + cRay->opacity[2])*0.333333333); cRay->samples[4] = cRay->t; } // Copy the extra samples } else { // Transparency hit for (i=0;iobject->attributes->flags & ATTRIBUTES_FLAGS_MATTE) { cRay->ropacity[0] *= 1-Oi[0]; cRay->ropacity[1] *= 1-Oi[1]; cRay->ropacity[2] *= 1-Oi[2]; } else { movvv(t,Oi); mulvv(Ci,cRay->ropacity); mulvv(Oi,cRay->ropacity); addvv(cRay->color,Ci); addvv(cRay->opacity,Oi); cRay->ropacity[0] *= 1-t[0]; cRay->ropacity[1] *= 1-t[1]; cRay->ropacity[2] *= 1-t[2]; } if (transparent) { rays[last++] = cRay; } else { movvv(cRay->samples,cRay->color); } cRay->samples[3] = (float) ((cRay->opacity[0] + cRay->opacity[1] + cRay->opacity[2])*0.333333333);//1; } // The extra samples are copied from the closest intersection } } /////////////////////////////////////////////////////////////////////// // Class : CRaytracer // Method : CRaytracer // Description : Ctor // Return Value : - // Comments : // Date last edited : 8/26/2001 void CPrimaryBundle::postShade(int nr,CRay **r) { int i; if (depth == 0) { for (i=0;isamples[0] = 0; cRay->samples[1] = 0; cRay->samples[2] = 0; cRay->samples[3] = 0; cRay->samples[4] = C_INFINITY; } } else { for (i=0;isamples,cRay->color); } } } /////////////////////////////////////////////////////////////////////// // Class : CRaytracer // Method : CRaytracer // Description : Ctor // Return Value : - // Comments : // Date last edited : 8/26/2001 void CPrimaryBundle::post() { numRays = last; last = 0; depth++; } /////////////////////////////////////////////////////////////////////// // Class : CRaytracer // Method : CRaytracer // Description : Ctor // Return Value : - // Comments : // Date last edited : 8/26/2001 CRaytracer::CRaytracer(COptions *o,CXform *x,SOCKET s) : CShadingContext(o,x,s,HIDER_NEEDS_RAYTRACING) , primaryBundle(o->shootStep,numSamples) { const int xoffset = (int) ceil((pixelFilterWidth - 1) / (float) 2); const int yoffset = (int) ceil((pixelFilterHeight - 1) / (float) 2); const int xpixels = bucketWidth + 2*xoffset; const int ypixels = bucketHeight + 2*yoffset; const int xsamples = bucketWidth*pixelXsamples + 2*((int) ceil((pixelFilterWidth - 1)*pixelXsamples / 2)); const int ysamples = bucketHeight*pixelYsamples + 2*((int) ceil((pixelFilterHeight - 1)*pixelYsamples / 2)); fbContribution = new float[xpixels*ypixels]; fbPixels = new float[xpixels*ypixels*numSamples]; sampleOrder = new int[xsamples*ysamples]; } /////////////////////////////////////////////////////////////////////// // Class : CRaytracer // Method : ~CRaytracer // Description : Dtor // Return Value : - // Comments : // Date last edited : 8/26/2001 CRaytracer::~CRaytracer() { delete [] fbContribution; delete [] fbPixels; delete [] sampleOrder; } /////////////////////////////////////////////////////////////////////// // Class : CRaytracer // Method : replace // Description : Replace the occurance of a pointer with another // Return Value : - // Comments : // Date last edited : 9/25/2001 void CRaytracer::renderFrame() { int left; int top; int width; int height; int numRenderedBuckets = 0; const char *previousActivity = stats.activity; stats.activity = "Raytracer - rendering"; memBegin(); if (netClient != INVALID_SOCKET) { // Let the client know that we're starting to render T32 netBuffer; // Let the server know that we're ready netBuffer.integer = NET_READY; rcSend(netClient,(char *) &netBuffer,1*sizeof(T32)); } // Do the job while(TRUE) { if (netClient != INVALID_SOCKET) { T32 netBuffer[3]; // Receive the bucket to render from the client rcRecv(netClient,(char *) netBuffer,3*sizeof(T32)); if (netBuffer[0].integer == NET_RENDER_BUCKET) { currentXBucket = netBuffer[1].integer; currentYBucket = netBuffer[2].integer; } else if (netBuffer[0].integer == NET_FINISH_FRAME) { // We have finished the frame, so terminate netBuffer[0].integer = NET_ACK; rcSend(netClient,(char *) netBuffer,1*sizeof(T32)); break; } else { error(CODE_BUG,"Unrecognised network request\n"); break; } } else { if (hiderFlags & HIDER_BREAK) break; } left = currentXBucket*bucketWidth; top = currentYBucket*bucketHeight; width = min(bucketWidth,xPixels-left); height = min(bucketHeight,yPixels-top); // Sample the framebuffer sample(left,top,width,height); // Flush the data to the out devices commit(left,top,width,height,fbPixels); // Advance the current bucket if (netClient == INVALID_SOCKET) { numRenderedBuckets++; advanceBucket(); } stats.progress = (numRenderedBuckets*100) / (float) (xBuckets*yBuckets); if (flags & OPTIONS_FLAGS_PROGRESS) info(CODE_PROGRESS,"Done %%%3.2f\r",stats.progress); } memEnd(); stats.progress = 100; if (flags & OPTIONS_FLAGS_PROGRESS) info(CODE_PROGRESS,"Done \r"); stats.activity = previousActivity; } /////////////////////////////////////////////////////////////////////// // Class : CRaytracer // Method : sample // Description : Samples a rectangular array of pixels // Return Value : - // Comments : // Date last edited : 9/25/2001 void CRaytracer::sample(int left,int top,int xpixels,int ypixels) { int maxShading = primaryBundle.maxPrimaryRays; int i,j; int k; const int xoffset = (int) ceil((pixelFilterWidth - 1)*pixelXsamples / (float) 2); const int yoffset = (int) ceil((pixelFilterHeight - 1)*pixelYsamples / (float) 2); const int xsamples = xpixels*pixelXsamples + 2*xoffset; const int ysamples = ypixels*pixelYsamples + 2*yoffset; CPrimaryRay *rays = primaryBundle.rayBase; CRay **rayPointers = primaryBundle.rays; CPrimaryRay *cRay; int maxSamples = pixelXsamples*pixelYsamples; const float invXsamples = 1 / (float) pixelXsamples; const float invYsamples = 1 / (float) pixelYsamples; // Clear the framebuffer for (i=0;i<(xpixels*ypixels);i++) { fbContribution[i] = 0; fbPixels[i] = 0; } for (;i<(xpixels*ypixels*numSamples);i++) { fbPixels[i] = 0; } // Generate the image { int numShading = 0; int x,y; cRay = rays; for (j=0;jx = (float) left + (float) (i+x-xoffset+jitter*(urand()-(float) 0.5) + (float) 0.5)*invXsamples; // Center the sample location in the pixel cRay->y = (float) top + (float) (j+y-yoffset+jitter*(urand()-(float) 0.5) + (float) 0.5)*invYsamples; rayPointers[numShading++] = cRay; cRay++; if (numShading >= maxShading) { computeSamples(rays,numShading); splatSamples(rays,numShading,left,top,xpixels,ypixels); cRay = rays; numShading = 0; } } } } } // Shade the leftover samples if (numShading > 0) { computeSamples(rays,numShading); splatSamples(rays,numShading,left,top,xpixels,ypixels); } } // At this point, fb should contain the framebuffer, normalize the framebuffer for (i=0;i0;i--,cRay++) { // First two numbers in the samples list are the x/y coordinates in pixels of the sample location const float jtime = urand(); vector from,to; x = cRay->x; y = cRay->y; pixels2camera(from,x,y,0); pixels2camera(to,x,y,imagePlane); movvv(cRay->from,from); subvv(cRay->dir,to,from); normalizev(cRay->dir); cRay->time = jtime; cRay->jimp = -1.0f; cRay->t = C_INFINITY; cRay->flags = ATTRIBUTES_FLAGS_PRIMARY_VISIBLE; cRay->tmin = 0; } } else { for (i=numShading;i>0;i--,cRay++) { // First two numbers in the samples list are the x/y coordinates in pixels of the sample location const float jtime = urand(); vector from,to; const float theta = (float) (urand()*2*C_PI); const float r = urand()*aperture; x = cRay->x; y = cRay->y; pixels2camera(from,x,y,0); pixels2camera(to,x,y,focaldistance); from[COMP_X] += cosf(theta) * r; from[COMP_Y] += sinf(theta) * r; movvv(cRay->from,from); subvv(cRay->dir,to,from); normalizev(cRay->dir); cRay->time = jtime; cRay->jimp = -1.0f; cRay->t = C_INFINITY; cRay->flags = ATTRIBUTES_FLAGS_PRIMARY_VISIBLE; cRay->tmin = 0; } } // Actually raytrace primaryBundle.numRays = numShading; primaryBundle.last = 0; primaryBundle.depth = 0; trace(&primaryBundle); stats.numShadingRays += numShading; } /////////////////////////////////////////////////////////////////////// // Class : CRaytracer // Method : splatSamples // Description : Splat the samples on the image // Return Value : - // Comments : // Date last edited : 9/25/2001 void CRaytracer::splatSamples(CPrimaryRay *samples,int numShading,int left,int top,int xpixels,int ypixels) { int i,j; const int pw = (int) ceil((pixelFilterWidth-1) / (float) 2); const int ph = (int) ceil((pixelFilterHeight-1) / (float) 2); for (i=0;ix; const float y = samples->y; float *fbs = samples->samples; int pixelX,pixelY; int ix = (int) floor(x); int iy = (int) floor(y); int pl = ix - pw; int pr = ix + pw; int pt = iy - ph; int pb = iy + ph; float cx,cy; pl = max(pl,left); pt = max(pt,top); pr = min(pr,left + xpixels - 1); pb = min(pb,top + ypixels - 1); for (cy=pt + (float) 0.5 - y,pixelY=pt;pixelY<=pb;pixelY++,cy++) { for (cx=pl + (float) 0.5 - x,pixelX=pl;pixelX<=pr;pixelX++,cx++) { float contribution = pixelFilter(cx,cy,pixelFilterWidth,pixelFilterHeight); if (fabs(cx) > marginX) contribution *= marginXcoverage; if (fabs(cy) > marginY) contribution *= marginYcoverage; float *dest = &fbPixels[((pixelY-top)*xpixels+pixelX-left)*this->numSamples]; const float *src = fbs; assert((top+ypixels) > pixelY); assert((left+xpixels) > pixelX); // Save the contribution for later normalization fbContribution[((pixelY-top)*xpixels+pixelX-left)] += contribution; for (j=this->numSamples;j>0;j--) { *dest++ += (*src++)*contribution; } } } } }