Shiv ENOW large front end

Company official website: CVTE(Guangzhou Shiyuan Stock)

Team: ENOW team of CVTE software Platform Center for Future Education

The author:

preface

WebGL does not provide basic apis to help you build basic geometries such as rectangles, circles, or cubes and spheres. If you want to draw these geometries in native WebGL, you need to know the basic primitives that WebGL supports for direct drawing. And how to use these primitives to build more complex geometries.

Based primitives

Primitive is the basic graphic element for drawing elements. Complex shapes are drawn in WebGL and are made up of primitive elements. In graphics, there are two kinds of arguments about which primions are supported by the API. One is that the system needs to provide a small number of non-overlapping primions that can be supported by the hardware used, and the hardware needs to be able to efficiently generate these primions. One is that the system needs to support a variety of commonly used primitives, including text, curves, rectangles, circles, and surfaces, to make it easier for users to build complex applications. In WebGL, instead of rectangles and circles and other commonly used graphics, there are 7 types of graphics, which can be classified into three types, namely point, line (line, strip, circular line) and triangle (triangle, triangle belt, triangle fan). Through these types of graphics, developers can compose other complex shapes by themselves. Let’s talk about these primitives in detail.

point

The default size of POINTS in WebGL is 1.0, so using the default value, each point is actually a pixel on the screen. You can also control the size of POINTS by setting gl_pointsize, as shown in the following image:

line

In WebGL, LINES refer to LINES, an independent line segment represented by two points. Multiple line segments can be connected to form a series of line segments. These line segments are connected together and eventually open, which is calledStrip line(LINE_STRIP), if it ends up end to endLoop line(LINE_LOOP), as shown below:

Note that the default width of a line drawn using line primiples is 1px, and WebGL does not provide an API to set the line width, so there are other complicated methods that need to be used to implement a line with width, which I won’t cover here.

triangle

TRIANGLES are a closed plane shape, using a pair of three non-collinear points. Using TRIANGLES, a triangle is the same in appearance as a triangle created by using closed polylines as mentioned above, but TRIANGLES can be drawn using fill mode in the former whereas TRIANGLES cannot be drawn using closed polylines. Multiple triangles form triangular strips (TRIANGLE_STRIP) and triangular fans (TRIANGLE_FAN) by sharing vertices and edges, as shown below

For example, the first triangle uses vertices (v0, v1, v2), the second triangle uses vertices (v3, V4, v5), and so on. If the number of vertices is less than 3, the triangle will not be drawn, and if the number of vertices n is not a multiple of 3, the last one or two vertices will be ignored. Draw a triangle using (v0, v1, v2) as follows:


// Define the coordinates of the three vertices of the triangle (v0, v1, v2), with the center point as the origin of the coordinates and the z-axis as 0
var data = [
  -0.5.0.5.0.0,
  -0.5, -0.5.0.0.0.5.0.5.0.0,]; sendDataToSharder(gl, data); gl.drawArrays(gl.TRIANGLE,0.3);
Copy the code

The triangle belt uses the first three points starting from the first point to form the first triangle, and the three points starting from the second point to form the second triangle. Therefore, to draw the triangle belt composed of two triangles, only four points are needed in total, and the corresponding code snippet and effect are as follows:

V0, v1, v2, v3
var data = [
  -0.5.0.5.0.0,
  -0.5, -0.5.0.0.0.5.0.5.0.0.0.5, -0.5.0.0
];
sendDataToSharder(gl, data);
gl.drawArrays(gl.TRIANGLE_STRIP, 0.4);
Copy the code

