Syllabus   Blank Homework   Quizzes  
Notes   Labs   Scores   Blank

Lecture Notes
Dr. Tong Lai Yu, 2010
    1. Introduction
    2. OpenGL Shading Language ( GLSL ) I
    3. GLSL II
    4. Curve and Surface Design
    5. Modeling Shapes with Polygonal Meshes
    6. Texture Mapping
    7. Casting Shadows
    8. Tools for Raster Display
    9. Parsing External Objects

    OpenGL Shading Language ( GLSL )

    Some of the materials in these two chapters are adopted from "Interactive Computer Graphics", by Edward Angel

    Also, please read the article "varray-glsl.pdf", which is downloaded from the Internet, in the directory:

    /pool/u/class/cs520/shaders-doc

    1. Extending OpenGL

    2. GLSL included in OpenGL 2.0, released in 2004.

    3. Other shading languages: Cg ( C for Graphics ) -- cross platform, HLSL ( High Level Shading Language ) -- by Microsoft, used in DirectX

    4. GLSL is part of OpenGL, so its naturally easily integrated in OpenGL programs.

    5. GLSL is a C-like language with some C++ features,

    6. mainly for processing numerics, not for strings or characters.
    7. Programmable Pipelines

    8. OpenGL 1.5 fixed function pipeline:

    9. OpenGL 2.0 allows programmable processors:

    10. Programmable processors: data flow from the application to the vertex processor, on to the fragment processor and ultimately to the frame buffer.

    11. Allows vendors to produce faster graphics hardware with more parallel features.
    12. Shade Trees

    13. Lighting models and shaders can be represented by trees.

    14. Phong shading model:

        I = kdLdl.n + ksLs(r.v)α + kaLaIa

        r = 2(n.l)n - l

    15. We can form collection of trees, each of which defines a different method for shading a surface.

    16. We use standard tree traversal algorithms for the evaluation of expressions that define shaders.


      Expression Tree for Phong Shading.

      Expression Tree for Reflection Vector.

    17. OpenGL Shaders Execution Model

    18. Driver -- a piece of software managing the access to a hardware.

    19. OpenGL libraries can be considered as drivers as they manage shared access to the underlying graphics hardware.

    20. The following figure illustrates how OpenGL shaders are handled in the execution environment of OpenGL:

    21. Applications communicate with OpenGL by calling functions that are part of the OpenGL API.

    22. glCreateShader allows applications to allocate within the OpenGL driver the data structures needed to store an OpenGL shader.

    23. Application provides shader source code by calling glShaderSource.
    24. Overview of GLSL

      1. Versions
        Many versions, not necessary using the latest, which may not be supported by hardware
        OpenGL version GLSL version
        2.0 110
        2.1 120
        3.0 130
        .. ..
        3.3 330
        .. ..
        4.5 450

      2. Data Types
        float int
        vec2 ivec2
        vec3 ivec3
        vec4 ivec4
        mat2, mat3, mat4
        sampler2D,   bool

      3. C-like Functions:
        	 float myFunction ( float x, float y )
         	 {
        	   float a = x * x;
        	   float b = y * y;
        
        	   return a + b;
        	 }
        	
        Recursion not allowed
        No pointers:
        	 void myFunction ( float x, float y, float result )
         	 {
        	   float a = x * x;
        	   float b = y * y;
        
        	   result = a + b;
        	 }
        	
      4. C++-like constructors:
        	 void myFunction ( float x )
         	 {
        	  vec3 color3 = vec3 ( 0.5, 0.2, 0.8 );
        	  vec3 color3a = vec3 ( 0.5 );  // 3 values are 0.5
        	  vec4 color4  = vec4 ( color3, 1.0 );  // alpha is 1.0
        	  //Arrays
        	  vec2[3] arr = vec2[3] ( vec2(1.0, 2.0), vec2(3.0,4.0),
        			vec2(5.0, 6.0) );
        	 }
        	
      5. Swizzling:
        	 void myFunction ( vec3 postion )
         	 {
        	   float x = position.x;
        	  
        	   float y = position[1];
        	   
        	 }
        	 Array-style aceesing:  [0]  [1]  [2]  [3]
        	 Coordinate dimension:  .x   .y   .z   .w
        	 Color channel        : .r   .g   .b   .a
        	 Texture dimension:     .s   .t   .p   .q
        
               
                 vec2 p1 = postion.xy;
        	 vec2 p2 = position.yz;
        
        	 vec4 colorAlpha;
                 vec3 color = colorAlpha.rgb;
                 vec3 colorbgr = colorAlpha.bgr;
                 vec4 colorgray = colorAlpha.bbba;
        	
      6. Operations
        	C-like operators
        	vector * matrix
        	matrix * vector
        	matrix * matrix
        	Note:
        	vec2(x1, y1 ) * vec2(x2, y2) == vec2 (x1*x2, y1*y2)
        	function dot() gives dot product
        	function cross() gives cross product
        	
        	
      7. Built-in functions
        	A lot of built-in functions
        	  abs, mod, exp, log, sin, cos, tan, acos, asin, atan
                  pow, min, max, floor, ceil, fract, sqrt
        
        	Can be applied to vectors
        	  sin ( vec2 ( x, y ) )
        
                Graphics specific functions
        	  clamp ( x, min, max )
        	  step ( threshold, x )
        	  smoothstep ( start, end, x )
        	  mix ( a, b, ratio )
        	  length ( vec2( x, y ) )
        	  distance ( vec2 ( A ), vec2 ( B ) ) == length ( B - A )
        	  dFdx ( value )         // segment shader only
        	  dFdy ( value )
        	  texture ( sampler2D anImage, vec2 texCoord )
        	

    25. Vertex Shaders

      A vertex processor is a programmable unit that operates on incoming vertex values, usually performing traditional graphics operations such as the following:

    26. Vertex transformation
    27. Normal transformation and normalization
    28. Texture coordinate generation
    29. Texture coordinate transformation
    30. Lighting
    31. Color material application
    32. A vertex shader ( program ) is a shader running on this processor; it replaces the fixed-function operations with operations defined by the shader.

      The following figure shows vertex processor inputs and outputs:

      An Example:

      //A simple pass-through vertex shader
      
      void main()
      {
        gl_Position = gl_ProjectionMatrix * gl_ModelViewMatrix * gl_Vertex;
      }
      

      //A simple vertex shader
      
      const vec4 green = vec4 ( 0.0, 1.0, 0.0, 1.0 );
       
      void main()
      {
        gl_Position = gl_ModelViewMatrix * gl_Vertex;
        gl_FrontColor = green;
      }
      

      Execution Model

    33. Fragment Shaders

      Fragment Processor is a programmable unit operating on fragment values, usually performing traditional graphics operations such as:

    34. Operations on interpolated values
    35. Texture access
    36. Texture application
    37. Fog
    38. Color sum
    39. Fragment shaders ( programs ) are executed after the rasterizer and thus operate on each fragment rather than on each vertex.

      Can perform traditional operations on rectangular images:

    40. Pixel zoom
    41. Scale and bias
    42. Color table lookup
    43. Convolution
    44. Color matrix
    45. The following figure shows fragment processor inputs and outputs:

      An example:

      //A simple fragment shader
      
      void main()
      {
        gl_FragColor = gl_FrontColor;
      }
      

      Execution Model


      Smooth Shading

      Environment Mapping

      Bump Mapping

    46. OpenGL Shading Language API

      Overview

    47. Need OpenGL 2.0 or later.

    48. Approved by OpenGL Architectural Review Board ( ARB )

    49. Necessary steps to get a shader program ready and going:
        Shader
        glCreateShader

        glShaderSource

        glCompileShader
         
        Program
        glCreateProgram

        glAttachShader

        glAttachShader

        glLinkProgram

        glUseProgram

    50. Program Object is a Container for shaders
      • Can contain multiple shaders
      • Other GLSL functions

        	
        	GLuint progObj;
        	progObj = glCreateProgram();
        	/*
        	  define shader objects here
        	*/
        	glUseProgrm ( progObj );
        	glLinkProgram ( progObj );
        	
        	

      glCreateShader() Creates one or more shader objects.
      glShaderSource() Provides source codes for these shaders.
      glCompileShader() Compiles each of the shaders.
      glCreateProgram() Creates a program object.
      glAttachShader()Attach all shader objects to the program.
      glLinkProgram()Link the program object.
      glUseProgram()Install the executable program as part of OpenGL's current state.

    51. Creating Shader Objects

    52. Shaders are added to the program object and compiled.

    53. Usual method of passing a shader is as a null-terminated string using the function glShaderSource().

    54. The shader can be saved in a file and read into a string:
        
        {
            struct stat statBuf;
            FILE* fp = fopen(shaderFile, "r");
            char* buf;
        
            stat(shaderFile, &statBuf);
            buf = (char*) malloc(statBuf.st_size + 1 * sizeof(char));
            fread(buf, 1, statBuf.st_size, fp);
            buf[statBuf.st_size] = '\0';
            fclose(fp);
            return buf;
        }

      Gluint glCreateShader ( GLenum shaderType )
      Creates an empty shader.
      shaderType: Specifies the type of shader to be created; either GL_VERTEX_SHADER or GL_FRAGMENT_SHADER.
      Return: A non-zero value for future reference.

    55. void glShaderSource ( GLuint shader, GLsizei count, const GLchar **string, const GLint *length )
      Defines a shader's source code.
      shader: The shader object created by glCreateShader().
      string: The array of strings specifying the source code of the shader.
      count: The number of strings in the array.
      length: Points to an array specifing the lengths of the strings. If NULL, the strings are NULL-terminated.

      Compiling Shader Objects

      void glCompileShader ( GLuint shader )
      Compiles the source code strings stored in the shader object shader.
      glShaderInfoLog() give the compilation log.

      Linking and Using Shaders

      Each shader object is compiled independently. To create a program, we need to link all the shader objects.

      GLuint glCreateProgram ( void )
      Creates an empty program object and returns a non-zero value for future reference.

      void glAttachShader ( GLuint program, GLuint shader )
      Attaches the shader object specified by shader to the program object specified by program.

      void glLinkProgram ( GLuint program )
      Links the program objects specified by program.

      void glUseProgram ( GLuint program )
      Installs the program object specified by program as part of current rendering state.
      If program is 0, the programmable processors are disabled, and fixed functionality is used for both vertex and fragment processing.

      Cleaning Up

      void glDeleteShader ( GLuint shader ), void glDeleteProgram ( GLuint program ), void glDetachShader ( GLuint program, GLuint shader ).

      Here are the typical steps in developing a shader program:

    56. Start by declaring a program object and getting an identifier for it:

      GLunit progObj;
      progObj = glCreateProgram();

      The program object is a container that can hold multiple shaders and other GLSL functions.

    57. Creating shader objects:

      GLchar vertexProg[] = "shader-source-code";
      GLuint vertexObj;

      vertexObj = glCreateShader ( GL_VERTEX_SHADER );
      glShaderSource ( vertexObj, 1, vertexProg, NULL );
      glCompileShader ( vertexObj );
      glAttachObject ( progObj, vertexObj );

    58. Link everything:

      glLinkProgram ( progObj );

    59. Data Types In GLSL

      Four main types: float, int, bool and sampler. For the first three types, vector types are available:

      vec2, vec3, vec4			2D, 3D and 4D floating point vector
      ivec2, ivec3, ivec4			2D, 3D and 4D integer vector
      bvec2, bvec3, bvec4			2D, 3D and 4D boolean vectors

      For floats there are also matrix types:

      mat2, mat3, mat4			2x2, 3x3, 4x4 floating point matrix

      Samplers are types representing textures:

      sampler1D, sampler2D, sampler3D		1D, 2D and 3D texture
      samplerCube				Cube Map texture
      sampler1Dshadow, sampler2Dshadow	1D and 2D depth-component texture

      Attributes, Uniforms And Varyings

      There are three types of inputs and outputs in a shader: uniforms, attributes and varyings.

      Uniforms values do not change during a rendering,
      i.e. variables whose values are assigned outside the scope of glBegin() and glEnd().
      read-only
      Uniform variables provide a mechanism for sharing data among an application program, vertex shaders, and fragment shaders.
      Attributes       Only available in vertex shader. They are used for variables that change at most once per vertex in the vertex shader. Attributes are read-only.
      Two types:
    60. User-defined: e.g.
        attribute float x;
        attribute vec3 velocity;
    61. Built-in variables: includes OpenGL state variables like color, position, normal, e.g.
        gl_Vertex
        gl_Color
    62. Varyings       Used for passing data from a vertex shader to a fragment shader.
      Defined on a per-vertex basis but are interpolated over the primitive by the rasterizer. Can be user-defined or built-in.

      Must be declared as global.

      Built-In Types

      GLSL has some built-in attributes in a vertex shader:

      gl_Vertex 4D vector representing the vertex position
      gl_Normal 3D vector representing the vertex normal
      gl_Color 4D vector representing the vertex color
      gl_MultiTexCoordX 4D vector representing the texture coordinate of texture unit X

      There are some other built-in attributes.

      GLSL also has some built-in uniforms:

      gl_ModelViewMatrix			4x4 Matrix representing the model-view matrix.
      gl_ModelViewProjectionMatrix		4x4 Matrix representing the model-view-projection matrix.
      gl_NormalMatrix				3x3 Matrix representing the inverse transpose model-view matrix.
      					This matrix is used for normal transformation.
      
      There are some other built-in uniforms, like lighting states. See reference [2], page 42 for a full list.

      GLSL Built-In Varyings:
      gl_FrontColor				4D vector representing the primitives front color
      gl_BackColor				4D vector representing the primitives back color
      gl_TexCoord[X]				4D vector representing the Xth texture coordinate
      
      There are some other built-in varyings. See reference [2], page 44 for a full list.

      And last but not least there are some built-in types which are used for shader output:
      gl_Position				4D vector representing the final processed vertex position. Only 
      					available in vertex shader.
      gl_FragColor				4D vector representing the final color which is written in the frame 
      					buffer. Only available in fragment shader.
      gl_FragDepth				float representing the depth which is written in the depth buffer.
      					Only available in fragment shader.
      
      The importance of built-in types is that they are mapped to the OpenGL states. For example if you call glLightfv(GL_LIGHT0, GL_POSITION, my_light_position) this value is available as a uniform using gl_LightSource[0].position in a vertex and/or fragment shader.

      Generic Types

      You are also able to specify your own attributes, uniforms and varyings. For example if you want to pass a 3D tangent vector for each vertex from your application to the vertex shader you can specify a "Tangent" attribute:
      attribute vec3 Tangent;
      
      Operators and Functions

    63. Bit operations are not allowed.
    64. Some usual operators are overloaded. e.g.
      	mat4 a;
      	vec4 b, c, d;
      	
      	c = b * a;	//treat b as a row matrix
      	d = a * b;	//treat b as a column matrix
          
    65. Has a swizzling operator that is a variant of the C selection operation (.).
      	vec4 a = vec4(1.0, 2.0, 3.0 1.0);
      
      	a.x = 2.5;
      	a.yz = vec2( -1.0, 3.0 );
          
      We may use the operator to swap elements. e.g.
      	a.xy = a.yz;
          
    66. GLSL has many built-in functions including:
      • trigonometric functions: sin, cos, tan
      • inverse trigonometric functions: asin, acos, atan
      • mathematical functions: pow, log2, sqrt, abs, max, min
      • geometrical functions: length, distance, normalize, reflect
    67. GLSL uses a mechanism called call by value-return to pass parameters. Function parameters are classified as in (default), out, or inout. Returned variables are copied back to the calling function. Input parameters are copied from the calling program.
    68. A Complete Example: A minimal shader program

      /*
        tests.cpp
        Sample program showing how to write GL shader programs.
        Shader sources are in files "tests.vert" and "tests.frag".
        @Author: T.L. Yu, 2008
      */
      #include <stdlib.h>
      #include <stdio.h>
      #include <string.h>
      #include <fcntl.h>
      #include <sys/types.h>
      #include <unistd.h>
      
      #define GLEW_STATIC 1
      #include <GL/glew.h>
      #include <GL/glu.h>
      #include <GL/glut.h>
      
      using namespace std;
      
      /*
         Global handles for the currently active program object, with its two shader objects
      */
      GLuint programObject = 0;
      GLuint vertexShaderObject = 0;
      GLuint fragmentShaderObject = 0;
      static GLint win = 0;
      
      int readShaderSource(char *fileName, GLchar **shader )
      {
          // Allocate memory to hold the source of our shaders.
          FILE *fp;
          int count, pos, shaderSize;
           
          fp = fopen( fileName, "r");
          if ( !fp )
              return 0;
      
          pos = (int) ftell ( fp );
          fseek ( fp, 0, SEEK_END );			//move to end
          shaderSize = ( int ) ftell ( fp ) - pos;	//calculates file size	
          fseek ( fp, 0, SEEK_SET ); 			//rewind to beginning
           
          if ( shaderSize <= 0 ){
              printf("Shader %s empty\n", fileName);
              return 0;
          }
      
          *shader = (GLchar *) malloc( shaderSize + 1);
          
          // Read the source code
         
          count = (int) fread(*shader, 1, shaderSize, fp);
          (*shader)[count] = '\0';
      
          if (ferror(fp))
              count = 0;
      
          fclose(fp);
          
          return 1;
      }
      
      //  public 
      int installShaders(const GLchar *vertex, const GLchar *fragment)
      {
          GLint  vertCompiled, fragCompiled;  // status values
          GLint  linked;
          
          // Create a vertex shader object and a fragment shader object
      
          vertexShaderObject = glCreateShader(GL_VERTEX_SHADER);
          fragmentShaderObject = glCreateShader(GL_FRAGMENT_SHADER);
          
          // Load source code strings into shaders, compile and link
      
          glShaderSource(vertexShaderObject, 1, &vertex, NULL);
          glShaderSource(fragmentShaderObject, 1, &fragment, NULL);
      
          glCompileShader(vertexShaderObject);
          glGetShaderiv(vertexShaderObject, GL_COMPILE_STATUS, &vertCompiled);
      
          glCompileShader( fragmentShaderObject );
          glGetShaderiv( fragmentShaderObject, GL_COMPILE_STATUS, &fragCompiled);
      
          if (!vertCompiled || !fragCompiled)
              return 0;
      
          // Create a program object and attach the two compiled shaders
      
          programObject = glCreateProgram();
          glAttachShader( programObject, vertexShaderObject);
          glAttachShader( programObject, fragmentShaderObject);
      
          // Link the program object 
      
          glLinkProgram(programObject);
          glGetProgramiv(programObject, GL_LINK_STATUS, &linked);
      
          if (!linked)
              return 0;
      
          // Install program object as part of current state
      
          glUseProgram(programObject);
      
          return 1;
      }
      
      int init(void)
      {
         
         const char *version;
         GLchar *VertexShaderSource, *FragmentShaderSource;
         int loadstatus = 0;
      
         version = (const char *) glGetString(GL_VERSION);
         if (version[0] != '2' || version[1] != '.') {
            printf("This program requires OpenGL 2.x, found %s\n", version);
            exit(1);
         }
         readShaderSource("tests.vert", &VertexShaderSource );
         readShaderSource("tests.frag", &FragmentShaderSource );
         loadstatus = installShaders(VertexShaderSource, FragmentShaderSource);
         
         return loadstatus;
      }
      
      static void Reshape(int width, int height)
      {
         glViewport(0, 0, width, height);
         glMatrixMode(GL_PROJECTION);
         glLoadIdentity();
         glFrustum(-1.0, 1.0, -1.0, 1.0, 5.0, 25.0);
         glMatrixMode(GL_MODELVIEW);
         glLoadIdentity();
         glTranslatef(0.0f, 0.0f, -15.0f);
      }
      
      void CleanUp(void)
      {
         glDeleteShader(vertexShaderObject);
         glDeleteShader(fragmentShaderObject);
         glDeleteProgram(programObject);
         glutDestroyWindow(win);
      }
      
      static void Idle(void)
      {
         glutPostRedisplay();
      }
      
      
      static void Key(unsigned char key, int x, int y)
      {
         switch(key) {
         case 27:
            CleanUp();
            exit(0);
            break;
         }
         glutPostRedisplay();
      }
      
      void display(void)
      {
         GLfloat vec[4];
      
         glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
          glClearColor( 1.0, 1.0, 1.0, 0.0 );	//get white background color
         glColor3f ( 1, 0, 0 );  		//red, this will have no effect if shader is loaded
         glutWireSphere(2.0, 10, 5);
         glutSwapBuffers();
         glFlush();
      }
      
      
      int main(int argc, char *argv[])
      {
         int success = 0;
      
         glutInit(&argc, argv);
         glutInitWindowPosition( 0, 0);
         glutInitWindowSize(200, 200);
         glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);
         win = glutCreateWindow(argv[0]);
         glutReshapeFunc(Reshape);
         glutKeyboardFunc(Key);
         glutDisplayFunc(display);
         glutIdleFunc(Idle);
      
         // Initialize the "OpenGL Extension Wrangler" library
          glewInit();
      
         success = init();
         if ( success )
           glutMainLoop();
         return 0;
      }
      
      //tests.vert
      //a minimal vertex shader
      void main(void)
      {
        gl_Position     = ftransform();
        /*
          Same as:
            gl_Position = gl_ProjectionMatrix *  gl_ModelViewMatrix * gl_Vertex;
          or
            gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
        */ 
      }
      
      //tests.frag
      //a minimal fragment shader
      
      void main(void)
      {
        gl_FragColor = vec4( 1, 1, 0, 1);	//yellow color
      }
      

      GLSL 1.5 Specification