Creating WebGL Buffers

Now that we understand how to define a geometry using vertices and indices, let's render a square. Once we have created the JavaScript arrays that define the vertices and indices for our geometry, the next step is to create the respective buffers. In this case, we have a simple square on the x-y plane with the z values set as 0:

const vertices = [
-0.5, 0.5, 0,
-0.5, -0.5, 0,
0.5, -0.5, 0,
0.5, 0.5, 0
];

const positionBuffer = gl.createBuffer();
Clipspace Coordinates

These vertices are defined in clipspace coordinates, because WebGL only deals with clipspace coordinates. Clipspace coordinates always go from -1 to +1, regardless of the size of the canvas. In later chapters, we will cover coordinates in more detail and learn how to convert between different coordinate systems.

In Chapter 1, Getting Started, you may remember learning that WebGL operates as a state machine. Now, when positionBuffer is made the currently-bound WebGL buffer, any subsequent buffer operation will be executed on this buffer until it is unbound, or another buffer is made the current one with a bound call. We can bind a buffer with the following instruction:

gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);

The first parameter is the type of buffer we are creating. We have two options for this parameter:

  • gl.ARRAY_BUFFER: Vertex data
  • gl.ELEMENT_ARRAY_BUFFER: Index data

In the previous example, we created the buffer for vertex coordinates; therefore, we use ARRAY_BUFFER. For indices, the ELEMENT_ARRAY_BUFFER type is used.

Bound Buffer Operations

WebGL will always access the currently-bound buffer looking for the data. This means that we need to ensure that we always have bound a buffer before calling any other operation for geometry processing. If there is no buffer bound, you will obtain the INVALID_OPERATION error.

Remember that drawArrays uses VBOs. Once we have bound a buffer, we need to pass along its contents. We do this with the bufferData function:

gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);

In this example, the vertices variable is a normal JavaScript array that contains the vertex coordinates. WebGL does not accept JavaScript arrays as a parameter for the bufferData method. Instead, WebGL requires JavaScript typed array so that the buffer data can be processed in its native binary form with the objective of speeding up geometry-processing performance.

The typed arrays used by WebGL include Int8ArrayUint8ArrayInt16ArrayUint16ArrayInt32ArrayUint32ArrayFloat32Array, and Float64Array.

It's important to note that vertex coordinates can be float, but indices are always integers. Therefore, we will use Float32Array for VBOs and Uint16Array for IBOs in this book. These two types represent the largest typed arrays that you can use in WebGL per rendering call. Other types may or may not be present in your browser, as this specification is not yet final at the time of this book's publication.

Since the indices support in WebGL is restricted to 16-bit integers, an index array can only be 65,535 elements in length. If you have a geometry that requires more indices, you will need to use several rendering calls. More about rendering calls will be presented later in this chapter.

JavaScript Typed Arrays

Specifications for typed arrays can be found at http://www.khronos.org/registry/typedarray/specs/latest/.

Finally, it is a good practice to unbind the buffer. We can achieve this by calling the following instruction:

gl.bindBuffer(gl.ARRAY_BUFFER, null);

We will repeat the same calls described here for every WebGL buffer (VBO or IBO) that we will use.

Let's review what we have just learned with an example. We are going to look at an example from ch02_01_square.html to see the definition of VBOs and IBOs for a square:

// Set up the buffers for the square
function initBuffers() {
/*
V0 V3
(-0.5, 0.5, 0) (0.5, 0.5, 0)
X---------------------X
| |
| |
| (0, 0) |
| |
| |
X---------------------X
V1 V2
(-0.5, -0.5, 0) (0.5, -0.5, 0)
*/
const vertices = [
-0.5, 0.5, 0,
-0.5, -0.5, 0,
0.5, -0.5, 0,
0.5, 0.5, 0
];

// Indices defined in counter-clockwise order
indices = [0, 1, 2, 0, 2, 3];

// Setting up the VBO
squareVertexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, squareVertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices),
gl.STATIC_DRAW);

// Setting up the IBO
squareIndexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, squareIndexBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices),
gl.STATIC_DRAW);

// Clean
gl.bindBuffer(gl.ARRAY_BUFFER, null);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
}

If you want to see this scene in action, launch the ch02_01_square.html file in your browser.

To summarize, for every buffer, we want to do the following:

  • Create a new buffer
  • Bind it to make it the current buffer
  • Pass the buffer data using one of the typed arrays
  • Unbind the buffer
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.17.6.75