Time for Action: Rendering Modes

Let's revisit the signature of the drawElements function:

gl.drawElements(mode, count, type, offset)

The first parameter determines the type of primitives that we are rendering. In the following section, we will see the different rendering modes with examples.

Follow the given steps:

  1. Open the ch02_04_rendering-modes.html file in your browser. This example follows the same structure as in the previous section.
  2. Open ch02_04_rendering-modes.html in your editor and scroll down to the initBuffers function:
function initBuffers() {
const vertices = [
-0.5, -0.5, 0,
-0.25, 0.5, 0,
0.0, -0.5, 0,
0.25, 0.5, 0,
0.5, -0.5, 0
];

indices = [0, 1, 2, 0, 2, 3, 2, 3, 4];

// Create VAO
trapezoidVAO = gl.createVertexArray();

// Bind VAO
gl.bindVertexArray(trapezoidVAO);

const trapezoidVertexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, trapezoidVertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices),
gl.STATIC_DRAW);
// Provide instructions to VAO
gl.vertexAttribPointer(program.aVertexPosition, 3, gl.FLOAT,
false, 0, 0);
gl.enableVertexAttribArray(program.aVertexPosition);

trapezoidIndexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, trapezoidIndexBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices),
gl.STATIC_DRAW);

// Clean
gl.bindVertexArray(null);
gl.bindBuffer(gl.ARRAY_BUFFER, null);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
}
  1. Here, you will see that we are drawing a trapezoid. However, on screen, you will see two triangles! Later, we'll see how this happened.
  2. At the top of the page, there is a settings controller that allows you to select the different rendering modes that WebGL provides:

let gl,
canvas,
program,
indices,
trapezoidVAO,
trapezoidIndexBuffer,
// Global variable that captures the current rendering mode type
renderingMode = 'TRIANGLES';
  1. When you select any option from the settings, you are changing the value of the renderingMode variable defined at the top of the code (scroll up if you want to see where it is defined). The code that sets up the settings controller is inside the initControls function. We will cover this functionality later.
  2. To see how each option modifies the rendering, scroll to the draw function:
function draw() {
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);

// Bind VAO
gl.bindVertexArray(trapezoidVAO);

gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, trapezoidIndexBuffer);

// Depending on the rendering mode type, we will draw differently
switch (renderingMode) {
case 'TRIANGLES': {
indices = [0, 1, 2, 2, 3, 4];
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new
Uint16Array(indices), gl.STATIC_DRAW);
gl.drawElements(gl.TRIANGLES, indices.length,
gl.UNSIGNED_SHORT,
0);
break;
}
case 'LINES': {
indices = [1, 3, 0, 4, 1, 2, 2, 3];
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new
Uint16Array(indices), gl.STATIC_DRAW);
gl.drawElements(gl.LINES, indices.length, gl.UNSIGNED_SHORT,
0);
break;
}
case 'POINTS': {
indices = [1, 2, 3];
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new
Uint16Array(indices), gl.STATIC_DRAW);
gl.drawElements(gl.POINTS, indices.length, gl.UNSIGNED_SHORT,
0);
break;
}
case 'LINE_LOOP': {
indices = [2, 3, 4, 1, 0];
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new
Uint16Array(indices), gl.STATIC_DRAW);
gl.drawElements(gl.LINE_LOOP, indices.length,
gl.UNSIGNED_SHORT, 0);
break;
}
case 'LINE_STRIP': {
indices = [2, 3, 4, 1, 0];
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new
Uint16Array(indices), gl.STATIC_DRAW);
gl.drawElements(gl.LINE_STRIP, indices.length,
gl.UNSIGNED_SHORT, 0);
break;
}
case 'TRIANGLE_STRIP': {
indices = [0, 1, 2, 3, 4];
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new
Uint16Array(indices), gl.STATIC_DRAW);
gl.drawElements(gl.TRIANGLE_STRIP, indices.length,
gl.UNSIGNED_SHORT, 0);
break;
}
case 'TRIANGLE_FAN': {
indices = [0, 1, 2, 3, 4];
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new
Uint16Array(indices), gl.STATIC_DRAW);
gl.drawElements(gl.TRIANGLE_FAN, indices.length,
gl.UNSIGNED_SHORT, 0);
break;
}
}

// Clean
gl.bindVertexArray(null);
gl.bindBuffer(gl.ARRAY_BUFFER, null);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
}
  1. You will see that after binding the IBO trapezoidIndexBuffer with the following instruction:
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, trapezoidIndexBuffer);
  1. You also have a switch statement where there is some code that executes, depending on the value of the renderingMode variable.
  2. For each mode, we define the contents of the JavaScript array indices. Then, we pass this array to the currently-bound buffer, trapezoidIndexBuffer, by using the bufferData function. Finally, we call the drawElements function.
  3. Let's see what each mode does:
  1. The following diagram can be useful in visualizing these various rendering modes. That being said, it's easiest to see these modes in action by changing the setting's drop-down values and seeing the various results:

  1. Let's make some changes by editing ch02_04_rendering-modes.html so that when you select the TRIANGLES option, you render the trapezoid instead of two triangles.
Hint

You need one extra triangle in the indices array.
  1. Save the file and test it in your browser.
  2. Edit the web page so that you draw the letter M using the LINES option.
Hint

You need to define four lines in the indices array.
  1. Just like before, save your changes and test them in your browser.
  2. Using the LINE_LOOP mode, draw only the boundary of the trapezoid.

What just happened?

This simple exercise helped us see the different rendering modes supported by WebGL. These different modes determine how to interpret vertex and index data to render an object.