/// LSU EE 4702-1 (Fall 2021), GPU Programming // // Specify version of OpenGL Shading Language. // #version 460 #extension GL_GOOGLE_include_directive : enable #include <light.h> #include <transform.h> #include "links-shdr-common.h" #include "shader-common-generic-lighting.h" /// Background -- True Spheres /// /// True Sphere // // A sphere rendered perfectly. // (A tessellation using triangles is not perfect since the triangles // approximate the sphere surface.) // Each sphere rendered using one (one!) vertex. // // Storage Buffers / Arrays // // Each array has one element per sphere. // // sphere_pos_rad. A vec4. // sphere_pos_rad[i].xyz // Coordinates of center of sphere i. // sphere_pos_rad[i].w // Radius of sphere i. // // sphere_rot. A mat4 // Orientation of sphere. (A rotation matrix.) // // sphere_color. A vec4. // Sphere color. /// Rendering Pass // // Input primitive is point. // There are no vertex shader inputs .. // .. other than gl_VertexIndex which is automatically generated. // /// Vertex Shader // // Passes gl_VertexIndex to geometry shader. // That's all the vertex shader does. // /// Geometry Shader // // Input: Point // Output: Triangle Strip // // Generates a rectangle (two triangles) around sphere. // From eye, the rectangle perfectly frames the sphere. // The rectangle is a square for spheres in the center of the window, // and grow more rectangular closer to the window edges. // /// Fragment Shader // // Determines whether pixel is covered by sphere .. // .. and if so, computes the coordinates of that point on the sphere. // Fragment is dropped if pixel not covered by sphere .. // .. otherwise determines normal and texture coordinates. /// Efficiency: True Sphere v. Tessellated Sphere // // Operation Background // // mad: // Multiply add. // Performed by a FP32 unit (in older GPUs performed by CUDA core). // "Cost" of one multiply or one add same as cost of one mad. // // special: // Operations using special functional unit. // Include reciprocal, reciprocal square root. Used for division, sqrt. // From CC 5.0 (Maxwell) to present (Ampere) .. // .. cost is 4 times that of a mad. // // trig: // Operations requiring library code. // Assume cost is 20 * mad. // // /// Tessellated Sphere // Code in links-shdr.cc. // // Rendered using an instanced rendering pass: // One rendering pass renders n spheres. // Vertex shader input is for vertex on a radius=1 sphere at origin. // Instance ID used to retrieve actual radius, center, and rotation. // // Vertex Shader Estimated Work per Vertex // 59 mad, 1 special. // Cost: 63 // // Let s denote number of slices. Approximate: s^2 vertices in tessellation. // /// True Sphere // Code in this file. // // Fragment Shader Extra Work per Fragment // Note: Extra work means work not done by tessellated sphere's fragment // shader. // 61 mad, 7 special, 2 trig // Cost: 61 + 7*4 + 2*20 = 129 // /// Comparison // // Work FS True / Work VS Tess = 129 / 63 approx 2 // // Expect True Sphere better when: // Tess Sphere Frag / Vtx ratio < 0.5 // #ifdef BIND_SPHERE_POS layout ( binding = BIND_SPHERE_POS ) buffer spr { vec4 sphere_pos_rad[]; }; layout ( binding = BIND_SPHERE_ROT ) buffer sr { mat4 sphere_rot[]; }; layout ( binding = BIND_SPHERE_COLOR ) buffer sc { vec4 sphere_color[]; }; #endif #ifdef BIND_UNI_COMMON layout ( binding = BIND_UNI_COMMON ) uniform Common_Uniform { Shdr_Uni_Common com; }; bool tryout1 = bool(com.tryout.x); bool tryout2 = bool(com.tryout.y); #endif #ifdef _VERTEX_SHADER_ // Interface block for vertex shader output / geometry shader input. // layout ( location = 0 ) out Data_to_GS { int vertex_id; }; void vs_main() { vertex_id = gl_VertexIndex; } #endif #ifdef _GEOMETRY_SHADER_ layout ( location = 0 ) in Data_to_GS { int vertex_id; } In[1]; layout ( location = 0 ) out Data_to_FS { flat int vertex_id; vec3 vertex_o; }; // Type of primitive at geometry shader input. // layout ( points ) in; // Type of primitives emitted geometry shader output. // layout ( triangle_strip, max_vertices = 4 ) out; void gs_main() { vertex_id = In[0].vertex_id; vec4 pos_rad = sphere_pos_rad[vertex_id]; vec3 ctr_o = pos_rad.xyz; float r = pos_rad.w; // Eye location in object space. vec3 e_o = -transpose(mat3(gl_ModelViewMatrix)) * gl_ModelViewMatrix[3].xyz; // Vector from eye to sphere center. vec3 ec_o = ctr_o - e_o; // Eye to Center (of sphere). // Vectors orthogonal to ec_o. // vec3 atr_o = abs(ec_o); int min_idx = atr_o.x < atr_o.y ? ( atr_o.x < atr_o.z ? 0 : 2 ) : ( atr_o.y < atr_o.z ? 1 : 2 ); vec3 ax_raw = min_idx == 0 ? vec3(0,-ec_o.z,ec_o.y) : min_idx == 1 ? vec3(-ec_o.z,0,ec_o.x) : vec3(-ec_o.y,ec_o.x,0); vec3 ax = normalize(ax_raw); vec3 ay = normalize(cross(ec_o,ax)); // Compute center of the limb, lmb_o, the circle formed by the most // distant visible parts of the sphere surface. float sin_theta_sq = r * r / dot(ec_o,ec_o); // The following is equivalent to the simplified expression for lmb_o: // vec3 lmb_o = ctr_o - sin_theta * r * ec_o / length(ec_o); vec3 lmb_o = ctr_o - sin_theta_sq * ec_o; // Compute axes to draw the limb. float b = r * sqrt( 1 - sin_theta_sq ); vec3 vx = b * ax; vec3 vy = b * ay; mat4 mvp = gl_ModelViewProjectionMatrix; // Emit a bounding square for the limb. for ( int i = -1; i < 2; i += 2 ) for ( int j = -1; j < 2; j += 2 ) { vertex_id = In[0].vertex_id; vertex_o = lmb_o + vx * i + vy * j; gl_Position = mvp * vec4(vertex_o,1); EmitVertex(); } EndPrimitive(); } #endif #ifdef _FRAGMENT_SHADER_ #ifdef BIND_TEXUNIT layout ( binding = BIND_TEXUNIT ) uniform sampler2D tex_unit_0; #endif layout ( location = 0 ) in Data_to_FS { flat int vertex_id; vec3 vertex_o; }; layout ( location = 0 ) out vec4 frag_color; void fs_main() { vec4 pos_rad = sphere_pos_rad[vertex_id]; // Center of sphere in original object-space coordinates. vec3 ctr_o = pos_rad.xyz; float rsq = pos_rad.w * pos_rad.w; // mad: 1 // Eye location in object-space coordinates. // mad: 9 Or, just use uniform. vec3 e_o = -transpose(mat3(gl_ModelViewMatrix)) * gl_ModelViewMatrix[3].xyz; // Prepare to compute intersection of ray from eye to through fragment with // sphere. That intersection is the point on the sphere corresponding // to this fragment. // vec3 ef = vertex_o - e_o; float ef_d_ef = dot(ef,ef); // mad: 3 vec3 ce = e_o-ctr_o; float ce_d_ce = dot(ce,ce); // mad: 3 float ef_d_ce = dot(ef,ce); // mad: 3 float qfa = ef_d_ef; float qfb = 2 * ef_d_ce; // mad: 1 float qfc = ce_d_ce - rsq; float discr = qfb*qfb - 4 * qfa * qfc; // mad: 3 // If outside the limb, return. if ( discr < 0 ) discard; // Finish computing coordinate of point for this fragment. // float t = ( -qfb - sqrt( discr ) ) / ( 2 * qfa ); // mad: 1; spc: 2 vec4 sur_o = vec4(e_o + t * ef, 1); // mad: 1 { // Compute clip-space depth. vec4 sur_c = gl_ModelViewProjectionMatrix * sur_o; // mad: 4 gl_FragDepth = sur_c.z / sur_c.w; // spc: 1 } // Compute eye-space coordinate and vector of unreflected point. // vec3 normal_o = normalize(sur_o.xyz - ctr_o); // mad: 3 spc: 1 vec3 normal_e = normalize(gl_NormalMatrix * normal_o); // mad: 12 spc: 1 vec4 sur_e = gl_ModelViewMatrix * sur_o; // mad: 16 // Use sphere-local coordinates to compute texture coordinates. // vec3 sur_l = transpose(mat3(sphere_rot[vertex_id])) * normal_o; // mad: 9 float pi = 3.14159265359; float tpi = 2 * pi; float theta = atan(sur_l.x,sur_l.z); // trg: 1 float eta = acos(sur_l.y); // trg: 1 vec2 tcoord = vec2( ( 1.5 * pi + theta ) / tpi, eta / pi ); // spc: 2 // Totals: mad: 69 spc: 7 trg: 2 // Get filtered texel. // vec4 texel = tryout1 ? vec4(1,1,1,1) : texture(tex_unit_0,tcoord); vec4 color = sphere_color[vertex_id]; vec4 color2 = gl_FrontFacing ? color : vec4(0.5,0,0,1); // Multiply filtered texel color with lighted color of fragment. // frag_color = texel * generic_lighting(sur_e, color2, normal_e); } #endif