The shader program created in the previous recipe needs to be loaded and compiled into a binary form. This recipe will be helpful in understanding the procedure of loading and compiling a shader program.
Compiling and linking a shader is necessary so that these programs are understandable and executable by the underlying graphics hardware/platform (that is, the vertex and fragment processors).
The following figure provides an overview of the complete process of creating a shader executable. The different number labels help us understand the order of flow in the build process. Each stage within the build process is marked with the respective OpenGL ES APIs responsible for it.
In order to load and compile the shader source, use the following steps:
Create a
NativeTemplate.h
/NativeTemplate.cpp
and define a function namedloadAndCompileShader
in it. Use the following code, and proceed to the next step for detailed information about this function:GLuint loadAndCompileShader(GLenum shaderType, const char* sourceCode) { // Create the shader GLuint shader = glCreateShader(shaderType); if ( shader ) { // Pass the shader source code glShaderSource(shader, 1, &sourceCode, NULL); // Compile the shader source code glCompileShader(shader); // Check the status of compilation GLint compiled = 0; glGetShaderiv(shader,GL_COMPILE_STATUS,&compiled); if (!compiled) { // Get the info log for compilation failure GLint infoLen = 0; glGetShaderiv(shader,GL_INFO_LOG_LENGTH, &infoLen); if (infoLen) { char* buf = (char*) malloc(infoLen); if (buf) { glGetShaderInfoLog(shader, infoLen, NULL, buf); printf("Could not compile shader %s:" buf); free(buf); } // Delete the shader program glDeleteShader(shader); shader = 0; } } } return shader; }
This function is responsible for loading and compiling a shader source. The argument
shaderType
accepts the type of shader that needs to be loaded and compiled; it can beGL_VERTEX_SHADER
orGL_FRAGMENT_SHADER
. ThesourceCode
specifies the source program of the corresponding shader.Create an empty shader object using the
glCreateShader
OpenGL ES 3.0 API. This shader object is responsible for loading the vertex or fragment source code depending on the specifiedshaderType
parameter:Syntax:
GLuint glCreateShader( Glenum shaderType);
This API returns a non-zero value if the object is successfully created. This value is used as a handle to reference this object. On failure, this function returns
0
. TheshaderType
argument specifies the type of the shader to be created. It must be eitherGL_VERTEX_SHADER
orGL_FRAGMENT_SHADER
:// Create the shader object GLuint shader = glCreateShader(shaderType);
Note
Unlike in C++, where object creation is transparent, in OpenGL ES, the objects are created behind the curtains. You can access, use, and delete the objects as and when required. All the objects are identified by a unique identifier, which can be used for programming purposes.
The created empty shader object (
shader
) needs to be bound first with the shader source in order to compile it. This binding is performed by using theglShaderSource
API:// Load the shader source code glShaderSource(shader, 1, &sourceCode, NULL);
The API sets the shader code string in the shader object,
shader
. The source string is simply copied in the shader object; it is not parsed or scanned.Syntax:
void glShaderSource(GLuint shader, GLsizei count, const GLchar * const *string, const GLint *length);
Variable
Description
shader
This is the handle of the shader object whose source code needs to bind
count
This is the number of elements in the string and length arrays
string
This specifies the array of pointers to strings containing source code that needs to be loaded
length
This specifies the array of string lengths
The count specifies the number of strings in the array. If the length array is
NULL
, this means that all the strings are null terminated. If the values inside in this array are non-zero, it specifies the length of the corresponding string. Any value less than0
is assumed it to be a null-terminated string.Compile the shader using the
glCompileShader
API. It accepts a shader object handle shader:glCompileShader(shader); // Compile the shader
Syntax:
void glCompileShader (GLuint shader);
Variable
Description
shader
This is the handle of the shader object that needs to be compiled
The compilation status of the shader is stored as a state of the shader object. This state can be retrieved using the
glGetShaderiv
OpenGL ES API:GLint compiled = 0; // Check compilation status glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
The
glGetShaderiv
API accepts the handle of the shader andGL_COMPILE_STATUS
as an argument to check the status of the compilation. It retrieves the status in params. The params returnsGL_TRUE
if the last compilation was successful. Otherwise, it returnsGL_FALSE
.Syntax:
void glGetShaderiv(GLuint shader, GLenum pname, GLint *params);
Variable
Description
shader
This is the handle of the shader object whose compilation status needs to be checked.
pname
This specifies the object's state parameter. The symbolic names accepted are
GL_SHADER_TYPE
,GL_DELETE_STATUS
,GL_COMPILE_STATUS
,GL_INFO_LOG_LENGTH
, andGL_SHADER_SOURCE_LENGTH
.params
This returns the requested object parameter state.
In the case of compilation failure, the
glGetShaderiv
API can also be used to retrieve the information log from the OpenGL ES state machine by passingGL_INFO_LOG_LENGTH
as the parameter. TheinfoLen
returns the length of the information log. If the returned value is0
, it means there is no information log. If theinfoLen
value is greater than0
, then the information log message can be retrieved usingglGetShaderInfoLog
:if (!compiled) { // Handle Errors GLint infoLen = 0; // Check error string length glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen); . . . . . }
Use
glGetShaderInfoLog
to get the error report:Syntax:
void glGetShaderInfoLog(GLuint shader, GLsizei maxLength, GLsizei*length, GLchar* infoLog);
Variable
Description
shader
This is the handle of the shader object whose information log is required
maxLength
This is the size of the character buffer to store the returned information log
length
This is the length of the string returned by the information length
infoLog
This specifies array of characters
The shader is deleted if the shader source cannot be compiled. Delete the shader object using the
glDeleteShader
API.Syntax:
void glDeleteShader(GLuint shader);
Variable
Description
shader
This is the handle of the shader object that needs to be deleted
Return the shader object ID if the shader is compiled successfully:
return shader; // Return the shader object ID
The loadAndCompileShader
function first creates an empty shader object. This empty object is referenced by the shader
variable. This object is bound with the source code of the corresponding shader. The source code is compiled through a shader object using the glCompileShader
API. If the compilation is successful, the shader object handle is returned successfully. Otherwise, the shader object returns 0
and needs to be deleted explicitly using glDeleteShader
. The status of the compilation can be checked using glGetShaderiv
with GL_COMPILE_STATUS
.
In order to differentiate among various versions of OpenGL ES and GL Shading Language, it is useful to get this information from the current driver of your device. This will be helpful to make the program robust and manageable by avoiding errors caused by version upgrade or application being installed on older versions of OpenGL ES and GLSL. The other vital information can be queried from the current driver, such as the vendor, renderer, and available extensions supported by the device driver. This information can be queried using the glGetString
API. This API accepts a symbolic constant and returns the queried system metrics in the string form. The printGLString
wrapper function in our program helps in printing device metrics:
static void printGLString(const char *name, GLenum s) { printf("GL %s = %s\n", name, (const char *) glGetString(s)); } // Print the OpenGL ES system metrics void printOpenGLESInfo(){ printGLString("Version", GL_VERSION); printGLString("Vendor", GL_VENDOR); printGLString("Renderer", GL_RENDERER); printGLString("Extensions", GL_EXTENSIONS); printGLString("GLSL version", GL_SHADING_LANGUAGE_VERSION); }