/// LSU EE 4702-1 (Fall 2016), GPU Programming // /// Homework 5 -- SOLUTION // // See http://www.ece.lsu.edu/koppel/gpup/2016/hw05.pdf // Specify version of OpenGL Shading Language. // #version 450 compatibility layout ( location = 1 ) uniform vec4 color_bottom; layout ( location = 2 ) uniform vec4 color_back; layout ( location = 4 ) uniform vec4 color_side; layout ( location = 5 ) uniform vec4 color_top; layout ( location = 6 ) uniform ivec2 seg_sides; layout ( location = 7 ) uniform float thickness; // Use this variable to debug your code. Press 'y' to toggle tryout.x // and 'Y' to toggle tryout.y (between true and false). // layout ( location = 3 ) uniform bvec2 tryout; #ifdef _VERTEX_SHADER_ in vec2 gl_MultiTexCoord0; // Interface block for vertex shader output / geometry shader input. // out Data_to_GS { vec3 normal_e; vec4 vertex_e; vec2 gl_TexCoord[1]; vec4 gl_Position; /// SOLUTION -- Problem 1 // // Carry the value of theta to the geometry shader where it will be // used to determine whether triangles should be discarded. // float theta; }; void vs_main() { /// Problem 1 solution here and other places. normal_e = normalize(gl_NormalMatrix * gl_Normal); gl_TexCoord[0] = gl_MultiTexCoord0; /// SOLUTION -- Problem 1 // // Move the theta value to the newly declared vertex shader output // named theta and re-construct the object space vertex coordinate // in vtx_o // theta = gl_Vertex.w; vec4 vtx_o = vec4(gl_Vertex.xyz,1); gl_Position = gl_ModelViewProjectionMatrix * vtx_o; vertex_e = gl_ModelViewMatrix * vtx_o; } #endif #ifdef _GEOMETRY_SHADER_ in Data_to_GS { vec3 normal_e; vec4 vertex_e; vec2 gl_TexCoord[1]; vec4 gl_Position; /// SOLUTION -- Problem 1 // // Carry the value of theta to the geometry shader where it will be // used to determine whether triangles should be discarded. // float theta; } In[]; out Data_to_FS { vec3 normal_e; vec4 vertex_e; vec2 gl_TexCoord[1]; /// SOLUTION -- Problem 2 // // See in Data_to_FS for detailed comments describing these declarations. // flat bool is_side; flat vec3 normal_f; float height; }; // Type of primitive at geometry shader input. // layout ( triangles ) in; // Type of primitives emitted geometry shader output. // /// SOLUTION -- Problem 2 // // Specify just enough vertices to render the raised or surface // triangle (3) and the sides (2*4=8) // layout ( triangle_strip, max_vertices = 11 ) out; void gs_main_1() { /// SOLUTION -- Problem 1 // // Discard primitives in which the provoking vertex is at theta = 0, // because for those primitives vertex In[0] belongs to a previous // segment. // if ( In[2].theta == 0 ) return; for ( int i=0; i<3; i++ ) { normal_e = In[i].normal_e; vertex_e = In[i].vertex_e; gl_Position = In[i].gl_Position; gl_TexCoord[0] = In[i].gl_TexCoord[0]; EmitVertex(); } EndPrimitive(); } void gs_main_2() { /// Problem 2 solution goes here, and other places. // Don't a render triangle for first two vertices of a segment since // such a triangle would cross between segments. Note: this is an // alternative method of fixing the issue described in Problem 1. // int vtx_num = gl_PrimitiveIDIn + 2; int side_num = vtx_num % ( 2 * seg_sides.y ); if ( side_num < 2 ) return; /// SOLUTION -- Problem 2 // Compute offset, the vector to use to raise the triangle. // vec3 v01 = In[1].vertex_e.xyz - In[0].vertex_e.xyz; vec3 v02 = In[2].vertex_e.xyz - In[0].vertex_e.xyz; vec3 norm = normalize(cross( v01, v02 )); vec3 offset = thickness * norm; is_side = true; // Raise one out of every four triangles. // const bool raised = ( gl_PrimitiveIDIn & 0x3 ) == 0; /// Draw Side Triangles // // Side triangles are drawn as one triangle strip. Note that In[0] // is examined for the first (i=0) and last (i=3) iteration of the loop. // if ( raised ) for ( int i=0; i<=3; i++ ) { const int this_vtx_idx = i % 3; const int prev_vtx_idx = ( i + 2 ) % 3; // Use same texture coordinate for surface and raised vertices. // gl_TexCoord[0] = In[this_vtx_idx].gl_TexCoord[0]; vec4 vertex_e_surface = In[this_vtx_idx].vertex_e; vec4 vertex_e_raised = vertex_e_surface + vec4(offset,0); vec4 vertex_e_prev = In[prev_vtx_idx].vertex_e; // Compute normal of side triangles and assign to flat normal output. // vec3 v_this_prev = vertex_e_prev.xyz - vertex_e_surface.xyz; normal_f = cross(norm,v_this_prev); // Emit the side triangle vertex shared with the raised triangle. // height = 1; vertex_e = vertex_e_raised; gl_Position = gl_ProjectionMatrix * vertex_e_raised; EmitVertex(); // Emit the side triangle vertex shared with the surface triangle. // height = 0; vertex_e = vertex_e_surface; gl_Position = In[this_vtx_idx].gl_Position; EmitVertex(); } EndPrimitive(); is_side = false; height = float(raised); // Emit either a raised or surface triangle. // for ( int i=0; i<3; i++ ) { normal_e = normalize(In[i].normal_e); vertex_e = In[i].vertex_e + ( raised ? vec4(offset,0) : vec4(0) ); gl_Position = gl_ProjectionMatrix * vertex_e; gl_TexCoord[0] = In[i].gl_TexCoord[0]; EmitVertex(); } EndPrimitive(); } #endif #ifdef _FRAGMENT_SHADER_ uniform sampler2D tex_unit_0; in Data_to_FS { vec3 normal_e; vec4 vertex_e; vec2 gl_TexCoord[1]; /// SOLUTION -- Problem 2 // If true, fragment is part of a side triangle. // flat bool is_side; // Surface normal to be used for side triangles. Since it is declared // flat the value for a fragment is taken from the provoking vertex // (3rd vertex for triangles) rather than an interpolation of the values // at all three triangle vertices. // flat vec3 normal_f; // Distance from surface. Zero means at the surface, one means at // the raised triangle. We want it's value to be interpolated. // float height; }; vec4 generic_lighting(vec4 vertex_e, vec4 color, vec3 normal_e); void fs_main_1() { /// Do not modify this module. Use fs_main_2 for Problem 2 // Perform lighting, fetch and blend texture, then emit fragment. // // Get filtered texel, unless the fragment belongs to an edge primitive. // vec4 texel = texture(tex_unit_0,gl_TexCoord[0]); vec4 color2 = gl_FrontFacing ? color_bottom : color_back; // Multiply filtered texel color with lighted color of fragment. // gl_FragColor = texel * generic_lighting(vertex_e, color2, normal_e); // Copy fragment depth unmodified. // gl_FragDepth = gl_FragCoord.z; } void fs_main_2() { /// Problem 2 solution goes here and other places. // Perform lighting, fetch and blend texture, then emit fragment. // // Get filtered texel, unless the fragment belongs to an edge primitive. // vec4 texel = texture(tex_unit_0,gl_TexCoord[0]); /// SOLUTION -- Problem 2 // float marg = 1.0/3; float omm = 1 - marg; // One Minus Margin // Blend appropriate set of colors together. Note that for // non-side triangles height = 0. // vec4 color2 = !gl_FrontFacing ? color_back : height > omm ? ( ( height - omm ) * color_top + ( 1.0 - height ) * color_side ) / marg : height < marg ? ( height * color_side + ( marg - height ) * color_bottom ) / marg : color_side; // Compute a texture blend amount and use it to blend the texel color // with the color white. // float tblend = 2 * abs(height-0.5); // Note: Value is between 0 and 1. vec4 white = vec4(1,1,1,1); vec4 texel2 = (1-tblend) * white + tblend * texel; // For side triangles use the non-interpolated normal. // vec3 norm = is_side ? normal_f : normal_e; // Multiply filtered texel color with lighted color of fragment. // gl_FragColor = texel2 * generic_lighting(vertex_e, color2, norm); // 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) { /// It should not be necessary to modify this module for Homework 5. // Return lighted color of vertex_e. // vec4 light_pos = gl_LightSource[0].position; vec3 v_vtx_light = light_pos.xyz - vertex_e.xyz; float dist = length(v_vtx_light); float d_n_vl = dot(normalize(normal_e), v_vtx_light) / dist; float phase_light = max(0,gl_FrontFacing ? d_n_vl : -d_n_vl ); vec3 ambient_light = gl_LightSource[0].ambient.rgb; vec3 diffuse_light = gl_LightSource[0].diffuse.rgb; 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 + 0.3 * color.rgb * ( ambient_light + phase_light * diffuse_light ) / atten_inv; lighted_color.a = color.a; return lighted_color; } #endif