/// LSU EE 4702-1 (Fall 2017), GPU Programming // /// Homework 5 -- SOLUTION /// Instructions // // Solution Discussion: http://www.ece.lsu.edu/koppel/gpup/2017/hw05_sol.pdf // /// Put the solution in this file. // To solve the problem code in hw05.cc needs to be inspected. // Specify version of OpenGL Shading Language. // #version 450 compatibility // The _GEOMETRY_SHADER_ define is put there by code in shader.h. // #ifdef _GEOMETRY_SHADER_ #extension GL_EXT_geometry_shader4 : enable #endif // ---------------------------------------------------------------------------- /// PROBLEM 1 - 3 ↓↓ Code can be placed below. ↓↓ layout ( location = 1 ) uniform bvec2 opt_tryout; layout ( location = 2 ) uniform int lighting_options; layout ( location = 3 ) uniform float world_time; layout ( binding = 1 ) buffer b1 { vec4 pt_00s[]; }; layout ( binding = 2 ) buffer b2 { vec4 axs[]; }; layout ( binding = 3 ) buffer b3 { vec4 ays[]; }; layout ( binding = 4 ) buffer b4 { vec4 colors[]; }; layout ( binding = 5 ) buffer b5 { vec4 tact_pos_times[]; }; #ifdef _VERTEX_SHADER_ /// PROBLEM 1 - 3 ↑↑ Code can be placed above. ↑↑ // ---------------------------------------------------------------------------- #ifndef _PROBLEM_2_ // ---------------------------------------------------------------------------- /// PROBLEM 1 and 3 ↓↓ Code can be placed below. ↓↓ // Interface block for vertex shader output / geometry shader input. // out Data_to_GS { vec4 gl_Position; vec3 normal_e; vec4 vertex_e; vec4 color; // Any changes here must also be made to the geometry shader input. }; void vs_main_tiles_0() { gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; vertex_e = gl_ModelViewMatrix * gl_Vertex; normal_e = normalize(gl_NormalMatrix * gl_Normal); color = gl_Color; } void vs_main_tiles_1() { /// SOLUTION - Problem 1 // Pass through inputs unchanged. // vertex_e = gl_Vertex; // Either pt_00, ax, or ay. color = gl_Color; // Either color or tact_pos_time. } /// PROBLEM 1 and 3 ↑↑ Code can be placed above. ↑↑ // ---------------------------------------------------------------------------- #else // ---------------------------------------------------------------------------- /// PROBLEM 2 and 3 ↓↓ Code can be placed below. ↓↓ // Interface block for vertex shader output / geometry shader input. // out Data_to_GS { vec4 gl_Position; vec3 normal_e; vec4 vertex_e; vec4 color; // Any changes here must also be made to the geometry shader input. /// SOLUTION -- Problem 2 int vertex_id; }; void vs_main_tiles_2() { /// SOLUTION -- Problem 2 // Pass vertex id unchanged. // vertex_id = gl_VertexID; // // Note: Doing more work here and sending the results to the // geometry shader might result in exactly the same amount of // computation, but it will definitely increase the amount of data // sent between the vertex and geometry shaders, which is not // a good thing. } #endif /// PROBLEM 2 and 3 ↑↑ Code can be placed above. ↑↑ // ---------------------------------------------------------------------------- #endif // ---------------------------------------------------------------------------- /// PROBLEM 1 - 3 ↓↓ Code can be placed below. ↓↓ #ifdef _GEOMETRY_SHADER_ out Data_to_FS { flat vec3 normal_e; vec4 vertex_e; flat vec4 color; /// SOLUTION -- Problem 3 // Pass contact position and time to fragment shader to draw the ring. // flat vec4 tact_pos_time_e; // // Note the use of a flat qualifier since the values will be the // same at all three vertices and so there is no point in doing // interpolation, which isn't free. }; /// PROBLEM 1 - 3 ↑↑ Code can be placed above. ↑↑ // ---------------------------------------------------------------------------- #ifndef _PROBLEM_2_ // ---------------------------------------------------------------------------- /// PROBLEM 1 and 3 ↓↓ Code can be placed below. ↓↓ // Interface block for vertex shader output / geometry shader input. // in Data_to_GS { vec4 gl_Position; vec3 normal_e; vec4 vertex_e; vec4 color; // Any changes here must also be made to the vertex shader output. } In[3]; // Type of primitive at geometry shader input. // layout ( triangles ) in; // Type of primitives emitted geometry shader output. // /// SOLUTION -- Problem 1 and 2 // // Increase max_vertices by 1. // layout ( triangle_strip, max_vertices = 4 ) out; void gs_main_tiles_0() { /// Do not put solution here. Use this file for comparison or minor experiments. // Instead, make geometry shader changes to gs_main_p1. // Color and normal will be the same for each vertex. normal_e = In[0].normal_e; color = In[0].color; for ( int i=0; i<3; i++ ) { // Pass along values unchanged. vertex_e = In[i].vertex_e; gl_Position = In[i].gl_Position; EmitVertex(); } EndPrimitive(); } void gs_main_tiles_1() { /// SOLUTION -- Problem 1 vec4 vtx_o[4]; // Write tile information into appropriately named variables. // vec4 pt_00 = In[0].vertex_e; vec4 ax_o = vec4(In[1].vertex_e.xyz,0); vec4 ay_o = vec4(In[2].vertex_e.xyz,0); // // Note: The w component of ax and ay are set to zero. The OpenGL // code on the CPU would have set them to one since they were // specified using glVertex3f(FOO). Setting w to 1 is correct for // coordinates (when only x, y, and z are given) but wrong for // vectors. // // By setting the w components of vectors to 0 expressions like // pt_00 + ax_o compute the correct results. // Compute the object-space coordinates of the tile corners. // vtx_o[0] = pt_00; vtx_o[1] = pt_00 + ax_o; vtx_o[2] = pt_00 + ay_o; vtx_o[3] = pt_00 + ay_o + ax_o; // // The order is chosen for a triangle strip. // Compute the tile normal. // vec3 normal_o = normalize(cross(ax_o.xyz,ay_o.xyz)); normal_e = normalize(gl_NormalMatrix * normal_o); // // Note that the normal is set once here and used for all four // vertices. // // Note that the CPU case 1 code does NOT send a normal. The vertex // shader still has an input named gl_Normal, but since the CPU code // does not set it, its value will be arbitrary. color = In[0].color; // // The same color is also used for all four vertices. for ( int i=0; i<4; i++ ) { gl_Position = gl_ModelViewProjectionMatrix * vtx_o[i]; vertex_e = gl_ModelViewMatrix * vtx_o[i]; EmitVertex(); } EndPrimitive(); } /// PROBLEM 1 and 3 ↑↑ Code can be placed above. ↑↑ // ---------------------------------------------------------------------------- #else // ---------------------------------------------------------------------------- /// PROBLEM 2 and 3 ↓↓ Code can be placed below. ↓↓ // Interface block for vertex shader output / geometry shader input. // in Data_to_GS { vec4 gl_Position; vec3 normal_e; vec4 vertex_e; vec4 color; // Any changes here must also be made to the vertex shader output. /// SOLUTION -- Problem 2 int vertex_id; } In[1]; // Type of primitive at geometry shader input. // layout ( points ) in; // Type of primitives emitted geometry shader output. // /// SOLUTION -- Problem 2 // layout ( triangle_strip, max_vertices = 4 ) out; void gs_main_tiles_2() { /// SOLUTION -- Problem 2 int vertex_id = In[0].vertex_id; // Retrieve information about the tile from buffer objects instead // of shader inputs. // vec4 vtx_o[4]; vec4 pt_00 = pt_00s[vertex_id]; vec4 ax_o = vec4(axs[vertex_id].xyz,0); vec4 ay_o = vec4(ays[vertex_id].xyz,0); // Compute tile object space vertex coordinates. // vtx_o[0] = pt_00; vtx_o[1] = pt_00 + ax_o; vtx_o[2] = pt_00 + ay_o; vtx_o[3] = pt_00 + ay_o + ax_o; // Compute normal. // vec3 normal_o = normalize(cross(ax_o.xyz,ay_o.xyz)); normal_e = normalize(gl_NormalMatrix * normal_o); // // Don't write comments like "compute normal" in real-world code // for situations when it's obvious what the code is doing. /// SOLUTION -- Problem 3 // Convert contact position to eye space and write it to a // geometry shader output. Also write time. // vec4 tact_pos_time = tact_pos_times[vertex_id]; tact_pos_time_e = gl_ModelViewMatrix * vec4(tact_pos_time.xyz,1); tact_pos_time_e.w = tact_pos_time.w; // // Using one variable for position and time is not the best coding // style. But, in GPUs movement of 4-element vectors is more // efficient than scalars or smaller vectors. /// SOLUTION -- Problem 2 // // The code below is identical to the corresponding code in Problem 1. color = colors[vertex_id]; for ( int i=0; i<4; i++ ) { gl_Position = gl_ModelViewProjectionMatrix * vtx_o[i]; vertex_e = gl_ModelViewMatrix * vtx_o[i]; EmitVertex(); } EndPrimitive(); } /// PROBLEM 2 and 3 ↑↑ Code can be placed above. ↑↑ // ---------------------------------------------------------------------------- #endif #endif // ---------------------------------------------------------------------------- /// PROBLEM 1 - 3 ↓↓ Code can be placed below. ↓↓ #ifdef _FRAGMENT_SHADER_ in Data_to_FS { flat vec3 normal_e; vec4 vertex_e; flat vec4 color; /// SOLUTION -- Problem 3 // Pass contact position and time to fragment shader to draw the ring. // flat vec4 tact_pos_time_e; // // Note the use of a flat qualifier since the values will be the // same at all three vertices and so there is no point in doing // interpolation, which isn't free. }; vec4 generic_lighting(vec4 vertex_e, vec4 color, vec3 normal_e); void fs_main_tiles_0() { /// Do not put solution here. Use this for minor experiments. // // Multiply filtered texel color with lighted color of fragment. // gl_FragColor = generic_lighting(vertex_e, color, normal_e); // Copy fragment depth unmodified. // gl_FragDepth = gl_FragCoord.z; } void fs_main_tiles_1() { fs_main_tiles_0(); } void fs_main_tiles_2() { /// SOLUTION -- Problem 3 float age = world_time - tact_pos_time_e.w; // Quantities below are in object space. // float speed = 0.5; // Speed of increase of the ring radius. float width = 0.1; // Width of ring. // // Note: The distance between two points in object-space coordinates // is (or should be) the same as the distance in eye-space // coordinates and so it's okay to use an object-space width and // speed on eye-space distances. // Compute distance from this fragment to the contact point. // float dist = distance(vertex_e.xyz,tact_pos_time_e.xyz); float inner_radius = age * speed; float outer_radius = inner_radius + width; // Determine if this fragment is in the ring, and set color appropriately. // bool ring = dist >= inner_radius && dist <= outer_radius; vec4 color_red = vec4(1,0,0,1); vec4 color2 = ring ? color_red : color; gl_FragColor = generic_lighting(vertex_e, color2, normal_e); // Copy fragment depth unmodified. // gl_FragDepth = gl_FragCoord.z; } vec4 generic_lighting(vec4 vertex_e, vec4 color, vec3 normal_er) { // Return lighted color of vertex_e. // vec3 nspc_color = color.rgb * gl_LightModel.ambient.rgb; vec3 spec_color = vec3(0); vec3 normal_e = normalize(normal_er); for ( int i=0; i<2; i++ ) { if ( ( lighting_options & ( 1 << i ) ) == 0 ) continue; vec4 light_pos = gl_LightSource[i].position; vec3 v_vtx_light = light_pos.xyz - vertex_e.xyz; float dist = length(v_vtx_light); float dist_vl_inv = 1.0 / dist; vec3 v_vtx_l_n = v_vtx_light * dist_vl_inv; float d_n_vl = dot(normal_e, v_vtx_l_n); float phase_light = max(0,gl_FrontFacing ? d_n_vl : -d_n_vl ); vec3 ambient_light = gl_LightSource[i].ambient.rgb; vec3 diffuse_light = gl_LightSource[i].diffuse.rgb; float distsq = dist * dist; float atten_inv = gl_LightSource[i].constantAttenuation + gl_LightSource[i].linearAttenuation * dist + gl_LightSource[i].quadraticAttenuation * distsq; vec3 lighted_color = color.rgb * ( ambient_light + phase_light * diffuse_light ) / atten_inv; nspc_color += lighted_color; vec3 h = normalize( v_vtx_l_n - normalize(vertex_e.xyz) ); spec_color += pow(max(0.0,dot(normal_e,h)),16) * color.rgb * gl_LightSource[i].specular.rgb / atten_inv; } return vec4(nspc_color+spec_color,1); } #endif