/// LSU EE 7700-2 (Sp 08), Graphics Processors // /// Homework 3 -- SOLUTION /// Name: David Koppelman // $Id:$ /// Problem 1 // Assignment details are given in the homework handout, // http://www.ece.lsu.edu/koppel/gp/2008/hw03.pdf // For the solution one probably needs to refer to the OpenGL spec: // http://www.ece.lsu.edu/koppel/gp/refs/glspec21.pdf // Very Briefly: Make the "t" command described below work without // breaking "v" command: // Some User Interface Commands // // v: Vertex Buffering // Change the way vertices are passed to OpenGL. // See uses of opt_v_buffering. // Note: The "Buffer Object" (2) setting won't work on some GPUs. // // r: Vertex Re-computation // Force unnecessary recomputation of vertices, perhaps to help // in finding performance bottlenecks. // // t: Triangle Specification: Individual or Strips // Changes variable opt_use_triangle_strips. // In homework template individual triangles are used // regardless of opt_use_triangle_strips value. In solved // assignment it should actually change how triangles are specified. /// Solution Hints // This code is admittedly a bit turgid. // Start by examining the code near " switch ( opt_v_buffering ) ", // this is where GL_TRIANGLE_STRIP will have to be used. // Next examine the loop nest that builds the vertex array, after // comment " Outer Loop: z axis (down axis of tube)." That code will // have to be modified so that the vertex array (coor_buffer) is // appropriate for triangle strips rather than individual triangles. // A correct solution is possible by only: // o A modification to the value of vertices_per_ring (it will depend // on opt_use_triangle_strips). // Just one line here. // o A single added line to the loop nest (after comment "Outer Loop"). // o Most code needs to be added to the switch statement. /// SOLUTION // Note: This solution is not exactly correct because the appearance // does change slightly. However, there is enough similarity so that // performance comparisons are valid. // Search for the word SOLUTION to find the changes or see // the diff below: #if 0 @@ -395,7 +396,6 @@ Tube::render() const int pattern_width = 3 * int( opt_pattern_width * 0.33333333 ); const int pattern_levels = int( opt_pattern_levels + 0.5 ); - // const int vertices_per_ring = 3 * 2 * pattern_width; // SOLUTION // Number of vertices passed to OpenGL. const int vertices_per_ring = @@ -554,8 +554,27 @@ Tube::render() // Individual Vertex Specification (Buffering) // { + int idx_v = phase_v; const int end_v = phase_v + num_v; + // SOLUTION + if ( opt_use_triangle_strips ) + { + while ( idx_v < end_v ) + { + glBegin(GL_TRIANGLE_STRIP); + for ( int c=0; c<vertices_per_ring; c++ ) + { + glNormal3fv(norm_buffer[idx_v]); + glVertex3fv(coor_buffer[idx_v]); + num_bytes += sizeof(float) * 6; + idx_v++; + } + glEnd(); + } + break; + } + glBegin(GL_TRIANGLES); for ( int i=phase_v; i<end_v; i++ ) { @@ -577,7 +596,19 @@ Tube::render() glVertexPointer(3,GL_FLOAT,sizeof(pCoor),coor_buffer); glEnableClientState(GL_NORMAL_ARRAY); glEnableClientState(GL_VERTEX_ARRAY); - glDrawArrays(GL_TRIANGLES,phase_v,num_v); + + // SOLUTION + if ( opt_use_triangle_strips ) + { + const int end_v = phase_v + num_v; + for( int idx_v = phase_v; idx_v < end_v; idx_v += vertices_per_ring ) + glDrawArrays(GL_TRIANGLE_STRIP,idx_v,vertices_per_ring); + } + else + { + glDrawArrays(GL_TRIANGLES,phase_v,num_v); + } + glDisableClientState(GL_NORMAL_ARRAY); glDisableClientState(GL_VERTEX_ARRAY); num_bytes += sizeof(float) * 6 * num_v; @@ -599,7 +630,21 @@ Tube::render() glBindBuffer(GL_ARRAY_BUFFER,0); glEnableClientState(GL_NORMAL_ARRAY); glEnableClientState(GL_VERTEX_ARRAY); - glDrawArrays(GL_TRIANGLES,phase_v,num_v); + + // SOLUTION + if ( opt_use_triangle_strips ) + { + const int end_v = phase_v + num_v; + for( int idx_v = phase_v; idx_v < end_v; idx_v += vertices_per_ring ) + glDrawArrays(GL_TRIANGLE_STRIP,idx_v,vertices_per_ring); + } + else + { + glDrawArrays(GL_TRIANGLES,phase_v,num_v); + } + glDisableClientState(GL_NORMAL_ARRAY); glDisableClientState(GL_VERTEX_ARRAY); } #endif /// Problem 2,... // See assignment for Problems 2, ... // http://www.ece.lsu.edu/koppel/gp/2008/hw03.pdf // See solution for solutions of Problems 2, ... // http://www.ece.lsu.edu/koppel/gp/2008/hw03_sol.pdf #include <stdio.h> #include <strings.h> #include <stdlib.h> #include <deque> #define GL_GLEXT_PROTOTYPES #define GLX_GLXEXT_PROTOTYPES #include <GL/gl.h> #include <GL/glext.h> #include <GL/glx.h> #include <GL/glxext.h> #include <GL/glu.h> #include <GL/freeglut.h> #include "util.h" #include "coord.h" // Display a tetrahedron, used to indicate light position. // void insert_tetrahedron(pCoor& loc, float size) { pCoor v0(loc.x,loc.y,loc.z); pCoor v1(loc.x,loc.y-size,loc.z+size); pCoor v2(loc.x-.866*size,loc.y-size,loc.z-0.5*size); pCoor v3(loc.x+.866*size,loc.y-size,loc.z-0.5*size); static pColor c1(0xffffff); static pColor c2(0xff00); glDisable(GL_LIGHTING); #define TRI(va,vb,vc) \ { \ pVect n = cross(va,vb,vc); \ glNormal3fv(n); \ glColor3fv(c1); glVertex3fv(va); \ glColor3fv(c2); glVertex3fv(vb); \ glVertex3fv(vc); \ } glBegin(GL_TRIANGLES); TRI(v0,v1,v2); TRI(v0,v2,v3); TRI(v0,v3,v1); glEnd(); # undef TRI glEnable(GL_LIGHTING); } // Class for re-using sine and cosine values. // class MTrig { public: MTrig():size(0),storage(NULL){} void init(int sizep){ size = sizep; if ( storage ) delete storage; storage = new float[size]; idx = 0; full = false; } float sin(float theta){ return trig(theta,::sin); } float cos(float theta){ return trig(theta,::cos); } private: float trig(float theta,double (*func)(double)) { if ( !full ) { storage[idx] = func(theta); full = idx == size - 1; } if ( idx == size ) idx = 0; return storage[idx++]; } int size; float* storage; int idx; bool full; }; // /// Tube Object // class Tube { public: Tube(pOpenGL_Helper &fb):ogl_helper(fb){ init(); } static void render_w(void *moi){ ((Tube*)moi)->render(); } void init(); void render(); private: pOpenGL_Helper &ogl_helper; pVariable_Control variable_control; pFrame_Timer frame_timer; pVect to_eye_vector; float r0; float x_shift; float pattern_pitch_z; float opt_pattern_levels; float opt_pattern_width; float opt_light_intensity; int opt_v_buffering; bool opt_recompute; pCoor opt_light_location; bool buffer_data_0; double time_app_start; pCoor* coor_buffer; pVect* norm_buffer; int num_coor_alloc; MTrig tarray; GLuint gpu_coor_buffer; GLuint gpu_norm_buffer; bool opt_use_triangle_strips; bool opt_animation; // If true tube undulates. bool use_triangle_strips_alloc; }; void Tube::init() { time_app_start = time_wall_fp(); // Tell frame timer that work unit is "MB/s" and how should be scaled. // frame_timer.work_unit_set("MB/s",1e-6); r0 = 2; // Tube radius. x_shift = 0.4; // Tube x offset. pattern_pitch_z = 0.25; // Triangle size (z axis). opt_pattern_levels = 50; // Tube depth (z direction.) opt_pattern_width = 200; // Number of triangles along circumference. opt_light_intensity = 2; opt_v_buffering = 0; opt_recompute = false; opt_light_location.set(( r0 - 0.1 ), 0, -3 ); to_eye_vector.set(-1,-0.5,-3); // Arrange that variables below can be modified from the keyboard. // variable_control.insert(opt_light_intensity,"Light Intensity"); variable_control.insert(opt_pattern_levels,"Pattern Levels"); variable_control.insert(to_eye_vector.x,"Viewer X"); buffer_data_0 = false; coor_buffer = NULL; norm_buffer = NULL; num_coor_alloc = 0; // Get names (just names) for GL-managed buffers. // glGenBuffers(1,&gpu_norm_buffer); glGenBuffers(1,&gpu_coor_buffer); use_triangle_strips_alloc = opt_use_triangle_strips = false; glEnable(GL_NORMALIZE); } void Tube::render() { frame_timer.frame_start(); /// /// Reset Frame and Z Buffers /// glClearColor(0,0,0.0,0.5); glClearDepth(1.0); glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); // Have frame timer provide timing information for top of image. // ogl_helper.fbprintf("%s\n",frame_timer.frame_rate_text_get()); /// /// Transformation Matrices Setup /// glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glTranslatef(to_eye_vector.x,to_eye_vector.y,to_eye_vector.z); const int win_width = ogl_helper.get_width(); const int win_height = ogl_helper.get_height(); const float aspect = float(win_width) / win_height; glMatrixMode(GL_PROJECTION); glLoadIdentity(); glFrustum(-0.8,+0.8,-0.8/aspect,0.8/aspect,1,5000); glViewport(0, 0, win_width, win_height); pError_Check(); /// /// Light Location and Lighting Options /// // Adjust options based on user input. // switch ( ogl_helper.keyboard_key ) { case FB_KEY_LEFT: opt_light_location.x -= 0.1; break; case FB_KEY_RIGHT: opt_light_location.x += 0.1; break; case FB_KEY_UP: opt_light_location.y += 0.1; break; case FB_KEY_DOWN: opt_light_location.y -= 0.1; break; case FB_KEY_PAGE_DOWN: opt_light_location.z += 0.2; break; case FB_KEY_PAGE_UP: opt_light_location.z -= 0.2; break; case 9: variable_control.switch_var_right(); break; case '-':case '_': variable_control.adjust_lower(); break; case '+':case '=': variable_control.adjust_higher(); break; case 'r':case 'R': opt_recompute = !opt_recompute; break; case 'v': case 'V': opt_v_buffering++; if ( opt_v_buffering == 3 ) opt_v_buffering = 0; break; case 't': case 'T': opt_use_triangle_strips = !opt_use_triangle_strips; break; default: break; } glLightfv(GL_LIGHT0, GL_POSITION, opt_light_location); const float light_intensity[4] = {opt_light_intensity, opt_light_intensity, opt_light_intensity, 1.0}; const float light_off[4] = {0,0,0,0}; const float light_dim[4] = {0.1,0.1,0.1,1}; glLightModelfv(GL_LIGHT_MODEL_AMBIENT, &light_dim[0]); glLightfv(GL_LIGHT0, GL_DIFFUSE, &light_intensity[0]); glLightfv(GL_LIGHT0, GL_AMBIENT, &light_off[0]); glLightf(GL_LIGHT0, GL_CONSTANT_ATTENUATION, 0); glLightf(GL_LIGHT0, GL_LINEAR_ATTENUATION, 1); glLightf(GL_LIGHT0, GL_QUADRATIC_ATTENUATION, 0.25); glEnable(GL_LIGHT0); glEnable(GL_LIGHTING); glColorMaterial(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE); glEnable(GL_COLOR_MATERIAL); const char* const v_buffering_str[] = { "Individual", "Client Array", "Buffer Object" }; ogl_helper.fbprintf ("Vertex buffering: %s (v to change)\n", v_buffering_str[opt_v_buffering]); ogl_helper.fbprintf ("Tube recomputation: %d (r to change).\n", opt_recompute); ogl_helper.fbprintf ("Triangle Specification: %s (t to change)\n", opt_use_triangle_strips ? "Triangle Strips" : "Individual Triangles"); pVariable_Control_Elt* const cvar = variable_control.current; ogl_helper.fbprintf("VAR %s = %.3f\n",cvar->name,cvar->var[0]); glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LESS); // Insert marker (green tetrahedron) to show light location. // insert_tetrahedron(opt_light_location,0.05); /// /// Insert a tessellated tube in the vertex array. /// float z = -1; pColor color_purple(0x580da6); // LSU Spirit Purple pColor color_gold(0xf9b237); // LSU Spirit Gold // // Compute Tube Specifications // const int pattern_width = 3 * int( opt_pattern_width * 0.33333333 ); const int pattern_levels = int( opt_pattern_levels + 0.5 ); // SOLUTION // Number of vertices passed to OpenGL. const int vertices_per_ring = ( opt_use_triangle_strips ? 2 : 3 * 2 ) * pattern_width; const int num_coor = pattern_levels * vertices_per_ring; const double cycles_per_second = 0.2; const double phase_n = ( time_wall_fp() - time_app_start ) * cycles_per_second; const double phase = phase_n * 2.0 * M_PI; const double phase_01 = 1.0 - ( phase_n - floor(phase_n) ); const float wavelength_z = 4.8; const float wavelength_rings = wavelength_z / pattern_pitch_z; const float radians_per_z = 2.0 * M_PI / wavelength_z; const int wavelength_v = int(wavelength_rings * vertices_per_ring + 0.5); const int phase_rings = int( phase_01 * wavelength_rings + 0.5 ); const float phase_z = opt_recompute ? 0 : wavelength_z * phase_01; const int phase_v = opt_recompute ? 0 : phase_rings * vertices_per_ring; if ( phase_v > wavelength_v || phase_v < 0 ) pError_Exit(); const int num_v = num_coor - wavelength_v; int num_bytes = 0; const float ampl = 0.4; glColor3fv( color_gold ); // If number of vertices has changed re-allocate our storage // (coor_buffer, norm_buffer) and MTrig object and also remember // that gpu's buffer needs to be updated. if ( num_coor_alloc != num_coor || use_triangle_strips_alloc != opt_use_triangle_strips ) { if ( coor_buffer ) { delete coor_buffer; delete norm_buffer; } coor_buffer = new pCoor[num_coor]; norm_buffer = new pVect[num_coor]; tarray.init( vertices_per_ring * 4 ); num_coor_alloc = num_coor; use_triangle_strips_alloc = opt_use_triangle_strips; buffer_data_0 = false; // Remember that GPU buffer needs updating. } pCoor* cptr = coor_buffer; pVect* nptr = norm_buffer; // Outer Loop: z axis (down axis of tube). // if ( opt_recompute || !buffer_data_0 ) { const float phase_use = opt_recompute ? phase : 0; const float delta_theta = M_PI / pattern_width; for ( int i = 0; i < pattern_levels; i++ ) { const float next_z = z - pattern_pitch_z; const float last_z = z + pattern_pitch_z; float theta = i & 1 ? delta_theta : 0; const float angle_z = phase_use + radians_per_z * z; const float angle_nz = phase_use + radians_per_z * next_z; const float angle_lz = phase_use + radians_per_z * last_z; const float r = r0 * ( 1 + ampl * sin( angle_z ) ); const float rnz = r0 * ( 1 + ampl * sin( angle_nz ) ); const float rlz = r0 * ( 1 + ampl * sin( angle_lz ) ); const float cos_z = cos(angle_z); const float cos_lz = cos(angle_lz); const float cos_nz = cos(angle_nz); pCoor* const cptr_stop = cptr + vertices_per_ring; // Inner Loop: around circumference of tube. // while ( cptr < cptr_stop ) { // This loop iterates for two trips around the ring, // on the first trip first_round is true and triangles point // away from the viewer, on the second round they point towards // the viewer. const bool first_round = theta < 2 * M_PI; const float z1 = first_round ? next_z : last_z; const float rz1 = first_round ? rnz : rlz; const float cos_z1 = first_round ? cos_nz : cos_lz; float cos_theta = tarray.cos(theta); // Reassigned float sin_theta = tarray.sin(theta); // Reassigned // First vertex of triangle. // *cptr++ = pCoor(x_shift + r * cos_theta, r * sin_theta, z); *nptr++ = pVect(-cos_theta,-sin_theta,cos_z); theta += delta_theta; cos_theta = tarray.cos(theta); sin_theta = tarray.sin(theta); // Second vertex of triangle. // *cptr++ = pCoor(x_shift + rz1 * cos_theta, rz1 * sin_theta, z1); *nptr++ = pVect(-cos_theta,-sin_theta,cos_z1); theta += delta_theta; // SOLUTION if ( opt_use_triangle_strips ) continue; cos_theta = tarray.cos(theta); sin_theta = tarray.sin(theta); // Third vertex of triangle. // *cptr++ = pCoor(x_shift + r * cos_theta, r * sin_theta, z); *nptr++ = pVect(-cos_theta,-sin_theta,cos_z); } z = next_z; } // // Write vertices to buffer object, if necessary. // if ( !opt_recompute || opt_v_buffering == 2 ) { const int v_bytes = num_coor * sizeof(pVect); const int c_bytes = num_coor * sizeof(pCoor); glBindBuffer(GL_ARRAY_BUFFER, gpu_norm_buffer); glBufferData (GL_ARRAY_BUFFER, v_bytes, norm_buffer, GL_STATIC_DRAW); glBindBuffer(GL_ARRAY_BUFFER, gpu_coor_buffer); glBufferData (GL_ARRAY_BUFFER, c_bytes, coor_buffer, GL_STATIC_DRAW); glBindBuffer(GL_ARRAY_BUFFER, 0); num_bytes += v_bytes + c_bytes; } buffer_data_0 = !opt_recompute; } glMatrixMode(GL_MODELVIEW); glPushMatrix(); glTranslatef(0,0,phase_z); // // Specify Vertices to GL in one of Three Ways // switch ( opt_v_buffering ) { case 0: // Individual Vertex Specification (Buffering) // { int idx_v = phase_v; const int end_v = phase_v + num_v; // SOLUTION if ( opt_use_triangle_strips ) { while ( idx_v < end_v ) { glBegin(GL_TRIANGLE_STRIP); for ( int c=0; c<vertices_per_ring; c++ ) { glNormal3fv(norm_buffer[idx_v]); glVertex3fv(coor_buffer[idx_v]); num_bytes += sizeof(float) * 6; idx_v++; } glEnd(); } break; } glBegin(GL_TRIANGLES); for ( int i=phase_v; i<end_v; i++ ) { glNormal3fv(norm_buffer[i]); glVertex3fv(coor_buffer[i]); num_bytes += sizeof(float) * 6; } glEnd(); } break; case 1: // Client Array Vertex Buffering // // Vertices given to OpenGL as a single array each time this code reached. { glNormalPointer(GL_FLOAT,0,norm_buffer); glVertexPointer(3,GL_FLOAT,sizeof(pCoor),coor_buffer); glEnableClientState(GL_NORMAL_ARRAY); glEnableClientState(GL_VERTEX_ARRAY); // SOLUTION if ( opt_use_triangle_strips ) { const int end_v = phase_v + num_v; for( int idx_v = phase_v; idx_v < end_v; idx_v += vertices_per_ring ) glDrawArrays(GL_TRIANGLE_STRIP,idx_v,vertices_per_ring); } else { glDrawArrays(GL_TRIANGLES,phase_v,num_v); } glDisableClientState(GL_NORMAL_ARRAY); glDisableClientState(GL_VERTEX_ARRAY); num_bytes += sizeof(float) * 6 * num_v; } break; case 2: { // Buffer Object Vertex Buffering // // Vertices given to OpenGL earlier (using a buffer object, see // glBufferData above). Hopefully the vertices are now on the // GPU. Code below merely refers to that data. // glBindBuffer(GL_ARRAY_BUFFER,gpu_coor_buffer); glVertexPointer(3,GL_FLOAT,sizeof(pCoor),NULL); glBindBuffer(GL_ARRAY_BUFFER,gpu_norm_buffer); glNormalPointer(GL_FLOAT,0,NULL); glBindBuffer(GL_ARRAY_BUFFER,0); glEnableClientState(GL_NORMAL_ARRAY); glEnableClientState(GL_VERTEX_ARRAY); // SOLUTION if ( opt_use_triangle_strips ) { const int end_v = phase_v + num_v; for( int idx_v = phase_v; idx_v < end_v; idx_v += vertices_per_ring ) glDrawArrays(GL_TRIANGLE_STRIP,idx_v,vertices_per_ring); } else { glDrawArrays(GL_TRIANGLES,phase_v,num_v); } glDisableClientState(GL_NORMAL_ARRAY); glDisableClientState(GL_VERTEX_ARRAY); } break; } glPopMatrix(); frame_timer.work_amt_set(num_bytes); // Insert additional triangle. // { pCoor v0( 1.5, 0, -3.2 ); pCoor v1( 0, 5, -5 ); pCoor v2( 9, 6, -9 ); pVect normal(cross(v0,v1,v2)); glColor3fv( color_purple ); glBegin(GL_TRIANGLES); glNormal3fv(normal); glVertex3fv(v0); glNormal3fv(normal); glVertex3fv(v1); glNormal3fv(normal); glVertex3fv(v2); glEnd(); } glColor3f(0,1,0); // This sets the text color. Don't know why. pError_Check(); frame_timer.frame_end(); glutSwapBuffers(); } int main(int argc, char **argv) { pOpenGL_Helper popengl_helper(argc,argv); Tube tube(popengl_helper); popengl_helper.rate_set(30); popengl_helper.display_cb_set(tube.render_w,&tube); return 0; }