Triangle sector using from the first point to start the first three points of the first triangle, the next point (the fourth point) and the last one before a triangle with the second triangle, such as the four vertices are given (where v0, v1, v2 and v3), is the first triangle (where v0, v1, v2), the last side is (v2, The third triangle is (v2, v0, v3). The corresponding code snippet and effect are as follows:

Var data = [0.5, 0.5, 0.0, 0.5, 0.5, 0.0, 0.5, 0.5, 0.0, 0.5, 0.5, 0.0]. sendDataToSharder(gl, data); gl.drawArrays(gl.TRIANGLE_FAN, 0, 4);Copy the code

Note: make sure the vertices are drawn counterclockwise, whether using a triangle fan, a basic triangle, or a triangle strip. If the vertices of a triangle are not in counterclockwise order, triangles that are not in counterclockwise order will not be drawn after backside cull is enabled.

Basic pixel rendering method

WebGL provides two methods for drawing base primitives, namely drawArrays and drawElements

drawArray

Void gl.drawarrays (mode, first, count); void gl.drawarrays (mode, first, count); void gl.drawarrays (mode, first, count);

parameter describe
mode Specify how to draw using the following constant symbols: Gl. POINTS, gl.LINES, G1.line_strip, G1.lineloop, G1.Triangles, Gl.triangle STRIP, Gl.triangLE_fan
first Specifies the number of vertices to draw from
count Specifies how many vertices to draw

Using the drawArrays method, and using TRIANGLES, draw a rectangle using the following TRIANGLES:

// Pass six vertices (v0, v1, v2, v0, v2, v3)
var data = [
  -0.5.0.5.0.0.// v0
  -0.5, -0.5.0.0.// v1
  0.5.0.5.0.0.// v2
  -0.5.0.5.0.0.// v0
  0.5.0.5.0.0.// v2
  0.5, -0.5.0.0.// v3
];
      
sendDataToSharder(gl, data);
gl.drawArrays(gl.TRIANGLES, 0, data.length / 3);
Copy the code

drawElements

A careful observation of drawArrays’ code shows that a rectangle has only four vertices, namely (v0, v1, v2, v3), but we actually pass in six vertices to the buffer (v0, v1, v2, v0, v2, v3), with two redundant vertices. This is 2 * (4 * 3) total of 24 bytes wasted, and as the total number of vertices increases, the memory waste becomes more severe. Void gl.drawElements(mode, count, type, offset); void gl.drawElements(mode, count, type, offset);

parameter describe
mode Specify how to draw using the following constant symbols: Gl. POINTS, gl.LINES, G1.line_strip, G1.lineloop, G1.Triangles, Gl.triangle STRIP, Gl.triangLE_fan
count Specifies how many vertices to draw
type Specifies the type of the value in the element array buffer. Possible values are gl.unsigned_byte and gl.unsigned_short
offset Specifies the offset in the element array buffer. Must be a valid multiple of the given type size

Again using the above drawing rectangle as an example, the improved code using drawElements is as follows:

// Pass four vertices (v0, v1, v2, v3)
var data = [
  -0.5.0.5.0.0.// v0
  -0.5, -0.5.0.0.// v1
  0.5.0.5.0.0.// v2
  0.5, -0.5.0.0.// v3
];
// Generate index data
var indices = [
  0.1.2.// The first triangle
  0.2.3.// The second triangle
];
// Create a new buffer to write index data
function bindIndices(gl, indices) {
  var indicesBuffer = gl.createBuffer();
  gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indicesBuffer);
  gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW);
}
sendDataToSharder(gl, data);
bindIndices(gl, indices);
gl.drawElements(gl.TRIANGLES, indices.length, gl.UNSIGNED_SHORT, 0);
Copy the code

Construct geometry

Draw polygons using line segments

We can construct all kinds of polygon wireframes by using line segment elements  

const center = [0.0] / / circle
const radius = 0.5 / / radius
const count = 12 // The number of polygon edges
const positions = []

for (let i = 0; i <= count; i++) {
  const angle = (Math.PI * 2 * i) / count
  const pointX = radius * Math.cos(angle) + center[0]
  const pointY = radius * Math.sin(angle) + center[1]
  positions.push(pointX, pointY, 0)
}

 gl.drawArrays(gl.LINE_LOOP, 0, positions.length / 3)
Copy the code

Use triangulation to fill polygons

Filling a regular polygon can be problematic because the vertices of the polygon may not be in the same plane, but there is no problem if the polygon is a triangle because the definition of a triangle isA triangle is a closed figure composed of three line segments in the same plane that are not on the same line. Therefore, triangles are the only geometric entities that WebGL recognizes and can fill in. Therefore, the first step in filling polygons in WebGL is to split polygons into triangles. This operation of dividing a polygon into triangles is called in graphicsThe triangle subdivision(Triangulation).Triangulation of simple polygons is relatively easy, while complex polygons are more difficult due to the intersection of sides and overlapping areas. Therefore, we can usually use some third-party libraries, such asearcutTo triangulate. For example, we want to triangulate the following multilateral row:

First of all, we need to list his vertex data, and then use earcut to generate the corresponding index data after triangulation, using the method of drawElement index drawing previously learned to draw, the example code is as follows:

const vertices = [
  -0.7.0.5.0,
  -0.4.0.3.0,
  -0.25.0.71.0,
  -0.1.0.56.0,
  -0.1.0.13.0.0.4.0.21.0.0, -0.6.0,
  -0.3, -0.3.0,
  -0.6, -0.3.0,
  -0.45.0.0.0,];var indices = earcut(vertices, null.3);
sendDataToSharder(gl, program, vertices);
bindIndices(gl, indices);
gl.drawElements(gl.TRIANGLES, indices.length, gl.UNSIGNED_SHORT, 0);
Copy the code

The resources

  1. Interactive Computer Graphics — A Top-down Approach Based on WEBGL (7th edition)
  2. WebGL Programming Guide
  3. Description of how to use the triangle subdivision and vector operations and dealing with polygon time.geekbang.org/column/arti…
  4. Introduction and Practice of webGL – Building planes with Basic Graphics juejin.cn/book/684473…