SHADERS/GLSL CS 4363/6353 SHADERS • What is a shader? • It is a small program that runs on the GPU • Usually written in a high level shader language (e.g. GLSL) • common shaders: • Vertex Shader: executes once for every vertex • Fragment Shader: executes one for every fragment (potential pixel) 2 VERTEX SHADER APPLICATIONS • Moving vertices • Transformations • Morphing • Wave motion (e.g., water) • Fractals • Lighting • More realistic models • Cartoon shaders 3 FRAGMENT SHADER APPLICATIONS Per fragment lighting calculations per vertex lighting per fragment lighting 4 FRAGMENT SHADER APPLICATIONS Texture mapping smooth shading environment mapping bump mapping 5 SHADERS IN THE GRAPHICS PIPELINE Vertex Shader Fragment Shader OpenGL (application software) 6 COMPILE A SHADER 1) Read in shader source (standard c/c++ io) 2) glCreateShader – create a shader object 3) glShaderSource - add the source code in a shader object 4) glCompileShader – compile the shader 7 LINK AND USE A SHADER PROGRAM • The program nust include BOTH fragment and vertex shaders 1) glCreateProgram – create an empty program id 2) glAttachShader - attach a frag shader 3) glAttachShader – attach a vertex shader 4) glLinkProgram – after compilation (prev slide), link! 5) glUseProgram – call this before you start using the shader during rendering. 8 GLSL • Must have a minimum of two shaders • Syntax is similar to C (it has a main) • Supports functions • Doesn’t support pointers! • Has variables similar to C • bool finished = false; • int myInt = -6; • uint = 567u; • float value = 42.0; VECTORS • GLSL supports vectors with 2, 3 and 4elements • vec2, vec3, vec4 • ivec2, ivec3, ivec4 • uvec2, uvec3, uvec4 • bvec2, bvec3, bvec4 • Initialize with a “constructor” • vec4 color = vec4 (1.0f, 0.0f, 1.0f, 1.0f); • Common operations • result = myVec + yourVec; • myVec += vec4 (1.0f, 0.5f, 0.4f, 0.2f); • myVec *= 3.0f; INDIVIDUAL ELEMENTS • Can access individual elements using • xyzw – typically for positions • rgba – usually for colors • stpq – usually for texture coordinates • … it’s your choice, but you can’t mix them! • Examples • myVec.x = 4.0f; • myVec.xy = vec2 (1.0f, 5.0f); • myVec.xyz = yourVec.xyz; • vColor.bgra = vOldColor.rgba; // called “swizzling” A NOTE ABOUT EFFICIENCY & GOTCHAS • Vector operations are supported by hardware • Performed all at once! • Inefficient: • vPos.x = vPos2.x + 1.0f; • vPos.y = vPos2.y +3.0f; • vPos.z = vPos2.z + 2.0f; • Efficient: • vPos.xyz = vPos2.xyz + vec3 (1.0f, 3.0f, 2.0f); • Gotcha: • If you don’t use an attribute or uniform, OpenGL “optimizes” by removing it! MATRICES! • Several types in columns and rows: • mat2x2 (or simply mat2) • mat3x3 (or mat3) • mat4x4 (or mat4) • mat2x3 and mat2x4 • mat3x2 and mat3x4 • mat4x2 and mat4x3 MATRICES • Organized as an array of column vectors • mMV[3] = vec4(1.0f, 0.0f, 1.0f, 1.0f); • vec3 myVec = mMV[3].xyz; • Matrices can be multiplied by other matrices and vectors • Matrices have one nasty “constructor” and one good one mat4 theMatrix = mat4 (1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f); mat4 theMatrix = mat4(1.0f); STORAGE QUALIFIERS • in – passed in from a previous stage • out – going to the next stage • const – a read-only variable (one that doesn’t change) • uniform – does not change across vertices • inout – only used in functions (it’s essentially a pointer) • in centroid/out centroid – used in multisampled buffers (interpolation) • noperspective – don’t use perspectively-correct interpolation • flat – don’t interpolate (colors) at all; declared in both vertex and frag shaders • smooth – the default interpolation EXAMPLE Perspectively interpolated noperspective http://wwwx.cs.unc.edu/~sud/courses/236/a6/ A SIDE-BY-SIDE COMPARISON • Show the relationship between client code and shader code • Assume you loaded a sphere, plane, or monkey face… • numVertices – the number of vertices (duh!) • vVerts – the position information of each vertex • vNorms – the normal information of each vertex glBindVertexArray(vao); GLuint buffer; buffer glGenBuffers(1, &buffer); Note: buffer “lives” on the graphics card in a nice, two-bedroom loft… glBindVertexArray(vao); GLuint buffer; Hey – I’m active now buffer glGenBuffers(1, &buffer); glBindBuffer (GL_ARRAY_BUFFER, buffer); glBindVertexArray(vao); GLuint buffer; Now I know how big I am! buffer glGenBuffers(1, &buffer); glBindBuffer (GL_ARRAY_BUFFER, buffer); glBufferData(GL_ARRAY_BUFFER, numVertices*6*sizeof(GLfloat), NULL, GL_STATIC_DRAW); Why 6? glBindVertexArray(vao); GLuint buffer; Now I’m putting vVerts at the beginning buffer glGenBuffers(1, &buffer); vVerts glBindBuffer (GL_ARRAY_BUFFER, buffer); glBufferData(GL_ARRAY_BUFFER, numVertices*6*sizeof(GLfloat), NULL, GL_STATIC_DRAW); glBufferSubData (GL_ARRAY_BUFFER, 0, numVertices*3*sizeof(GLfloat), vVerts); glBufferSubData (GL_ARRAY_BUFFER, numVertices*3*sizeof(GLfloat), numVertices*3*sizeof(GLfloat), vNorms); Put vVerts at 0… it’s pretty big though… I’m putting vNormals next glBindVertexArray(vao); GLuint buffer; buffer glGenBuffers(1, &buffer); vVerts vNorms glBindBuffer (GL_ARRAY_BUFFER, buffer); glBufferData(GL_ARRAY_BUFFER, numVertices*6*sizeof(GLfloat), NULL, GL_STATIC_DRAW); glBufferSubData (GL_ARRAY_BUFFER, 0, numVertices*3*sizeof(GLfloat), vVerts); glBufferSubData (GL_ARRAY_BUFFER, numVertices*3*sizeof(GLfloat), numVertices*3*sizeof(GLfloat), vNorms); Put vNormals starting right after that! It’s pretty big too… WHAT WE HAVE SO FAR… • We have a buffer with an ID • That buffer lives on the graphics card • That buffer is full of vertex position/normal data • How do we get that info to our shader? • Immediately after this code, we put the following… GLuint loc = glGetAttribLocation(shaderProgramID, "vPosition"); #version 150 in vec4 vPosition; in vec3 vNormal; out vec4 color; // This will be referenced in your OpenGL program!! // The normal of the vertex // Out to fragment shader uniform mat4 p; uniform mat4 mv; uniform vec4 light_pos; // This is perpsective matrix // This is the model-view matrix // This is the light position void main () { gl_Position = p*mv*vPosition; vec3 L = normalize (light_pos.xyz); vec3 N = normalize (vNormal); color = vColor*max(0.2f, dot(N, L)); } GLuint loc = glGetAttribLocation(shaderProgramID, "vPosition"); glEnableVertexAttribArray(loc); glVertexAttribPointer(loc, 3, GL_FLOAT, GL_FALSE, 0, 0); Guys! I’m still active, remember? buffer vVerts vNorms #version 150 in vec4 vPosition; in vec3 vNormal; out vec4 color; // This will be referenced in your OpenGL program!! // The normal of the vertex // Out to fragment shader uniform mat4 p; uniform mat4 mv; uniform vec4 light_pos; // This is perpsective matrix // This is the model-view matrix // This is the light position void main () { gl_Position = p*mv*vPosition; vec3 L = normalize (light_pos.xyz); vec3 N = normalize (vNormal); color = vColor*max(0.2f, dot(N, L)); } Tell vNormal where to look in me… buffer vVerts vNorms GLuint loc2 = glGetAttribLocation(shaderProgramID, "vNormal"); glEnableVertexAttribArray(loc2); glVertexAttribPointer(loc2, 3, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(numVertices*3*sizeof(GLfloat))); #version 150 in vec4 vPosition; in vec3 vNormal; out vec4 color; // This will be referenced in your OpenGL program!! // The normal of the vertex // Out to fragment shader uniform mat4 p; uniform mat4 mv; uniform vec4 light_pos; // This is perpsective matrix // This is the model-view matrix // This is the light position void main () { gl_Position = p*mv*vPosition; vec3 L = normalize (light_pos.xyz); vec3 N = normalize (vNormal); color = vColor*max(0.2f, dot(N, L)); } GLuint loc = glGetAttribLocation(shaderProgramID, "vPosition"); glEnableVertexAttribArray(loc); glVertexAttribPointer(loc, 3, GL_FLOAT, GL_FALSE, 0, 0); GLuint loc2 = glGetAttribLocation(shaderProgramID, "vNormal"); glEnableVertexAttribArray(loc2); glVertexAttribPointer(loc2, 3, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(numVertices*3*sizeof(GLfloat))); #version 150 in vec4 vPosition; in vec3 vNormal; out vec4 color; // This will be referenced in your OpenGL program!! // The normal of the vertex // Out to fragment shader uniform mat4 p; uniform mat4 mv; uniform vec4 light_pos; // This is perpsective matrix // This is the model-view matrix // This is the light position void main () { gl_Position = p*mv*vPosition; vec3 L = normalize (light_pos.xyz); vec3 N = normalize (vNormal); color = vColor*max(0.2f, dot(N, L)); } THE FRAGMENT SHADER • For every vertex shader out, there’s a fragment shader in • Value is smoothly interpolated from vertex shader • Fragment shaders have an out as well • Called “output zero” • Sent to color buffer • Represents the color of the fragment COMPILING AND LINKING SHADERS GLint fShaderID = glCreateShader(GL_FRAGMENT_SHADER); glShaderSource (fShaderID, 1, (const GLchar**)&shaderSource1, NULL); glCompileShader(fShaderID); GLint vShaderID = glCreateShader(GL_VERTEX_SHADER); glShaderSource (vShaderID, 1, (const GLchar**)&shaderSource2, NULL); glCompileShader(vShaderID); GLuint programID = glCreateProgram(); // KEEP THIS! glAttachShader(programID, vShaderID ); glAttachShader(programID, fShaderID ); glLinkProgram (programID); USING/DELETING • To use the shader: • glUseProgram (GLuint progID); • Any subsequent draw calls will use that shader • There are only a limited number of shaders on the graphics card (16) • When you’re done with one: • glDeleteShader( GLuint progID); #version 150 in vec4 vPosition; in vec3 vNormal; out vec4 color; // This will be referenced in your OpenGL program!! // The normal of the vertex // Out to fragment shader uniform mat4 p; uniform mat4 mv; uniform vec4 light_pos; // This is perpsective matrix // This is the model-view matrix // This is the light position void main () { gl_Position = p*mv*vPosition; vec3 L = normalize (light_pos.xyz); vec3 N = normalize (vNormal); color = vColor*max(0.2f, dot(N, L)); } #version 150 out vec4 fColor; in vec4 color; void main () { fColor = color; } // from the vertex shader A NOTE ABOUT VERSIONS • Version number – minimum version supported • Version numbers changed! • OpengGL 3.0 – GLSL version 1.3 (#version 130) • OpenGL 3.1 – GLSL version 1.4 (#version 140) • OpenGL 3.2 – GLSL version 1.5 (#version 150) • OpenGL 3.3 – GLSL version 3.0 (#version 300) • OpenGL 4.0 – GLSL version 4.0 (#version 400) UNIFORMS • Persistent across all vertices • Can’t be marked as in/out • Can’t be interpolated • Always read-only • Find uniform variables using: • glGetUniformLocation (GLuint progID, const GLchar* varName); SETTING UNIFORMS • • Scalars and vectors (glUniformXY – where Y is the data type): • glUniform1f (GLuint location, GLfloat v0); • glUniform2f (GLuint location, GLfloat v0, GLfloat v1); • glUniform3f (GLuint location, GLfloat v0, GLfloat v1, GLfloat v2); • glUniform4f (GLuint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); Example: GLunit lightID = glGetUniformLocation (progID, “light_pos”); glUniform4f (lightID, light_x, light_y, light_z, 1.0f); SETTING UNIFORMS • • Arrays are similar: • glUniform1fv (GLuint location, GLuint count, GLfloat* v); • glUniform2fv (GLuint location, GLuint count, GLfloat* v); • glUniform3fv (GLuint location, GLuint count, GLfloat* v); • glUniform4fv (GLuint location, GLuint count, GLfloat* v); What’s the difference? Format of glUniformXYv • X is how many elements are in each array • count is how many of those arrays you have • Y is the type • Example: GLunit lightID = glGetUniformLocation (progID, “light_pos”); GLfloat* myLightPosition[4] = {10.0f, 10.0f, 10.0f, 1.0f); glUniform4fv (lightID, 1, myLightPosition); SETTING UNIFORMS • Set uniform matrices by their dimension: • glUniformMatrix2fv (GLunit loc, GLuint count, GLboolean transpose, GLfloat* m); • glUniformMatrix3fv (GLunit loc, GLuint count, GLboolean transpose, GLfloat* m); • glUniformMatrix4fv (GLunit loc, GLuint count, GLboolean transpose, GLfloat* m); • count represents the number of matrices (almost always 1) • transpose is used to indicate if the matrix is stored in column order BUILT-IN GLSL FUNCTIONS • Tons of different mathematical functions • Scalars • Vectors • Matrices • Robust trig functions: • float x = sin (1.0); // Also have cos, tan, atan, asin… • vec4 y = sin (vec4(1.0f, 0.5f, 0.25f, 0.0f)); // Overloaded function • float z = radians (45.0f); // Convert from degrees to radians • float z = degrees (0.6f); // Convert from radians to degrees BUILT-IN GLSL FUNCTIONS • Robust exponentials: • vec2 results = pow (vec2(2, 3), vec2 (2, 2)); • log( ) – natural log • exp( ) - ex • log2( ) – log base 2 • exp2( ) – 2 to the power of… • sqrt( ) // 2^2, 3^2 BUILT-IN GLSL FUNCTIONS • Geometric functions (generic “vec”) • float length (vec x); • float distance (vec p0, vec p1); • float dot (vec x, vec y); • vec3 cross (vec x, vec y); • vec normalize (vec x); • vec reflect (vec I, vec N); // I is incident vector (light/view) and N is normal • vec refract(vec I, vec N, float eta); • vec faceForward (vec N, vec I, vec nRef);// if dot(Nref, I) < 0, return N, else return -N BUILT-IN GLSL FUNCTIONS • Matrices • transpose( ) • determinant( ) • inverse( ) • outerProduct( ) • Relational functions • vec lessThan (vec x, vec y); && vec lessThanEqual (vec x, vec y); • vec greaterThan (vec x, vec y); • vec equal (vec x, vec y); && vec notEqual (vec x, vec y); • bool any (bvec x); // returns true if any booleans in x are true • bool all (bvec x); // returns true if all booleans in x are true BUILT-IN GLSL FUNCTIONS • Common functions • abs, sign, floor, ceil, mod, min, max... yeah, yeah… • trunc(x) – nearest whole number not larger than the absolute value of x • round(x) – based on 0.5 • roundEven(x) – “returns a value equal to the nearest integer to x.The fractional part of 0.5 will round toward the nearest even integer. For example, both 3.5 and 4.5 will round to 4.0.” • fract (x) – returns the fractional part • clamp(x, y, z) – returns x if it’s between y and z, else returns y or z • mix (x, y, a) – returns the linear blend of x and y, as a varies from 0 to 1 • step (edge, x) – returns 0.0 if x < edge, 1.0f otherwise • smoothstep (edge0, edge1, x) – 0 if x < edge0, 1 if x > edge1, interpolated otherwise LAST OF THE BUILT-INS • isnan(x) – true is x is not a number (NAN) • isinf(x) – returns true is x is +∞ or -∞ • floatBitsToInt(x) – converts floating point values to ints • intBitstoFloat(x) – converts integers to floating points