/// LSU EE 4702-1 (Fall 2015), GPU Programming // /// Homework 4 // // Use this file for your solution. /// Instructions // // Read the assignment: http://www.ece.lsu.edu/koppel/gpup/2015/hw04.pdf // Specify version of OpenGL Shading Language. // #version 430 compatibility layout ( location = 2 ) uniform int tri_cull; // Use this variable to debug your code. Press 'y' to toggle tryout.x // and 'Y' to toggle debug_bool.y (between true and false). // layout ( location = 3 ) uniform bvec2 tryout; // Array of ball positions. // layout ( binding = 1 ) buffer Balls_Pos { vec4 balls_pos_rad[]; }; layout ( binding = 2 ) buffer Balls_Color { vec4 balls_color[]; }; layout ( binding = 4 ) buffer Balls_Velocity { vec4 balls_velocity[]; }; #ifdef _VERTEX_SHADER_ // Interface block for vertex shader output / geometry shader input. // out Data_to_GS { vec3 normal_e; vec4 vertex_e; vec4 color; vec4 gl_Position; /// SOLUTION -- Problem 3 // // Pass instance ID to geometry shader. int inst_id; // Any changes here must also be made to the fragment shader input. }; void vs_main() { // Here, the vertex shader does nothing except pass variables // to the geometry shader. gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; vertex_e = gl_ModelViewMatrix * gl_Vertex; normal_e = gl_NormalMatrix * gl_Vertex.xyz; color = gl_Color; /// SOLUTION -- Problem 3 // // Set instance ID to -1, indicating that this is not a sphere. inst_id = -1; } void vs_main_instances() { vec4 center_o_rad = balls_pos_rad[gl_InstanceID]; float rad = center_o_rad.w; vec3 to_surface = rad * gl_Vertex.xyz; vec4 position_o = vec4(center_o_rad.xyz + to_surface, 1f); gl_Position = gl_ModelViewProjectionMatrix * position_o; vertex_e = gl_ModelViewMatrix * position_o; normal_e = gl_NormalMatrix * gl_Vertex.xyz; color = balls_color[gl_InstanceID]; /// SOLUTION -- Problem 3 // // Pass instance ID to geometry shader. This is only done for spheres. // inst_id = gl_InstanceID; } #endif #ifdef _GEOMETRY_SHADER_ in Data_to_GS { vec3 normal_e; vec4 vertex_e; vec4 color; vec4 gl_Position; int inst_id; /// SOLUTION -- Problem 3 } In[]; out Data_to_FS { vec3 normal_e; vec4 vertex_e; flat vec4 color; }; // Type of primitive at geometry shader input. // layout ( triangles ) in; // Type of primitives emitted geometry shader output. // /// SOLUTION -- Problem 3 // Increase number of vertices to 6 to accommodate peeled triangle. layout ( triangle_strip, max_vertices = 6 ) out; /// SOLUTION -- Problem 3 // Entire peeled routine. bool peeled(vec3 gnorm_e) { // Routine to be called from the geometry shader before emitting a // triangle. This routine will, if appropriate, emit a triangle that // looks like it's peeling off the sphere due to the force of // rushing air, and return true. Otherwise return false. // Return if this primitive isn't part of a sphere. (The // vertex shader sets inst_id, and it knows.) // if ( In[0].inst_id < 0 ) return false; // Since we already have eye-space vertex coordinates, convert // the velocity to eye space. Note that vectors are transformed // using a different transformation than coordinates. // vec3 vel_o = balls_velocity[In[0].inst_id].xyz; vec3 vel_e = gl_NormalMatrix * vel_o; // Return if air presses triangle into sphere. // float inward_amt = dot(vel_e,gnorm_e); if ( inward_amt > 0 ) return false; // Find the triangle vertex that is furthest downstream in the // direction of motion. // float upstr_amt[3]; for ( int i=0; i<3; i++ ) upstr_amt[i] = dot(In[i].vertex_e.xyz,vel_e); int min01 = upstr_amt[0] < upstr_amt[1] ? 0 : 1; int free_i = upstr_amt[min01] < upstr_amt[2] ? min01 : 2; // Get the indices of the other two vertices. // int a0 = ( free_i + 1 ) % 3; int a1 = ( free_i + 2 ) % 3; // If the downstream vertex (free_i) is too close to either of the // other two vertices, return. This happens when the sphere is // moving slowly or not at all. // if ( abs(upstr_amt[free_i] - upstr_amt[a0]) < .01 ) return false; if ( abs(upstr_amt[free_i] - upstr_amt[a1]) < .01 ) return false; // Get the vertices' coordinates. // vec3 p_0 = In[a0].vertex_e.xyz; vec3 p_1 = In[a1].vertex_e.xyz; vec3 p_f = In[free_i].vertex_e.xyz; // Note: edge 01 (axis) doesn't move. // vec3 axis = p_1 - p_0; vec3 axis_n = normalize(axis); // Find the velocity component that's not along the axis, vel_rad. // It is vel_rad that will cause the triangle to swing along axis. // float vel_axis = dot(vel_e,axis_n); vec3 vel_rad = vel_e - vel_axis * axis_n; float vel_rad_mag = length(vel_rad); vec3 vel_rad_n = vel_rad/vel_rad_mag; // If axis is fixed and the triangle can't change its shape, // the free vertex will move in a circle. Find two vectors // orthogonal to axis, v_cf and v_up, that can be used to find // points on this circle. (See the line assigning p_r.) // vec3 v_0f = p_f - p_0; vec3 p_c = p_0 + axis_n * dot(axis_n,v_0f); vec3 v_cf = p_f - p_c; float len_cf_sq = dot(v_cf,v_cf); float len_cf = sqrt(len_cf_sq); vec3 v_up = cross(axis_n,v_cf); // Compute the position of the free vertex that aligns the triangle // with the direction of motion (as though it were pushed by the // rushing air [ignoring the effect of the sphere shape on air // flow]). // vec3 p_r_max = p_c - len_cf * vel_rad_n; vec3 v_cr_max = p_r_max - p_c; // Compute the angle that triangle 01f makes with 01rmax. // float bend = dot(v_cf,v_cr_max) / len_cf_sq; float angle_max = acos(bend); // Scale that angle based on the velocity. // float stiffness = 10; float angle = ( 1 - stiffness/(stiffness+vel_rad_mag) ) * angle_max; // Compute the position of the free vertex based upon the magnitude // of the velocity. // vec3 p_r = p_c + v_cf * cos(angle) + v_up * sin(angle); // Collect the vertices in a convenient array. // vec3 vertices[3] = { p_0, p_1, p_r }; // The triangle should look like it's peeling off the // sphere. Therefore, use the sphere normals for the two fixed // vertices (p_0 and p_1), and the normal of triangle 01r for the // free vertex (p_r). // vec3 normal_r_e = normalize(cross(axis,p_r-p_0)); vec3 normals[3] = { In[a0].normal_e, In[a1].normal_e, normal_r_e }; color = In[0].color; // We know that all vertices are the same color. for ( int i=0; i<3; i++ ) { normal_e = normals[i]; vertex_e = vec4(vertices[i],1); gl_Position = gl_ProjectionMatrix * vertex_e; EmitVertex(); } EndPrimitive(); return true; } void gs_main_simple() { /// Problem 1 and 3 Solution Here. // // Behavior: // When try_cull = 0, emit all triangles. // When try_cull = 1, only emit triangles facing user. // When try_cull = 2, only emit triangles not facing user. /// SOLUTION Problem 1 // Compute the normal based on the triangle coordinates (geometric // norm) rather than using the vertex normal. // vec3 gnorm_e = cross ( In[1].vertex_e.xyz - In[0].vertex_e.xyz, In[2].vertex_e.xyz - In[1].vertex_e.xyz ); // Compute the vector to the user. This is easy in eye space because // the user is at the origin. // vec3 to_user = -In[0].vertex_e.xyz; // Determine if the triangle is facing the user. float visibility = dot( gnorm_e, to_user ); if ( tri_cull == 1 && visibility < 0 ) return; if ( tri_cull == 2 && visibility > 0 ) return; /// SOLUTION -- Problem 3 // // Call routine peeled to emit a peeled triangle (if any). If the // return value is true then a peeled triangle was emitted. // bool peeled_tri = peeled(gnorm_e); for ( int i=0; i<3; i++ ) { normal_e = In[i].normal_e; vertex_e = In[i].vertex_e; /// SOLUTION -- Problem 3 // If this triangle has peeled, then use dark red for the // surface of the sphere. color = peeled_tri ? vec4(0.5,0,0,1) : In[i].color; gl_Position = In[i].gl_Position; EmitVertex(); } EndPrimitive(); } #endif #ifdef _FRAGMENT_SHADER_ in Data_to_FS { vec3 normal_e; vec4 vertex_e; flat vec4 color; }; vec4 generic_lighting(vec4 vertex_e, vec4 color, vec3 normal_e); void fs_main() { // Perform lighting, fetch and blend texture, then emit fragment. // // Multiply filtered texel color with lighted color of fragment. // gl_FragColor = generic_lighting(vertex_e, color, normalize(normal_e)); // Copy fragment depth unmodified. // gl_FragDepth = gl_FragCoord.z; } /// /// Routine used by Either Vertex or Fragment Shader /// vec4 generic_lighting(vec4 vertex_e, vec4 color, vec3 normal_e) { // Return lighted color of vertex_e. // vec4 light_pos = gl_LightSource[0].position; vec3 v_vtx_light = light_pos.xyz - vertex_e.xyz; float d_n_ve = -dot(normal_e,vertex_e.xyz); float d_n_vl = dot(normal_e, normalize(v_vtx_light).xyz); bool same_sign = ( d_n_ve > 0 ) == ( d_n_vl > 0 ); float phase_light = same_sign ? abs(d_n_vl) : 0; vec3 ambient_light = gl_LightSource[0].ambient.rgb; vec3 diffuse_light = gl_LightSource[0].diffuse.rgb; float dist = length(v_vtx_light); float distsq = dist * dist; float atten_inv = gl_LightSource[0].constantAttenuation + gl_LightSource[0].linearAttenuation * dist + gl_LightSource[0].quadraticAttenuation * distsq; vec4 lighted_color; lighted_color.rgb = color.rgb * gl_LightModel.ambient.rgb + color.rgb * ( ambient_light + phase_light * diffuse_light ) / atten_inv; lighted_color.a = color.a; return lighted_color; } #endif