Using functions in shaders

The GLSL supports functions that are syntactically similar to C functions. However, the calling conventions are somewhat different. In the following example, we'll revisit the ADS shader using functions to help provide abstractions for the major steps.

Getting ready

As with previous recipes, provide the vertex position at attribute location 0 and the vertex normal at attribute location 1. Uniform variables for all of the ADS coefficients should be set from the OpenGL side, as well as the light position and the standard matrices.

How to do it...

To implement ADS shading using functions, use the following code:

  1. Use the following vertex shader:
    layout (location = 0) in vec3 VertexPosition;
    layout (location = 1) in vec3 VertexNormal;
    
    out vec3 LightIntensity;
    
    struct LightInfo {
        vec4 Position; // Light position in eye coords.
        vec3 La;       // Ambient light intensity
        vec3 Ld;       // Diffuse light intensity
        vec3 Ls;       // Specular light intensity
    };
    uniform LightInfo Light;
    
    struct MaterialInfo {
        vec3 Ka;            // Ambient reflectivity
        vec3 Kd;            // Diffuse reflectivity
        vec3 Ks;            // Specular reflectivity
        float Shininess;    // Specular shininess factor
    };
    uniform MaterialInfo Material;
    
    uniform mat4 ModelViewMatrix;
    uniform mat3 NormalMatrix;
    uniform mat4 ProjectionMatrix;
    uniform mat4 MVP;
    
    void getEyeSpace( out vec3 norm, out vec4 position )
    {
        norm = normalize( NormalMatrix * VertexNormal);
        position = ModelViewMatrix * vec4(VertexPosition,1.0);
    }
    
    vec3 phongModel( vec4 position, vec3 norm )
    {
        vec3 s = normalize(vec3(Light.Position - position));
        vec3 v = normalize(-position.xyz);
        vec3 r = reflect( -s, norm );
        vec3 ambient = Light.La * Material.Ka;
        float sDotN = max( dot(s,norm), 0.0 );
        vec3 diffuse = Light.Ld * Material.Kd * sDotN;
        vec3 spec = vec3(0.0);
        if( sDotN > 0.0 )
            spec = Light.Ls * Material.Ks *
             pow( max( dot(r,v), 0.0 ), Material.Shininess );
    
        return ambient + diffuse + spec;
    }
    
    void main()
    {
        vec3 eyeNorm;
        vec4 eyePosition;
    
        // Get the position and normal in eye space
        getEyeSpace(eyeNorm, eyePosition);
    
        // Evaluate the lighting equation.
        LightIntensity = phongModel( eyePosition, eyeNorm );
    
        gl_Position = MVP * vec4(VertexPosition,1.0);
    }
  2. Use the following fragment shader:
    in vec3 LightIntensity;
    
    
    layout( location = 0 ) out vec4 FragColor;
    
    
    void main() {
        FragColor = vec4(LightIntensity, 1.0);
    }
  3. Compile and link both shaders within the OpenGL application, and install the shader program prior to rendering.

How it works...

In GLSL functions, the evaluation strategy is "call by value-return" (also called "call by copy-restore" or "call by value-result"). Parameter variables can be qualified with in, out, or inout. Arguments corresponding to input parameters (those qualified with in or inout) are copied into the parameter variable at call time, and output parameters (those qualified with out or inout) are copied back to the corresponding argument before the function returns. If a parameter variable does not have any of the three qualifiers, the default qualifier is in.

We've created two functions in the vertex shader. The first, named getEyeSpace, transforms the vertex position and vertex normal into eye space, and returns them via output parameters. In the main function, we create two uninitialized variables (eyeNorm and eyePosition) to store the results, and then call the function with the variables as the function's arguments. The function stores the results into the parameter variables (norm and position) which are copied into the arguments before the function returns.

The second function, phongModel, uses only input parameters. The function receives the eye-space position and normal, and computes the result of the ADS shading equation. The result is returned by the function and stored in the shader output variable LightIntensity.

There's more...

Since it makes no sense to read from an output parameter variable, output parameters should only be written to within the function. Their value is undefined.

Within a function, writing to an input-only parameter (qualified with in) is allowed. The function's copy of the argument is modified, and changes are not reflected in the argument.

The const qualifier

The additional qualifier const can be used with input-only parameters (not with out or inout). This qualifier makes the input parameter read-only, so it cannot be written to within the function.

Function overloading

Functions can be overloaded by creating multiple functions with the same name, but with different number and/or type of parameters. As with many languages, two overloaded functions may not differ in return type only.

Passing arrays or structures to a function

It should be noted that when passing arrays or structures to functions, they are passed by value. If a large array or structure is passed, it can incur a large copy operation which may not be desired. It would be a better choice to declare these variables in the global scope.

See also

  • The Implementing per-vertex ambient, diffuse, and specular (ADS) shading recipe
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.222.125.171