1. An overview of the

In the previous tutorial, WebGL Easy Tutorial ii: Transferring data to Shaders, I changed the size and color of the drawing points by transferring data to shaders. The previous example could only draw a point. What if you need to draw something slightly more complex, such as a triangle, rectangle, or cube? This is where we need a very handy mechanism called buffer objects.

As we know, OpenGL/WebGL needs to access video memory data for graphics work. Programming languages like C or JS always store data in memory — that is, they need to transfer the data in memory to video memory for OpenGL/WebGL to draw. Applying for, transmitting, and releasing data is an I/O operation. For AN I/O operation, the efficiency of multiple read/write operations is inferior to that of a single read/write operation. Buffer objects are designed to solve both of these problems: we can fill buffer objects with large amounts of vertex data at once for use by vertex shaders.

Here is an example of drawing a triangle to illustrate the use of buffer objects. Generally speaking, the basic unit of any 3D model is triangle, and knowing how to draw a triangle can draw arbitrarily complex graphics.

2. Example: Draw a triangle

As in the previous example, the triangle drawing instance contains both HTML and JavaScript.

1) HelloTriangle.html

<! DOCTYPEhtml>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>Hello Triangle</title>
  </head>

  <body onload="main()">
    <canvas id="webgl" width="400" height="400">
    Please use a browser that supports "canvas"
    </canvas>

    <script src=".. /lib/webgl-utils.js"></script>
    <script src=".. /lib/webgl-debug.js"></script>
    <script src=".. /lib/cuon-utils.js"></script>
    <script src="HelloTriangle.js"></script>
  </body>
</html>
Copy the code

This HTML code is almost unchanged from the previous example, introducing the need for webGL components and the main drawing code, Hellotriangl.js. Without special changes, future HTML code will not be introduced.

2) HelloTriangle.js

// Vertex shader program
var VSHADER_SOURCE =
  'attribute vec4 a_Position; \n' + // attribute variable
  'void main() {\n' +
  '  gl_Position = a_Position;\n' + // Set the vertex coordinates of the point
  '}\n';

// Chip shader program
var FSHADER_SOURCE =
  'precision mediump float; \n' +
  'uniform vec4 u_FragColor; \n' +  / / uniform number of variations
  'void main() {\n' +
  'gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); \n' +
  '}\n';

function main() {
  // Get the 
      
        element
      
  var canvas = document.getElementById('webgl');

  // Get the WebGL rendering context
  var gl = getWebGLContext(canvas);
  if(! gl) {console.log('Failed to get the rendering context for WebGL');
    return;
  }

  // Initialize the shader
  if(! initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {console.log('Failed to intialize shaders.');
    return;
  }

  // Set the vertex position
  var n = initVertexBuffers(gl);
  if (n < 0) {
    console.log('Failed to set the positions of the vertices');
    return;
  } 

  // specify to clear the color of 
      
  gl.clearColor(0.0.0.0.0.0.1.0);

  / / to empty < canvas >
  gl.clear(gl.COLOR_BUFFER_BIT);

  // Draw a triangle
  gl.drawArrays(gl.TRIANGLES, 0.3);
}

function initVertexBuffers(gl) {
  var vertices = new Float32Array([
    0.0.5,   -0.5, -0.5.0.5, -0.5
  ]);
  var n = 3; // Number of points

  // Create a buffer object
  var vertexBuffer = gl.createBuffer();
  if(! vertexBuffer) {console.log('Failed to create the buffer object');
    return -1;
  }

  // Bind the buffer object to the target
  gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
  // Writes data to the buffer object
  gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);

  var a_Position = gl.getAttribLocation(gl.program, 'a_Position');
  if (a_Position < 0) {
    console.log('Failed to get the storage location of a_Position');
    return -1;
  }
  // Assign the buffer object to the a_Position variable
  gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false.0.0);

  // Connect the a_Position variable to the buffer object assigned to it
  gl.enableVertexAttribArray(a_Position);

  return n;
}
Copy the code

The main change is that instead of passing data to the shader through the gl.vertexattrib3f () function, a custom initVertexBuffers() function is used to initialize vertex positions. It is through the buffer object that data is passed to the shader in this function.

3) Buffer object

In initVertexBuffers(), you can see that a JavaScript array (Float32Array is a special typed array introduced by WebGL that can hold a large number of elements of the same type) is first initialized, which is what the buffer needs to write:

var vertices = new Float32Array([
    0.0.5,   -0.5, -0.5.0.5, -0.5
  ]);
Copy the code

This data is passed to the vertex shader through the buffer object in the following five steps:

CreateBuffer object (gl.createbuffer ())

  // Create a buffer object
  var vertexBuffer = gl.createBuffer();
  if(! vertexBuffer) {console.log('Failed to create the buffer object');
    return -1;
  }
Copy the code

WebGL creates a buffer object through gl.createBuffer(), which tells the WebGL system to open up video memory to accept data transferred from memory. The function is described as follows:

(2) bindBuffer object (gl.bindbuffer ())

  // Bind the buffer object to the target
  gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
Copy the code

Since buffer objects can have multiple uses and need to be bound to different targets after they are created, the gl.array_buffer parameter indicates that buffer objects store data about vertices. The binding function gl.bindbuffer () is specified as follows:

(3) Write data to buffer object (gl.bufferData())

  // Writes data to the buffer object
  gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
Copy the code

This code means transferring data from the array vertices to a buffer object on the target gl.array_buffer. The function is described as follows:

(4) Assign buffer object to attribute variable (gl.vertexattribPointer ())

  var a_Position = gl.getAttribLocation(gl.program, 'a_Position');
  if (a_Position < 0) {
    console.log('Failed to get the storage location of a_Position');
    return -1;
  }
  // Assign the buffer object to the a_Position variable
  gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false.0.0);
Copy the code

asWebGL Tutorial part 2: Transferring data to shadersGetAttribLocation () gets the address of the vertex shader attribute variable a_Position. The difference here is that the function gl.vertexattribPointer () is used to assign the entire buffer object, the vertex data, to the attribute variable a_Position at once. The function is described as follows:

(5) open the attribute variables (gl) enableVertexAttribArray ())

/ / connection a_Position variables and gl buffer object assigned to it. The enableVertexAttribArray (a_Position);Copy the code

The last step is very simple: open the attribute variable and establish a connection between the buffer and the attribute variable. Its function is described as follows:

Through the above five steps, the shader can draw correctly from the data of the buffer object. The schematic diagram is as follows:

4) Basic graph drawing

Instead of drawing points in the previous two tutorials, draw a triangle:

  // Draw a triangle
  gl.drawArrays(gl.TRIANGLES, 0.3);
Copy the code

As you can see, the same function gl.drawarrays () is used to draw. The function is described as follows:



The second and third arguments are very simple and represent which vertex data to draw from. For example, if I draw a triangle here, I draw it from point 1 to point 3.

The first parameter is very powerful and represents seven basic graphs that can be drawn:





The basic schematic diagram is as follows:

Results 3.

Open your browser to Hellotriangle.html and see that a red triangle has been drawn as follows:

4. Reference

Some of the original code and illustrations come from the WebGL Programming Guide. Code and data addresses

The original link