Shaders are the programs that run on GPU. Shaders are written in OpenGL ES Shader Language (known as ES SL). ES SL has variables of its own, data types, qualifiers, built-in inputs and outputs.
The following table lists the basic data types provided by OpenGL ES SL.
Sr.No. | Type & Description |
---|---|
1 | void Represents an empty value. |
2 | bool Accepts true or false. |
3 | int This is a signed integer data type. |
4 | float This is a floating scalar data type. |
5 | vec2, vec3, vec4 n-component floating point vector |
6 | bvec2, bvec3, bvec4 Boolean vector |
7 | ivec2, ivec3, ivec4 signed integer vector |
8 | mat2, mat3, mat4 2x2, 3x3, 4x4 float matrix |
9 | sampler2D Access a 2D texture |
10 | samplerCube Access cube mapped texture |
There are three main qualifiers in OpenGL ES SL −
Sr.No. | Qualifier & Description |
---|---|
1 | attribute This qualifier acts as a link between a vertex shader and OpenGL ES for per-vertex data. The value of this attribute changes for every execution of the vertex shader. |
2 | uniform This qualifier links shader programs and the WebGL application. Unlike attribute qualifier, the values of uniforms do not change. Uniforms are read-only; you can use them with any basic data types, to declare a variable. Example − uniform vec4 lightPosition; |
3 | varying This qualifier forms a link between a vertex shader and fragment shader for interpolated data. It can be used with the following data types − float, vec2, vec3, vec4, mat2, mat3, mat4, or arrays. Example − varying vec3 normal; |
Vertex shader is a program code, which is called on every vertex. It transforms (move) the geometry (ex: triangle) from one place to other. It handles the data of each vertex (per-vertex data) such as vertex coordinates, normals, colors, and texture coordinates.
In the ES GL code of vertex shader, programmers have to define attributes to handle data. These attributes point to a Vertex Buffer Object written In JavaScript. The following tasks can be performed using vertex shaders along with vertex transformation −
OpenGL ES SL provides the following predefined variables for vertex shader −
Sr.No. | Variables & Description |
---|---|
1 | highp vec4 gl_Position; Holds the position of the vertex. |
2 | mediump float gl_PointSize; Holds the transformed point size. The units for this variable are pixels. |
Take a look at the following sample code of a vertex shader. It processes the vertices of a triangle.
attribute vec2 coordinates; void main(void) { gl_Position = vec4(coordinates, 0.0, 1.0); };
If you observe the above code carefully, we have declared an attribute variable with the name coordinates. (This variable will be associated with the Vertex Buffer Object using the method getAttribLocation(). The attribute coordinates is passed as a parameter to this method along with the shader program object.)
In the second step of the given vertex shader program, the gl_position variable is defined.
gl_Position is the predefined variable which is available only in the vertex shader program. It contains the vertex position. In the above code, the coordinates attribute is passed in the form of a vector. As vertex shader is a per-vertex operation, the gl_position value is calculated for each vertex.
Later, the gl_position value is used by primitive assembly, clipping, culling, and other fixed functionality operations that operate on the primitives after the vertex processing is over.
We can write vertex shader programs for all possible operations of vertex shader, which we will discuss individually in this tutorial.
A mesh is formed by multiple triangles, and the surface of the each triangle is known as a fragment. A fragment shader is the code that runs on every pixel on each fragment. This is written to calculate and fill the color on individual pixels. The following tasks can be performed using fragment shaders −
OpenGL ES SL provides the following predefined variables for fragment shader −
Sr.No. | Variables & Description |
---|---|
1 | mediump vec4 gl_FragCoord; Holds the fragment position within the frame buffer. |
2 | bool gl_FrontFacing; Holds the fragment that belongs to a front-facing primitive. |
3 | mediump vec2 gl_PointCoord; Holds the fragment position within a point (point rasterization only). |
4 | mediump vec4 gl_FragColor; Holds the output fragment color value of the shader |
5 | mediump vec4 gl_FragData[n] Holds the fragment color for color attachment n. |
The following sample code of a fragment shader shows how to apply color to every pixel in a triangle.
void main(void) { gl_FragColor = vec4(0, 0.8, 0, 1); }
In the above code, the color value is stored in the variable gl.FragColor
. The fragment shader program passes the output to the pipeline using fixed function variables; FragColor is one of them. This variable holds the color value of the pixels of the model.
Since shaders are independent programs, we can write them as a separate script and use in the application. Or, you can store them directly in string format, as shown below.
var vertCode = 'attribute vec2 coordinates;' + 'void main(void) {' + ' gl_Position = vec4(coordinates, 0.0, 1.0);' + '}';
Compilation involves following three steps −
To create an empty shader, WebGL provides a method called createShader(). It creates and returns the shader object. Its syntax is as follows −
Object createShader (enum type)
As observed in the syntax, this method accepts a predefined enum value as parameter. We have two options for this −
gl.VERTEX_SHADER for creating vertex shader
gl.FRAGMENT_SHADER for creating fragment shader.
You can attach the source code to the created shader object using the method shaderSource(). Its syntax is as follows −
void shaderSource(Object shader, string source)
This method accepts two parameters −
shader − You have to pass the created shader object as one parameter.
Source − You have to pass the shader program code in string format.
To compile the program, you have to use the method compileShader(). Its syntax is as follow −
compileShader(Object shader)
This method accepts the shader program object as a parameter. After creating a shader program object, attach the source code to it and pass that object to this method.
The following code snippet shows how to create and compile a vertex shader as well as a fragment shader to create a triangle.
// Vertex Shader var vertCode = 'attribute vec3 coordinates;' + 'void main(void) {' + ' gl_Position = vec4(coordinates, 1.0);' + '}'; var vertShader = gl.createShader(gl.VERTEX_SHADER); gl.shaderSource(vertShader, vertCode); gl.compileShader(vertShader); // Fragment Shader var fragCode = 'void main(void) {' + ' gl_FragColor = vec4(0, 0.8, 0, 1);' + '}'; var fragShader = gl.createShader(gl.FRAGMENT_SHADER); gl.shaderSource(fragShader, fragCode); gl.compileShader(fragShader);
After creating and compiling both the shader programs, you need to create a combined program containing both the shaders (vertex & fragment). The following steps need to be followed −
Create a program object by using the method createProgram(). It will return an empty program object. Here is its syntax −
createProgram();
Attach the shaders to the created program object using the method attachShader(). Its syntax is as follows −
attachShader(Object program, Object shader);
This method accepts two parameters −
Program − Pass the created empty program object as one parameter.
Shader − Pass one of the compiled shaders programs (vertex shader, fragment shader)
Note − You need to attach both the shaders using this method.
Link the shaders using the method linkProgram(), by passing the program object to which you have attached the shaders. Its syntax is as follows −
linkProgram(shaderProgram);
WebGL provides a method called useProgram(). You need to pass the linked program to it. Its syntax is as follows −
useProgram(shaderProgram);
The following code snippet shows how to create, link, and use a combined shader program.
var shaderProgram = gl.createProgram(); gl.attachShader(shaderProgram, vertShader); gl.attachShader(shaderProgram, fragShader); gl.linkProgram(shaderProgram); gl.useProgram(shaderProgram);