WebGL - Shaders



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.

Data Types

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

Qualifiers

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

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 −

  • Vertex transformation
  • Normal transformation and normalization
  • Texture coordinate generation
  • Texture coordinate transformation
  • Lighting
  • Color material application

Predefined Variables

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.

Sample Code

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

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.

Fragment Shader

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 −

  • Operations on interpolated values
  • Texture access
  • Texture application
  • Fog
  • Color sum

Predefined Variables

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.

Sample Code

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.

Storing and Compiling the Shader Programs

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);' +
   '}';

Compiling the Shader

Compilation involves following three steps −

  • Creating the shader object
  • Attaching the source code to the created shader object
  • Compiling the program

Creating the Vertex Shader

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.

Attaching the Source to the 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.

Compiling the Program

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);

Combined Program

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
  • Attach both the shaders
  • Link both the shaders
  • Use the program

Create a Program Object

Create a program object by using the method createProgram(). It will return an empty program object. Here is its syntax −

createProgram();

Attach the Shaders

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

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);

Use the Program

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); 
Advertisements