WebGL - Modes of Drawing

Hello, future WebGL wizards! ? Today, we're going to dive into the exciting world of drawing modes in WebGL. As your friendly neighborhood computer teacher, I'm here to guide you through this journey, even if you've never written a line of code before. So, grab your virtual paintbrush, and let's create some digital masterpieces!

WebGL - Modes of Drawing

The mode Parameter

Before we start drawing, let's talk about the star of our show: the mode parameter. Think of it as the magic wand that tells WebGL how to connect the dots (or in our case, vertices) to create different shapes and patterns.

In WebGL, when we call the gl.drawArrays() or gl.drawElements() function, we need to specify a mode parameter. This parameter is like giving instructions to a connect-the-dots puzzle – it tells WebGL how to join the points we've defined.

Here's a table of the different drawing modes available in WebGL:

Mode Description
gl.POINTS Draws a single point for each vertex
gl.LINES Draws a line between each pair of vertices
gl.LINE_STRIP Draws a continuous line connecting the vertices
gl.LINE_LOOP Similar to LINE_STRIP, but closes the shape
gl.TRIANGLES Draws a triangle for every three vertices
gl.TRIANGLE_STRIP Draws a connected group of triangles
gl.TRIANGLE_FAN Draws a fan-like shape of connected triangles

Don't worry if these seem confusing now. We'll explore each of them with examples as we go along!

Example – Draw Three Parallel Lines

Let's start with a simple example: drawing three parallel lines. This will help us understand how the mode parameter works in practice.

// First, let's set up our WebGL context
const canvas = document.getElementById('myCanvas');
const gl = canvas.getContext('webgl');

// Now, let's define our vertex shader
const vertexShaderSource = `
    attribute vec2 a_position;
    void main() {
        gl_Position = vec4(a_position, 0.0, 1.0);
    }
`;

// And our fragment shader
const fragmentShaderSource = `
    precision mediump float;
    void main() {
        gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); // Red color
    }
`;

// Create and compile the shaders (don't worry, we'll explain this in detail in future lessons)
const vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, vertexShaderSource);
gl.compileShader(vertexShader);

const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, fragmentShaderSource);
gl.compileShader(fragmentShader);

// Create a program and link the shaders
const program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
gl.useProgram(program);

// Define the positions of our lines
const positions = new Float32Array([
    -0.8, -0.8,  // Line 1 start
    -0.8,  0.8,  // Line 1 end
    -0.3, -0.8,  // Line 2 start
    -0.3,  0.8,  // Line 2 end
     0.2, -0.8,  // Line 3 start
     0.2,  0.8   // Line 3 end
]);

// Create a buffer and put the positions in it
const positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW);

// Tell WebGL how to read the buffer and attribute it to a_position
const positionAttributeLocation = gl.getAttribLocation(program, 'a_position');
gl.enableVertexAttribArray(positionAttributeLocation);
gl.vertexAttribPointer(positionAttributeLocation, 2, gl.FLOAT, false, 0, 0);

// Clear the canvas
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);

// Draw the lines
gl.drawArrays(gl.LINES, 0, 6);

Now, let's break this down:

  1. We set up our WebGL context and define our shaders. Don't worry too much about these now; we'll cover shaders in depth in future lessons.

  2. We create a program and link our shaders to it. This is like preparing our digital paintbrush.

  3. We define the positions of our lines. Each line is defined by two points (its start and end), and each point is defined by two coordinates (x and y). So, we have 6 points in total for our 3 lines.

  4. We create a buffer and put our positions in it. Think of this as loading our paint onto the brush.

  5. We tell WebGL how to read this buffer and associate it with the a_position attribute in our vertex shader.

  6. Finally, we call gl.drawArrays(gl.LINES, 0, 6). This is where the magic happens!

    • gl.LINES is our mode. It tells WebGL to draw lines between pairs of vertices.
    • 0 is the starting index in our array of positions.
    • 6 is the number of vertices to consider (remember, we have 6 points for our 3 lines).

Run this code, and voila! You should see three red parallel lines on a black background. Congratulations, you've just created your first WebGL drawing! ?

Drawing Modes

Now that we've seen gl.LINES in action, let's explore some other drawing modes. We'll use the same setup as before, but change our positions and the drawing mode.

gl.POINTS

Let's start with the simplest mode: gl.POINTS. This mode draws a single point for each vertex.

// ... (previous setup code)

const positions = new Float32Array([
    -0.5, 0.5,
    0.0, 0.0,
    0.5, -0.5
]);

// ... (buffer setup code)

gl.drawArrays(gl.POINTS, 0, 3);

This will draw three red points on your canvas. Simple, right?

gl.LINE_STRIP

Now, let's try gl.LINE_STRIP. This mode draws a continuous line, connecting each vertex to the next.

// ... (previous setup code)

const positions = new Float32Array([
    -0.5, 0.5,
    0.0, -0.5,
    0.5, 0.5
]);

// ... (buffer setup code)

gl.drawArrays(gl.LINE_STRIP, 0, 3);

You should see a V-shaped line connecting the three points.

gl.LINE_LOOP

gl.LINE_LOOP is similar to LINE_STRIP, but it closes the shape by connecting the last vertex back to the first.

// ... (previous setup code)

const positions = new Float32Array([
    -0.5, 0.5,
    0.0, -0.5,
    0.5, 0.5
]);

// ... (buffer setup code)

gl.drawArrays(gl.LINE_LOOP, 0, 3);

This will draw a triangle outline.

gl.TRIANGLES

Now, let's move on to filling shapes with gl.TRIANGLES. This mode draws a separate triangle for every three vertices.

// ... (previous setup code)

const positions = new Float32Array([
    -0.5, -0.5,
    0.5, -0.5,
    0.0, 0.5
]);

// ... (buffer setup code)

gl.drawArrays(gl.TRIANGLES, 0, 3);

You should see a solid red triangle.

gl.TRIANGLE_STRIP

gl.TRIANGLE_STRIP is a bit more complex. It draws a connected group of triangles, where each triangle shares two vertices with the previous one.

// ... (previous setup code)

const positions = new Float32Array([
    -0.5, -0.5,
    0.5, -0.5,
    -0.5, 0.5,
    0.5, 0.5
]);

// ... (buffer setup code)

gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);

This will draw two connected triangles forming a rectangle.

gl.TRIANGLE_FAN

Finally, let's look at gl.TRIANGLE_FAN. This mode draws a fan-like shape, where all triangles share the first vertex as a common point.

// ... (previous setup code)

const positions = new Float32Array([
    0.0, 0.0,    // Center point
    0.5, 0.0,    // Point 1
    0.35, 0.35,  // Point 2
    0.0, 0.5,    // Point 3
    -0.35, 0.35  // Point 4
]);

// ... (buffer setup code)

gl.drawArrays(gl.TRIANGLE_FAN, 0, 5);

This will draw a shape that looks a bit like a quarter of a circle.

And there you have it! We've explored all the drawing modes in WebGL. Remember, the key to mastering these is practice. Try combining different modes, changing colors, and playing with vertex positions. Before you know it, you'll be creating complex WebGL scenes with ease!

In our next lesson, we'll dive deeper into shaders and learn how to add some pizzazz to our drawings with colors and textures. Until then, happy coding, future WebGL artists! ??‍??‍?

Credits: Image by storyset