#include #include #include #include #include #include #include #include Scene::Scene( void ) { up = Vector3( 0.0, 1.0, 0.0 ); forward = Vector3( 0.0, 0.0, -1.0 ); eye = Vector3( 0.0, 0.0, 40.0 ); //up = Vector3( 0.0, 0.0, -1.0 ); //forward = Vector3( 0.0, -1.0, 0.0 ); //eye = Vector3( 0.0, 20.0, -20.0 ); right = forward.cross( up ); fov = 30.0; /** * fov Pi * --- * --- * 2 180 * ^--- get into radians * ^-- since we only want the top half thingy (want a right triangle) */ //screendist = cos( ( 0.5 * fov * 3.14159 ) / 180.0 ) * 60; screendist = 1.5 / tan( ( 0.5 * fov * 3.14159 ) / 180.0 ); viewtoworld.r0[ 0 ] = right.x; viewtoworld.r0[ 1 ] = right.y; viewtoworld.r0[ 2 ] = right.z; viewtoworld.r0[ 3 ] = eye.x; viewtoworld.r1[ 0 ] = up.x; viewtoworld.r1[ 1 ] = up.y; viewtoworld.r1[ 2 ] = up.z; viewtoworld.r1[ 3 ] = eye.y; viewtoworld.r2[ 0 ] = forward.x; viewtoworld.r2[ 1 ] = forward.y; viewtoworld.r2[ 2 ] = forward.z; viewtoworld.r2[ 3 ] = eye.z; Sphere *s = new Sphere( Vector3( -5.0, 0.0, -20.0 ), 4.0 ); s->setColour( Pixel( 1.0, 0.0, 0.0 ) ); objects.push_back( s ); s = new Sphere( Vector3( 0.0, 8.0, -20.0 ), 4.0 ); s->setColour( Pixel( 0.0, 1.0, 0.0 ) ); objects.push_back( s ); s = new Sphere( Vector3( 16.0, -2.0, -20.0 ), 4.0 ); s->setColour( Pixel( 0.3, 0.8, 1.0 ) ); s->setReflectivity( 0.5 ); objects.push_back( s ); Plane *p = new Plane( Vector3( 0.0, -7.0, 0.0 ), Vector3( 0.0, 1.0, 0.0 ) ); p->setColour( Pixel( 0.2, 0.2, 0.2 ) ); p->setReflectivity( 0.7 ); objects.push_back( p ); p = new Plane( Vector3( 0.0, 0.0, -100.0 ), Vector3( 0.0, 0.0, 1.0 ) ); p->setColour( Pixel( 0.0, 0.0, 0.2 ) ); p->setReflectivity( 0.7 ); objects.push_back( p ); PointLight *l = new PointLight( Vector3( 0.0, 16.0, -20.0 ) ); lights.push_back( l ); l = new PointLight( Vector3( -10.0, 4.0, -20.0 ) ); lights.push_back( l ); } Scene::~Scene( void ) { } Pixel Scene::lightPositionPhong( Vector3 pos, Vector3 norm, BaseObject *object ) { vector::iterator lit; vector::iterator it; Pixel result = Pixel::black; for( lit = lights.begin(); lit != lights.end(); lit++ ) { Vector3 tolight = (*lit)->pos.subtract( pos ); double lightlen = tolight.length(); bool shadow = false; for( it = objects.begin(); it != objects.end(); it++ ) { Vector3 curpos; Vector3 curnorm; Vector3 offpoint = pos.add( tolight.scalarMult( 0.00001 ) ); if( (*it)->closestIntersect( &curpos, &curnorm, offpoint, tolight.normalize() ) ) { double curlen = curpos.subtract( pos ).length(); if( curlen < lightlen ) { shadow = true; break; } } } if( shadow ) continue; Vector3 normnorm = norm.normalize(); Vector3 lightnorm = tolight.normalize(); Vector3 reflection = normnorm.scalarMult( 2 * normnorm.dotProduct( lightnorm ) ); Vector3 toeye = eye.subtract( pos ).normalize(); reflection = reflection.subtract( lightnorm ).normalize(); // I want to calculate... double dot = reflection.dotProduct( toeye ); if( dot < 0.0 ) dot = 0.0; dot = pow( dot, 300.0 ); double ldot = norm.normalize().dotProduct( tolight.normalize() ); if( ldot < 0.0 ) ldot = 0.0; result = result.add( object->getColour().multiply( (*lit)->colour ).scalarMult( ldot ) ); result = result.add( (*lit)->colour.multiply( (*lit)->colour ).scalarMult( dot ) ); } /* Add ambient light. */ result = result.add( object->getColour().scalarMult( 0.05 ) ); return result; } bool Scene::traceRay( Vector3 *intersect, Vector3 *normal, BaseObject **object, double *reflectivity, Vector3 *reflectionpos, Vector3 *reflectiondir, Vector3 origin, Vector3 direction ) { /* We have to find the first intersection. */ BaseObject *nearestobj = 0; Vector3 nearestpos; Vector3 nearestnorm; double bestlength = 0.0; vector::iterator it; for( it = objects.begin(); it != objects.end(); it++ ) { Vector3 curpos; Vector3 curnorm; if( (*it)->closestIntersect( &curpos, &curnorm, origin, direction ) ) { if( !nearestobj || curpos.subtract( origin ).length() < bestlength ) { bestlength = curpos.subtract( origin ).length(); nearestpos = curpos; nearestnorm = curnorm; nearestobj = (*it); } } } if( !nearestobj ) return false; *intersect = nearestpos; *normal = nearestnorm; *object = nearestobj; *reflectivity = nearestobj->getReflectivity(); Vector3 normnorm = nearestnorm.normalize(); Vector3 toorigin = origin.subtract( nearestpos ).normalize(); *reflectiondir = normnorm.scalarMult( 2 * normnorm.dotProduct( toorigin ) ); *reflectiondir = (*reflectiondir).subtract( toorigin ).normalize(); *reflectionpos = nearestpos; return true; } Pixel Scene::pointSample( double x, double y ) { int i; /* Our output pixel starts at black. */ Pixel result = Pixel::black; /* The point we're sampling in view coordinates. */ Vector3 destinview( ( x - 0.5 ) * 4, 3 * ( 0.5 - y ), screendist ); /* Calculate the ray direction from the eye in world space. */ Vector3 raydest = multiplyVectorByMatrix( viewtoworld, destinview ); Vector3 raydirection = raydest.subtract( eye ).normalize(); /* Trace a ray! */ Vector3 curpos = eye; Vector3 curdir = raydirection; for( i = 0; i < 20; i++ ) { Vector3 intersect; Vector3 normal; BaseObject *object; Vector3 reflectionpos; Vector3 reflectiondir; double reflectivity; if( traceRay( &intersect, &normal, &object, &reflectivity, &reflectionpos, &reflectiondir, curpos, curdir ) ) { Pixel curcolour = lightPositionPhong( intersect, normal, object ); result = result.add( curcolour.scalarMult( 1.0 - reflectivity ) ); } else { break; } if( threshEqual( reflectivity, 0.0, 0.01 ) ) break; curpos = reflectionpos.add( reflectiondir.scalarMult( 0.00001 ) ); curdir = reflectiondir; } return result; } Pixel Scene::adaptivePoint( double x, double y, int depth, double w, double h, int maxdepth ) { if( depth > maxdepth ) return pointSample( x / w, y / h ); Pixel result = Pixel::black; double left = x - ( 1.0 / ( 2 * depth ) ); double right = x + ( 1.0 / ( 2 * depth ) ); double top = y + ( 1.0 / ( 2 * depth ) ); double bot = y - ( 1.0 / ( 2 * depth ) ); Pixel topleft = pointSample( left / w, top / h ); Pixel topright = pointSample( right / w, top / h ); Pixel botleft = pointSample( left / w, bot / h ); Pixel botright = pointSample( right / w, bot / h ); Pixel average = topleft.add( topright.add( botleft.add( botright ) ) ); average = average.scalarMult( 0.25 ); if( topleft.subtract( average ).intensity() > 0.002 || topright.subtract( average ).intensity() > 0.002 || botleft.subtract( average ).intensity() > 0.002 || botright.subtract( average ).intensity() > 0.002 ) { result = result.add( adaptivePoint( left + ( ( x - left ) / 2.0 ), y + ( ( top - y ) / 2.0 ), depth + 1, w, h, maxdepth ) ); result = result.add( adaptivePoint( x + ( ( right - x ) / 2.0 ), y + ( ( top - y ) / 2.0 ), depth + 1, w, h, maxdepth ) ); result = result.add( adaptivePoint( left + ( ( x - left ) / 2.0 ), y - ( ( top - y ) / 2.0 ), depth + 1, w, h, maxdepth ) ); result = result.add( adaptivePoint( x + ( ( right - x ) / 2.0 ), y - ( ( top - y ) / 2.0 ), depth + 1, w, h, maxdepth ) ); result = result.scalarMult( 0.25 ); } else { result = average; } return result; } Pixel Scene::superPoint( double x, double y, int depth, double w, double h, int maxdepth ) { if( depth > maxdepth ) return pointSample( x / w, y / h ); Pixel result = Pixel::black; double left = x - ( 1.0 / ( 2 * depth ) ); double right = x + ( 1.0 / ( 2 * depth ) ); double top = y + ( 1.0 / ( 2 * depth ) ); result = result.add( superPoint( left + ( ( x - left ) / 2.0 ), y + ( ( top - y ) / 2.0 ), depth + 1, w, h, maxdepth ) ); result = result.add( superPoint( x + ( ( right - x ) / 2.0 ), y + ( ( top - y ) / 2.0 ), depth + 1, w, h, maxdepth ) ); result = result.add( superPoint( left + ( ( x - left ) / 2.0 ), y - ( ( top - y ) / 2.0 ), depth + 1, w, h, maxdepth ) ); result = result.add( superPoint( x + ( ( right - x ) / 2.0 ), y - ( ( top - y ) / 2.0 ), depth + 1, w, h, maxdepth ) ); result = result.scalarMult( 0.25 ); return result; } void Scene::adaptiveSample( Pixel *output, int w, int h, int depth ) { int x, y; for( y = 0; y < h; y++ ) { fprintf( stderr, "At scanline %3d.\r", y ); for( x = 0; x < w; x++ ) { *output++ = adaptivePoint( x, y, 1, w, h, depth ); } } fprintf( stderr, "\n" ); } void Scene::superSample( Pixel *output, int w, int h, int depth ) { int x, y; for( y = 0; y < h; y++ ) { fprintf( stderr, "At scanline %3d.\r", y ); for( x = 0; x < w; x++ ) { *output++ = superPoint( x, y, 1, w, h, depth ); } } fprintf( stderr, "\n" ); } void Scene::pointSample( Pixel *output, int w, int h ) { int x, y; for( y = 0; y < h; y++ ) { fprintf( stderr, "At scanline %3d.\r", y ); for( x = 0; x < w; x++ ) { *output++ = pointSample( (double) x / (double) w, (double) y / (double) h ); } } fprintf( stderr, "\n" ); }