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.
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.
To implement ADS shading using functions, use the following code:
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); }
in vec3 LightIntensity; layout( location = 0 ) out vec4 FragColor; void main() { FragColor = vec4(LightIntensity, 1.0); }
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
.
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 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.
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.
18.222.125.171