//------------------------------------------------------------------- // newglobe.cpp // // This program demonstrates the rudiments of ray-tracing by // rendering a scene which contains a sphere and a tabletop. // It revises our 'globe.cpp' by omitting unneeded elements. // // compile using: $ g++ newglobe.cpp int86.cpp -o newglobe // // programmer: ALLAN CRUSE // written on: 19 DEC 2005 //------------------------------------------------------------------- #include // for printf(), perror() #include // for open() #include // for exit() #include // for lseek() #include // for sqrt(), fabs() #include // for mmap() #include // for inb(), outb(), etc. #include "int86.h" // for init8086(), int86() unsigned char *vram = (unsigned char*)0xA0000000; int vesa_mode = 0x4103, hres = 800, vres = 600; struct vm86plus_struct vm; // function prototypes void program_color_registers( void ); void display_raytraced_scene( void ); void enter_the_graphics_mode( void ); void leave_the_graphics_mode( void ); void mmap_the_display_memory( void ); void draw_a_border_rectangle( void ); int main( void ) { mmap_the_display_memory(); enter_the_graphics_mode(); draw_a_border_rectangle(); program_color_registers(); display_raytraced_scene(); leave_the_graphics_mode(); } void mmap_the_display_memory( void ) { // memory-map the display-memory into user-space int vd = open( "/dev/vram", O_RDWR ); if ( vd < 0 ) { perror( "/dev/vram" ); exit(1); } int size = lseek( vd, 0, SEEK_END ); int prot = PROT_READ | PROT_WRITE; int flag = MAP_FIXED | MAP_SHARED; if ( mmap( (void*)vram, size, prot, flag, vd, 0 ) == MAP_FAILED ) { perror( "mmap" ); exit(1); } } void program_color_registers( void ) { int min_index = 192, min_color = 192; for (int i = 0; i < 64; i++) { outb( min_index+i, 0x3C8 ); outb( min_color+i, 0x3C9 ); outb( min_color+i, 0x3C9 ); outb( min_color+i, 0x3C9 ); } } void enter_the_graphics_mode( void ) { init8086(); vm.regs.eax = 0x4F02; vm.regs.ebx = vesa_mode; int86( 0x10, vm ); } void draw_a_border_rectangle( void ) { int x = 0, y = 0, color = 15; do { vram[ y * hres + x ] = color; ++x; } while ( x < hres-1 ); do { vram[ y * hres + x ] = color; ++y; } while ( y < vres-1 ); do { vram[ y * hres + x ] = color; --x; } while ( x > 0 ); do { vram[ y * hres + x ] = color; --y; } while ( y > 0 ); getchar(); } void leave_the_graphics_mode( void ) { vm.regs.eax = 0x0003; int86( 0x10, vm ); printf( "\n" ); } //------------ OUR CODE FOR THE RAY-TRACING BEGINS HERE ------------ // the types for our data objects #define MAXVERT 8 typedef float scalar_t; typedef struct { scalar_t x, y, z; } vector_t; typedef struct { vector_t origin, toward; } ray_t; typedef struct { vector_t base, perp; } plane_t; typedef struct { vector_t center; scalar_t radius; } sphere_t; typedef struct { int numverts; vector_t vert[ MAXVERT ]; } poly_t; // our scene's data objects vector_t eye = { 0.0, 0.0, 18.0 }; sphere_t globe = { { 0.0, 0.0, 0.0 }, 1.0 }; plane_t plane = { { 0.0, -1.0, 0.0 }, { 0.0, 1.0, 0.0 } }; vector_t light = { 6.0, 8.0, 8.0 }; poly_t table = { 4, { { 3.5, -1.0, 3.5 }, { -3.5, -1.0, 3.5 }, { -3.5, -1.0, -3.5 }, { 3.5, -1.0, -3.5 } } }; // our scene's illumination parameters scalar_t Iambient = 15.0; scalar_t Idiffuse = 24.0; scalar_t Ispecular = 24.0; // parameters for mapping the view to the device scalar_t transX = hres/2, transY = vres/2; scalar_t scaleX = vres/8, scaleY = -vres/8; void normalize( vector_t &v ) { double norm = sqrt( v.x * v.x + v.y * v.y + v.z * v.z ); if ( norm > 0.0 ) { v.x /= norm; v.y /= norm; v.z /= norm; } } void vector_fromto( vector_t from, vector_t to, vector_t &v ) { v.x = to.x - from.x; v.y = to.y - from.y; v.z = to.z - from.z; } scalar_t dot_product( vector_t a, vector_t b ) { return a.x * b.x + a.y * b.y + a.z * b.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 via the formula: // c = a - / b scalar_t num = dot_product( a, b ); scalar_t den = dot_product( b, b ); if ( den == 0 ) return; scalar_t mul = num / den; c.x = a.x - b.x * mul; c.y = a.y - b.y * mul; c.z = a.z - b.z * mul; } scalar_t det( vector_t a, vector_t b, vector_t c ) { scalar_t sum = 0.0; sum += a.x * b.y * c.z; sum += a.y * b.z * c.x; sum += a.z * b.x * c.y; sum -= a.x * b.z * c.y; sum -= a.z * b.y * c.x; sum -= a.y * b.x * c.z; return sum; } int point_in_polygon( vector_t spot, poly_t polygon ) { vector_t s = spot; vector_t a = polygon.vert[ 0 ]; for (int i = 1; i < polygon.numverts-1; i++) { vector_t b = polygon.vert[ i ]; vector_t c = polygon.vert[ i+1 ]; scalar_t den = det( a, b, c ); if ( den == 0.0 ) continue; scalar_t c1 = det( s, b, c ); scalar_t c2 = det( a, s, c ); scalar_t c3 = det( a, b, s ); if (( c1 <= 0.0 )||( c2 <= 0.0 )||( c3 <= 0.0 )) continue; return 1; } return 0; } int 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 bet\ween 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 ); // compute cosine of angle between rr and se (for specular component) scalar_t dot_re = dot_product( rr, se ); // compute cosine power scalar_t cosine_power = 1.0; cosine_power *= dot_re; cosine_power *= dot_re; cosine_power *= dot_re; cosine_power *= dot_re; cosine_power *= dot_re; // compute the illunination intensity scalar_t intensity = Iambient; if ( dot_sn > 0.0 ) intensity += Idiffuse * dot_sn; if ( dot_re > 0.0 ) intensity += Ispecular * cosine_power; return (int)intensity; } int ray_hits_sphere( ray_t ray, sphere_t sphere, vector_t &spot ) { // returns 'true' if the ray lits the sphere, and // sets 'spot' to the position of the 'hit-point' // otherwise returns 'false' // compute vector ec from ray's origin 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.toward, pp ); // ok, find the spot where the ray first hits the globe scalar_t dot_dd = dot_product( ray.toward, ray.toward ); scalar_t dot_dn = dot_product( ray.toward, ec ); scalar_t dot_nn = dot_product( ec, ec ); scalar_t r_squared = sphere.radius * sphere.radius; if ( dot_product( pp, pp ) > r_squared ) return 0; 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 = ray.origin.x + ray.toward.x * t_hit; spot.y = ray.origin.y + ray.toward.y * t_hit; spot.z = ray.origin.z + ray.toward.z * t_hit; return 1; } int ray_hits_table( ray_t ray, plane_t plane, poly_t table, vector_t &spot ) { vector_t v; // vector from eye to plane's base vector_t d; // vector from eye in ray's direction vector_t n; // vector normasl to the plane vector_fromto( ray.origin, plane.base, v ); d = ray.toward; n = plane.perp; scalar_t dot_vn = dot_product( v, n ); scalar_t dot_dn = dot_product( d, n ); if ( dot_vn * dot_dn <= 0.0 ) return 0; // ok, find the spot where the ray hits the table scalar_t t_hit = dot_vn / dot_dn; spot.x = ray.origin.x + ray.toward.x * t_hit; spot.y = ray.origin.y + ray.toward.y * t_hit; spot.z = ray.origin.z + ray.toward.z * t_hit; // then verify that spot is within the table's bounds return point_in_polygon( spot, table ); } int beam_hits_globe( vector_t light, vector_t spot, sphere_t globe ) { ray_t beam; beam.origin = light; vector_fromto( light, spot, beam.toward ); vector_t locn; return ray_hits_sphere( beam, globe, locn ); } int globe_color( vector_t light, vector_t spot, sphere_t globe ) { // compute unit-vector nn normal to globe at hit-spot vector_t nn; vector_fromto( globe.center, spot, nn ); normalize( nn ); return 192 + light_intensity( light, spot, nn ); } int table_color( vector_t light, vector_t spot, sphere_t globe, plane_t plane ) { if ( beam_hits_globe( light, spot, globe ) ) return 192 + (int)Iambient; // compute unit-vector nn normal to table at hit-spot vector_t nn; nn = plane.perp; normalize( nn ); return 192 + light_intensity( light, spot, nn ); } int empty_color( void ) { return 0; } int 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, plane, table, spot ) ) return table_color( light, spot, globe, plane ); else return empty_color(); } void device_to_world( int x, int y, vector_t &pixel ) { pixel.x = ( x - transX )/scaleX; pixel.y = ( y - transY )/scaleY; pixel.z = 0; } void draw_pixel( int x, int y, int color ) { vram[ y * hres + x ] = color; } void display_raytraced_scene( void ) { for (int v = 0; v < vres; v++) for (int h = 0; h < hres; h++) { // locate the pixel in world coordinates vector_t pixel; device_to_world( h, v, pixel ); // construct a ray from eye toward pixel ray_t ray; ray.origin = eye; vector_fromto( eye, pixel, ray.toward ); // illuminate the pixel int color = pixel_color( ray ); draw_pixel( h, v, color ); } getchar(); }