//------------------------------------------------------------------- // globe.cpp (monochromatic version) // // This program demonstrates the rudiments of ray-tracing by // rendering a scene which contains a sphere and a tabletop. // // programmer: ALLAN CRUSE // date begun: 18 NOV 2003 // completion: 19 NOV 2003 // correction: 09 DEC 2003 (fixed line 246, thanks to Kai Long) //------------------------------------------------------------------- #include // for printf(), perror() #include // for open() #include // for exit() #include // for read(), write(), close() #include // for tcgetattr(), tcsetattr() #include // for sqrt(), fabs() #include // for mmap() #include // for iopl() #define VRAM_BASE_ADDRESS 0xA0000000 #define VGA_MEMORY_LENGTH ( 4 << 20) typedef unsigned char PEL; unsigned char *vram = (unsigned char*)VRAM_BASE_ADDRESS; const int display_mode = 0x4103, hres = 800, vres = 600; // function prototypes void setup_object_parameters( void ); void setup_color_palettes( void ); void render_the_scene( void ); void draw_rectangle( int x, int y, int h, int v, int color ) { int minx = x, miny = y, maxx = x+h-1, maxy = y+v-1; do { vram[ y*hres + x ] = color; ++x; } while ( x < maxx ); do { vram[ y*hres + x ] = color; ++y; } while ( y < maxy ); do { vram[ y*hres + x ] = color; --x; } while ( x > minx ); do { vram[ y*hres + x ] = color; --y; } while ( y > miny ); } int map_system_memory( int base, int size ) { int fd = open( "/dev/vram", O_RDWR ); if ( fd < 0 ) { perror( "/dev/vram" ); return -1; } int prot = PROT_READ | PROT_WRITE; int flag = MAP_FIXED | MAP_SHARED; void *mm = (void*)base; if ( mmap( mm, size, prot, flag, fd, 0 ) == MAP_FAILED ) { perror( "mmap" ); return -1; } close( fd ); } int main( int argc, char **argv ) { if ( iopl( 3 ) ) { perror( "iopl" ); exit(1); } system( "/sbin/insmod vram.o" ); map_system_memory( VRAM_BASE_ADDRESS, VGA_MEMORY_LENGTH ); struct termios otty; tcgetattr( 0, &otty ); struct termios tty = otty; tty.c_lflag &= ~( ICANON | ECHO | ISIG ); tty.c_cc[ VMIN ] = 1; tty.c_cc[ VTIME ] = 0; tcsetattr( 0, TCSAFLUSH, &tty ); char command[ 40 ]; sprintf( command, " clear ; mode3 %d ", display_mode ); system( command ); int color = 15; draw_rectangle( 0, 0, hres, vres, color ); getchar(); // draw the raytraced scene setup_object_parameters(); setup_color_palettes(); render_the_scene(); getchar(); system( " mode3 3 " ); tcsetattr( 0, TCSAFLUSH, &otty ); } //--------------- CODE FOR RAYTRACING BEGINS HERE --------------- // manifest constants #define MAXVERT 8 // type definitions typedef float scalar_t; typedef struct { scalar_t x, y, z; } vector_t; typedef struct { vector_t origin, direction; } ray_t; typedef struct { int numverts; vector_t vert[ MAXVERT ]; } polygon_t; typedef struct { scalar_t radius; vector_t center; vector_t color; } sphere_t; typedef struct { vector_t base; vector_t perp; vector_t color; } plane_t; // screen-placement parameters scalar_t transX = hres/2; scalar_t transY = vres/2; scalar_t scaleX = 0.8; scalar_t scaleY = -0.8; // illumination coefficients scalar_t Iambient = 16.0; scalar_t Idiffuse = 24.0; scalar_t Ispecular = 24.0; // static variables vector_t eye; plane_t table; sphere_t globe; polygon_t poly; vector_t light; void setup_object_parameters( void ) { // initialize our sphere vector_t white = { 100.0, 100.0, 100.0 }; vector_t origin = { 0.0, 0.0, 0.0 }; globe.radius = 100.0; globe.center = origin; globe.color = white; // initialize our polygon vector_t vertex = { 335.0, -100.0, 335.0 }; poly.numverts = 4; poly.vert[0] = vertex; vertex.x = -vertex.x; poly.vert[1] = vertex; vertex.z = -vertex.z; poly.vert[2] = vertex; vertex.x = -vertex.x; poly.vert[3] = vertex; vertex.z = -vertex.z; poly.vert[4] = vertex; // initialize our plane vertex.x = 0.0; vertex.z = 0.0; table.base = vertex; vertex.y = 1.0; table.perp = vertex; table.color = white; // initialize our light-source light.x = 600.0; light.y = 800.0; light.z = -800.0; // set location of viewer's eye eye.x = 0.0; eye.y = 0.0; eye.z = -1800.0; } void setup_color_palettes( void ) { int port = 192; for (int i = 0; i < 64; i++) { outb( port+i, 0x3C8 ); outb( port+i, 0x3C9 ); outb( port+i, 0x3C9 ); outb( port+i, 0x3C9 ); } } scalar_t dot_product( vector_t p, vector_t q ) { return p.x*q.x + p.y*q.y + p.z*q.z; } scalar_t vector_norm( vector_t v ) { return sqrt( v.x*v.x + v.y*v.y + v.z*v.z ); } void normalize( vector_t &v ) { scalar_t norm = sqrt( v.x*v.x + v.y*v.y + v.z*v.z ); v.x /= norm; v.y /= norm; v.z /= norm; } void vector_fromto( vector_t from, vector_t to, vector_t &toward ) { toward.x = to.x - from.x; toward.y = to.y - from.y; toward.z = to.z - from.z; } void partof_perpto( vector_t a, vector_t b, vector_t &c ) { // this function computes the component of vector a // that is orthogonal to vector b using the formula: // c = a - / b scalar_t scalar_num = dot_product( a, b ); scalar_t scalar_den = dot_product( b, b ); if ( scalar_den == 0.0 ) return; scalar_t scalar_mul = scalar_num / scalar_den; c.x = a.x - b.x * scalar_mul; c.y = a.y - b.y * scalar_mul; c.z = a.z - b.z * scalar_mul; } int ray_hits_sphere( ray_t ray, sphere_t sphere, vector_t &spot ) { // returns 'true' if the ray hits the sphere, and // sets 'spot' to the position of the 'hit-point' // otherwise, returns 'false' // compute vector ec from eye to sphere's center vector_t ec; vector_fromto( ray.origin, sphere.center, ec ); // compute the part of ec that is perpendicular to the ray vector_t pp; partof_perpto( ec, ray.direction, pp ); if ( vector_norm( pp ) > globe.radius ) return 0; // ok, find the spot where the ray first hits the globe scalar_t dot_dd = dot_product( ray.direction, ray.direction ); scalar_t dot_dn = dot_product( ray.direction, ec ); scalar_t dot_nn = dot_product( ec, ec ); scalar_t r_squared = globe.radius * globe.radius; //typo-> scalar_t rad = (dot_nn*dot_nn) - dot_dd*(dot_nn - r_squared); scalar_t rad = (dot_dn*dot_dn) - dot_dd*(dot_nn - r_squared); scalar_t t_hit = ( dot_dn - sqrt( rad ) )/dot_dd; spot.x = eye.x + ray.direction.x * t_hit; spot.y = eye.y + ray.direction.y * t_hit; spot.z = eye.z + ray.direction.z * t_hit; return 1; } int ray_hits_table( ray_t ray, plane_t table, polygon_t poly, vector_t &spot ) { vector_t v; // vector from eye to table's base vector_t d; // vector from eye in ray's direction vector_t n; // vector normal to the table vector_fromto( eye, table.base, v ); d = ray.direction; n = table.perp; scalar_t dot_vn = dot_product( v, n ); scalar_t dot_dn = dot_product( d, n ); if ( dot_dn * dot_vn <= 0.0 ) return 0; // ok, find the spot where the ray hits the table scalar_t t_hit = dot_vn / dot_dn; spot.x = eye.x + ray.direction.x * t_hit; spot.y = eye.y + ray.direction.y * t_hit; spot.z = eye.z + ray.direction.z * t_hit; // then verify that spot is within table's bounds if ( fabs( spot.x ) > poly.vert[0].x ) return 0; if ( fabs( spot.z ) > poly.vert[0].z ) return 0; return 1; } PEL light_intensity( vector_t light, vector_t spot, vector_t nn ) { vector_t se; // unit-vector se from spot to eye vector_fromto( spot, eye, se ); normalize( se ); vector_t ss; // unit-vector ss from spot to light-source vector_fromto( spot, light, ss ); normalize( ss ); // compute cosine of angle between ss and nn (for diffuse component) scalar_t dot_sn = dot_product( ss, nn ); // find the reflection rr of ss about nn vector_t rr; rr.x = -ss.x + 2*( nn.x * dot_sn ); rr.y = -ss.y + 2*( nn.y * dot_sn ); rr.z = -ss.z + 2*( nn.z * dot_sn ); normalize( rr ); // compute cosine of angle between rr and se (for specular component) scalar_t dot_re = dot_product( rr, se ); // calculate the intensities scalar_t cos_power = 1.0; cos_power *= dot_re; cos_power *= dot_re; cos_power *= dot_re; cos_power *= dot_re; cos_power *= dot_re; scalar_t intensity = Iambient; if ( dot_sn > 0.0 ) intensity += Idiffuse * dot_sn; if ( dot_re > 0.0 ) intensity += Ispecular * cos_power; return (PEL)intensity; } PEL globe_color( vector_t light, vector_t spot, sphere_t globe ) { // compute unit-vector nn normal to globe at hit-spot vector_t nn; // unit-vector nn normal to globe at spot vector_fromto( globe.center, spot, nn ); normalize( nn ); return 192 + light_intensity( light, spot, nn ); } int beam_hits_globe( vector_t light, vector_t spot, sphere_t globe ) { ray_t beam; beam.origin = light; vector_fromto( light, spot, beam.direction ); vector_t locn; return ray_hits_sphere( beam, globe, locn ); } PEL table_color( vector_t light, vector_t spot, sphere_t globe, plane_t table ) { if ( beam_hits_globe( light, spot, globe ) ) return 192 + (PEL)Iambient; // compute unit-vector nn normal to table at hit-spot vector_t nn; // unit-vector nn normal to table at spot nn = table.perp; normalize( nn ); return 192 + light_intensity( light, spot, nn ); } PEL empty_color( void ) { return 0; } PEL pixel_color( ray_t ray ) { vector_t spot; if ( ray_hits_sphere( ray, globe, spot ) ) return globe_color( light, spot, globe ); else if ( ray_hits_table( ray, table, poly, spot ) ) return table_color( light, spot, globe, table ); else return empty_color(); } void render_the_scene( void ) { for (int v = 0; v < vres; v++) for (int h = 0; h < hres; h++) { vector_t pixel; // device coordinates pixel.x = (float)h; pixel.y = (float)v; pixel.z = (float)0; // transform pixel to world coordinates pixel.x -= hres/2; pixel.y -= vres/2; pixel.x /= scaleX; pixel.y /= scaleY; // construct ray from eye toward pixel ray_t ray; ray.origin = eye; vector_fromto( eye, pixel, ray.direction ); // compute the pixel-color PEL color = pixel_color( ray ); vram[ v * hres + h ] = color; } }