/// LSU EE 4702-1 (Fall 2024), GPU Programming // /// CPU-Only Rendering -- Ray Tracing // // Simple Illustration of Ray Tracing #include <gp/coord.h> #include <gp/colors.h> #include "frame_buffer.h" class World_RT : public World_Base { public: World_RT(pFrame_Buffer& fb):World_Base(fb){}; virtual void render(bool first_time); }; void World_RT::render(bool first_time) { /// /// Insert Triangles /// // // Note: Each triangle has three vertices and one color. // (In other code each triangle vertex can have its own color.) vector<pCoor> coors_os; // Triangles' object-space coordinates. vector<pColor> colors; // Triangles' colors. // Insert coordinates of first triangle, and the triangle's color. // coors_os << pCoor( 0, 0, 0 ) << pCoor( 9, 6, -9 ) << pCoor( 0, 7, -5 ); colors << color_white; // Add coordinates and color of another triangle. // coors_os << pCoor(7,4,-2) << pCoor(-3,5,-9) << pCoor(9,2,-2); colors << color_blue; // Add a square consisting of a red and green triangle. // colors << color_red << color_green; coors_os << pCoor(-4,0,-2) << pCoor(-4,2,-2) << pCoor(-2,0,-2); coors_os << pCoor(-4,2,-2) << pCoor(-2,2,-2) << pCoor(-2,0,-2); /// /// Prepare Transformations for User's Eye and "Monitor" /// // Transformations are from object space to clip space. // Location of User // if ( first_time ) eye_location = pCoor(1,.5,10.2); // Use keyboard to change eye location. // switch ( frame_buffer.keyboard_key ) { case FB_KEY_LEFT: eye_location.x += 0.1; break; case FB_KEY_RIGHT: eye_location.x -= 0.1; break; case FB_KEY_PAGE_UP: eye_location.y += 0.1; break; case FB_KEY_PAGE_DOWN: eye_location.y -= 0.1; break; case FB_KEY_UP: eye_location.z -= 0.1; break; case FB_KEY_DOWN: eye_location.z += 0.1; break; } pVect eye_direction = pVect(0,0,-1); /// /// Prepare Information on Light /// pVect eye_to_light = pVect(0,1,0); pCoor light_location = eye_location + eye_to_light; pColor light_color = color_white * 100; /// /// Prepare Transformations /// /// Specifications of User's Monitor // // First, the width of the user's monitor in object space coordinates. // const float width_m = 1.6; // Wow, that's a big monitor! (m is for meter.) const float qn = 1; // Distance from eye to monitor along z axis. // // To determine the height of the user's monitor we need to know the // aspect ratio of the window we are painting. // const uint win_width = frame_buffer.width_get(); const uint win_height = frame_buffer.height_get(); // // win_width and win_height are pixel-space coordinates. // const float aspect = 1.0 * win_width / win_height; // // Using the aspect ratio and the width we can compute the height. // const float height_m = width_m / aspect; // // width_m and height_m are in object-space coordinates. /// Specify Transformation from Object to Eye Space: eye_from_object. // pMatrix_Translate center_eye(-eye_location); pMatrix_Rotation rotate_eye(eye_direction,pVect(0,0,-1)); pMatrix eye_from_object = rotate_eye * center_eye; pCoor light_location_e = eye_from_object * light_location; // Convert triangle coordinates from object space to eye space. // vector<pCoor> coors_es = eye_from_object * coors_os; // Note: There is no need for clip-space coordinates. // Coordinates of Corners of User Monitor Window in Eye Space // pCoor window_ll_e( -width_m/2, -height_m/2, -qn); // Lower-Left pCoor window_ul_e( -width_m/2, height_m/2, -qn); // Upper-Left pCoor window_lr_e( +width_m/2, -height_m/2, -qn); // Lower-Right // Pixel size in eye-space coordinates. // pVect window_dx_e = pVect( window_ll_e, window_lr_e ) / win_width; pVect window_dy_e = pVect( window_ll_e, window_ul_e ) / win_height; pCoor eye_location_e(0,0,0); // Just here to help us understand. /// /// Render Pixels /// /// Outer Loops (yw, xw): Iterate over each window pixel. // for ( uint yw = 0; yw < win_height; yw++ ) for ( uint xw = 0; xw < win_width; xw++ ) { // Eye-Space Coordinate of Pixel. // pCoor px_e = window_ll_e + window_dx_e * xw + window_dy_e * yw; // Ray From Eye to Pixel. // pVect ray(eye_location_e,px_e); float tmin = 1e10; // Distance to closest triangle found so far. auto ic = colors.begin(); /// Inner Loop (it): Iterate over each triangle. // // Note: In practical RT would only need to look at a small // subset of triangles. // for ( auto it = coors_es.begin(); it != coors_es.end(); ) { // Get Triangle and its Color // pCoor e0 = *it++, e1 = *it++, e2 = *it++; pColor color = *ic++; // // Notice that coordinates are in eye space. // // // e0 Possible position of triangle vertices. // / \ It's also possible that e1 or e2 is the // / \ top-most vertex. // e1----e2 // Find where ray intercepts plane defined by triangle. // pVect v01(e0,e1), v02(e0,e2); pVect nt = cross(v01,v02); // Plane Normal float t = dot( pVect(eye_location_e,e0), nt ) / dot( ray, nt ); // // t indicates distance from eye to intercept point in // units of ray lengths. // Skip this triangle if a closer triangle already found .. // ..or if it is too close to the eye. // if ( t >= tmin || t < 1 ) continue; // Compute the coordinate of the line/plane intercept. // pCoor s = t * ray; // // Is s inside the triangle? We don't know yet. // Compute a barycentric coordinate (b1,b2) of s. // // Note: s = e0 + b1 v01 + b2 v02. // pVect v0s(e0,s); // // Note: v0s = b1 v01 + b2 v02. // Vector orthogonal to v01 (but not v02). // pVect Ov01 = cross(nt,v01); // // Note: dot( Ov01, v01 ) = 0, so: // // dot( Ov01,v0s ) = b1 dot( Ov01,v01 ) + b2 dot( Ov01,v02 ) // dot( Ov01,v0s ) = b2 dot( Ov01,v02 ) // float b2 = dot( v0s, Ov01 ) / dot( v02, Ov01 ); if ( b2 < 0 || b2 > 1 ) continue; // // Note: If b2 < 0 then s is on the wrong side of edge e0,e1. // If b2 > 1 then s is on the right side, but too far away. // Compute another barycentric coordinate of s. // float b1 = dot( pVect( v0s - b2 * v02 ), v01 ) / dot( v01, v01 ); if ( b1 < 0 || b1 + b2 > 1 ) continue; // At this point we know that s is in the triangle. // Update the minimum distance and write the frame buffer. // tmin = t; /// Compute Lighted Color // // Compute vector from fragment to light. // pNorm f_to_l(s,light_location_e); // Compute how much triangle is facing light. // float phase = fabs(dot(pNorm(nt),f_to_l)); // Using these, compute the lighted color. // pColor lighted_color = phase / f_to_l.mag_sq * light_color * color; frame_buffer[ xw + yw * win_width ] = lighted_color.int_rgb(); } } } int main(int argc, char **argv) { pFrame_Buffer demo_frame_buffer(argc,argv); World_RT worldrt(demo_frame_buffer); WRender render(demo_frame_buffer); WRENDER_INSERT(render, worldrt); render.run(); return 0; }