OpenGL 4 Shading Language Cookbook(Second Edition)
上QQ阅读APP看书,第一时间看更新

Getting a list of active vertex input attributes and locations

As covered in the previous recipe, the input variables within a vertex shader are linked to generic vertex attribute indices at the time the program is linked. If we need to specify the relationship, we can either use layout qualifiers within the shader, or we could call glBindAttribLocation before linking.

However, it may be preferable to let the linker create the mappings automatically and query for them after program linking is complete. In this recipe, we'll see a simple example that prints all the active attributes and their indices.

Getting ready

Start with an OpenGL program that compiles and links a shader pair. You could use the shaders from the previous recipe.

As in previous recipes, we'll assume that the handle to the shader program is stored in a variable named programHandle.

How to do it...

After linking and enabling the shader program, use the following code to display the list of active attributes:

  1. Start by querying for the number of active attributes:
    GLint numAttribs;
    glGetProgramInterfaceiv(programHandle, GL_PROGRAM_INPUT,GL_ACTIVE_RESOURCES, &numAttribs);
  2. Loop through each attribute and query for the length of the name, the type and the attribute location, and print the results to standard out:
    GLenum properties[] = {GL_NAME_LENGTH, GL_TYPE, GL_LOCATION};
    
    printf("Active attributes:\n");
    for( int i = 0; i < numAttribs; ++i ) {
      GLint results[3];
      glGetProgramResourceiv(programHhandle, GL_PROGRAM_INPUT,i, 3, properties, 3, NULL, results);
    
      GLint nameBufSize = results[0] + 1;
      char * name = new char[nameBufSize];
      glGetProgramResourceName(programHandle, GL_PROGRAM_INPUT, i, nameBufSize, NULL, name);
      printf("%-5d %s (%s)\n", results[2], name, getTypeString(results[1]));
      delete [] name;
    }

How it works...

In step 1, we query for the number of active attributes, by calling glGetProgramInterfaceiv. The first argument is the handle to the program object, and the second (GL_PROGRAM_INPUT) indicates that we are querying for information about the program input variables (the vertex attributes). The third argument (GL_ACTIVE_RESOURCES) indicates that we want the number of active resources. The result is stored in the location pointed to by the last argument numAttribs.

Now that we have the number of attributes, we query for information about each one. The indices of the attributes run from 0 to numAttribs-1. We loop over those indices and for each we call glGetProgramResourceiv to get the length of the name, the type and the location. We specify what information we would like to receive by means of an array of GLenum values called properties. The first argument is the handle to the program object, the second is the resource that we are querying (GL_PROGRAM_INPUT). The third is the index of the attribute, the fourth is the number of values in the properties array, which is the fifth argument. The properties array contains GLenums, which specify the specific properties we would like to receive. In this example, the array contains: GL_NAME_LENGTH, GL_TYPE, and GL_LOCATION, which indicates that we want the length of the attribute's name, the data type of the attribute and its location. The sixth argument is the size of the buffer that will receive the results; the seventh argument is a pointer to an integer that would receive the number of results that were written. If that argument is NULL, then no information is provided. Finally, the last argument is a pointer to a GLint array that will receive the results. Each item in the properties array corresponds to the same index in the results array.

Next, we retrieve the name of the attribute by allocating a buffer to store the name and calling glGetProgramResourceName. The results array contains the length of the name in the first element, so we allocate an array of that size with an extra character just for good measure. The OpenGL documentation says that the size returned from glGetProgramResourceiv includes the null terminator, but it doesn't hurt to make sure by making a bit of additional space. In my tests, I've found this to be necessary on the latest NVIDIA drivers.

Finally, we get the name by calling glGetProgramResourceName, and then print the information to the screen. We print the attribute's location, name and type. The location is available in the third element of the results array, and the type is in the second. Note the use of the function getTypeString. This is a simple custom function that just returns a string representation of the data type. The data type is represented by one of the OpenGL defined constants GL_FLOAT, GL_FLOAT_VEC2, GL_FLOAT_VEC3, and so on. The getTypeString function consists of just one big switch statement returning a human-readable string corresponding to the value of the parameter (see the source code for glslprogram.cpp in the example code for this book).

The output of the previous code looks like this when it is run on the shaders from the previous recipes:

Active attributes:
1 VertexColor (vec3)
0 VertexPosition (vec3)

There's more...

It should be noted that in order for a vertex shader input variable to be considered active, it must be used within the vertex shader. In other words, a variable is considered active if it is determined by the GLSL linker that it may be accessed during program execution. If a variable is declared within a shader, but not used, the previous code will not display the variable because it is not considered active and effectively ignored by OpenGL.

The previous code is only valid for OpenGL 4.3 and later. Alternatively, you can achieve similar results with the functions glGetProgramiv, glGetActiveAttrib and glGetAttribLocation.

See also

  • The Compiling a shader recipe
  • The Linking a shader program recipe
  • The Sending data to a shader using vertex attributes and vertex buffer objects recipe