• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#  Using WebGL to Draw Graphics
2
3## When to Use
4
5Web Graphics Library (WebGL) is used for rendering interactive 2D graphics. WebGL used in OpenHarmony is based on OpenGL for Embedded Systems (OpenGL ES). It can be used in the HTML5 **\<canvas>** element without using plug-ins and supports cross-platform. WebGL is programmed by JavaScript code. Its APIs can implement graphics rendering and acceleration by using GPU hardware provided by the user equipment. For more information, see [WebGL™](https://www.khronos.org/registry/webgl/specs/latest/1.0/).
6
7> **NOTE**
8>
9> WebGL can be used only in the JavaScript-compatible web-like development paradigm.
10
11## Basic Concepts
12
13### Shader program
14
15The shader program, also known as WebGL program, is a JavaScript object responsible for associating the shader with the buffer. A WebGLProgram object consists of two compiled WebGL shaders: a vertex shader and a fragment shader.
16
17###  Shader
18
19Shaders are instructions and data that run in a graphics card. In WebGL, shaders are written in the OpenGL Shading Language (GLSL).
20
21There are vertex shaders and fragment shaders. The interaction between vertex shaders and fragment shaders involves rasterization.
22
23- The vertex shader is mainly used to receive the coordinates of a point in a 3D space, convert the coordinates into coordinates in a 2D space, and output the coordinates.
24
25- The fragment shader is mainly used to output a color value for each pixel being processed.
26
27###  Rasterization
28
29Rasterization is the process of converting the coordinates in a 2D space output by the vertex shader into pixels to be processed and passing the pixels to the fragment shader.
30
31### Frame buffer
32
33The frame buffer provides an alternative rendering target for the drawing buffer. They are a collection of colors, letters, depths, and template buffers and are usually used to render images.
34
35###  Texture
36
37A texture is an image that can be applied to the surface of a 3D model. Textures in WebGL have many properties, including width, height, format, and type. When using a texture, load it into WebGL and bind it to a texture unit.
38
39
40## Variables and APIs
41
42### Variables
43
44| Type        | Web IDL Type        | Description                                                        |
45| ------------ | -------------------- | ------------------------------------------------------------ |
46| GLenum     | unsigned long     | Enum.       |
47| GLboolean  | boolean            | Boolean, either **true** or **false**.|
48| GLbitfield | unsigned long      | Unsigned integer. Multiple bit flags can be contained, and each bit flag represents a specific option.|
49| GLbyte     | byte               | Signed integer represented by 2's complement of 8 bits (one byte).               |
50| GLshort    | short              | Signed integer represented by 2's complement of 16 bits.                            |
51| GLint      | long               | Signed integer represented by 2's complement of 32 bits.                          |
52| GLsizei    | long               | Size, for example, the width and height of the drawing buffer.     |
53| GLintptr   | long long          | A special type used to represent a pointer. It is usually used to specify the offset of a buffer object.      |
54| GLsizeiptr | long long          | A special type used to represent a pointer. It is usually used to specify the size of a buffer object.        |
55| GLubyte    | octet              | Unsigned integer represented by 2's complement of 8 bits (one byte).                |
56| GLushort   | unsigned short     | Unsigned integer represented by 2's complement of 16 bits.                         |
57| GLuint    | unsigned short     | Signed integer represented by 2's complement of 32 bits.                       |
58| GLfloat   | unrestricted float | 32-bit IEEE floating point number.                            |
59| GLclampf   | unrestricted float | 32-bit IEEE floating point number.                                     |
60
61### Available APIs
62
63| API                                                      | Description                                                  |
64| ------------------------------------------------------------ | ------------------------------------------------------ |
65| canvas.getContext                                            | Obtains the canvas context.                                |
66| webgl.createBuffer(): WebGLBuffer \| null                    | Creates and initializes a WebGL buffer.                         |
67| webgl.bindBuffer(target: GLenum, buffer: WebGLBuffer \| null): void | Binds a WebGL buffer to the target.                     |
68| webgl.bufferData(target: GLenum, srcData: ArrayBufferView, usage: GLenum, srcOffset: GLuint, length?: GLuint): void | Creates and initializes the WebGL buffer's data store.                       |
69| webgl.getAttribLocation(program: WebGLProgram, name: string): GLint | Obtains the address of the **attribute** variable in the shader from the given WebGL program.|
70| webgl.vertexAttribPointer(index GLuint, size: GLint, type: GLenum, normalized: GLboolean, stride: GLsizei, offset: GLintptr): void | Assigns a Buffer object to a variable.                              |
71| webgl.enableVertexAttribArray(index: GLuint): void           | Connects a variable to the Buffer object allocated to it.                      |
72| webgl.clearColor(red: GLclampf, green:GLclampf, blue: GLclampf, alpha: GLclampf): void | Clears the specified color on the canvas.                        |
73| webgl.clear(mask: GLbitfield): void                          | Clears the canvas.                                  |
74| webgl.drawArrays(mode: GLenum, first:;GLint, count: GLsizei): void | Draws data.                                        |
75| webgl.flush(): void                                          | Flushes data to the GPU and clears the buffer.                           |
76| webgl.createProgram(): WebGLProgram \| null                  | Creates a WebGLProgram object.                                  |
77
78## How to Develop
79
80 The following uses a color square as an example to describe how to draw a 2D graphic using WebGL.
81
821. Before using WebGL for 3D rendering, create a **\<canvas>** element. The following code snippet creates a **\<canvas>** element and sets an onclick event handler to initialize the WebGL context.
83
84   ```hml
85    <div class="container">
86        <canvas ref="canvas1" style="width : 400px; height : 400px; background-color : lightyellow;"></canvas>
87        <button class="btn-button" onclick="BtnColorTriangle">BtnColorTriangle</button>
88    </div>
89   ```
90
912. Set the WebGL context.
92
93   - Call the **main()** function in the JavaScript code after loading to set the WebGL context and start rendering.
94
95   - Call the **getContext** function, with the **webgl** parameter passed in, to obtain the WebGL rendering context. If the browser does not support WebGL, **null** is returned. If the WebGL context is initialized, the variable **'gl'** is used to reference the context.
96
97   ```js
98   function main() {
99     const canvas = document.querySelector("#glcanvas");
100     // Initialize the WebGL context.
101     const gl = canvas.getContext("webgl");
102
103     // Check the support for WebGL.
104     if (!gl) {
105       alert("Your browser, operating system, or hardware may not support WebGL.");
106       return;
107     }
108     // Use completely opaque black to clear all images.
109     gl.clearColor(0.0, 0.0, 0.0, 1.0);
110     // Clear the buffer with the color specified above.
111     gl.clear(gl.COLOR_BUFFER_BIT);
112   }
113   ```
1143. Define the vertex shader.
115
116   The vertex shader needs to perform the necessary transformation (for example, adjustment or calculation) on the vertex coordinates, saves the new vertices in a special variable provided by GLSL, and returns the variable.
117
118   Before performing matrix calculations, you need to import the open-source library gl-matrix. You can download it from the [gl-matrix official website](https://glmatrix.net/) or install it using the following npm command:
119   `npm install gl-matrix`
120   ```js
121   // Import mat4.
122   import { mat4 } from 'gl-matrix'
123   const vsSource = `
124       attribute vec4 aVertexPosition;
125       uniform mat4 uModelViewMatrix;
126       uniform mat4 uProjectionMatrix;
127       void main() {
128         gl_Position = uProjectionMatrix * uModelViewMatrix * aVertexPosition;
129       }
130     `;
131   ```
132
1334. Define the fragment shader.
134
135   After the vertex shader processes the vertices, the fragment shader is called once by each pixel to be drawn.
136
137   ```js
138   const fsSource = `
139       void main() {
140         gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
141       }
142    `;
143   ```
1445. Pass the shader to WebGL.
145
146   Pass the vertex shader and fragment shader defined to WebGL and compile them together.
147
148   The following code uses **loadShader()** to transfer the type and source for the shader. In this example, two shaders are created and attached to a shader program. If the compilation or linking fails, an alert is displayed.
149
150   ```js
151   // Initialize the shader program so that WebGL knows how to draw data.
152   function initShaderProgram(gl, vsSource, fsSource) {
153     const vertexShader = loadShader(gl, gl.VERTEX_SHADER, vsSource);
154     const fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, fsSource);
155     // Create a shader program.
156     const shaderProgram = gl.createProgram();
157     gl.attachShader(shaderProgram, vertexShader);
158     gl.attachShader(shaderProgram, fragmentShader);
159     gl.linkProgram(shaderProgram);
160     // An alert is displayed if the creation fails.
161     if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
162       alert(
163         "Unable to initialize the shader program: "+
164        gl.getProgramInfoLog(shaderProgram),
165       );
166       return null;
167     }
168     return shaderProgram;
169   }
170   // Create a shader of the specified type, upload the source code, and compile the source code.
171   function loadShader(gl, type, source) {
172     const shader = gl.createShader(type);
173     // Send the resource to the shader object.
174     gl.shaderSource(shader, source);
175     // Compile the shader program.
176     gl.compileShader(shader);
177     // Check whether the compilation is successful.
178     if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
179       alert(
180      "Error occurred when compiling the shader: "+ gl.getShaderInfoLog (shader),
181       );
182       gl.deleteShader(shader);
183       return null;
184     }
185     return shader;
186   }
187   ```
1886. Find the input location assigned by WebGL.
189
190   - After creating the shader program, find the input location allocated by WebGL. There is one property and two Uniforms.
191
192   - The property value is assigned by the buffer. For each iteration of the vertex shader, a new value is assigned.
193
194   - Uniforms are similar to JavaScript global variables. They use the same value in all iterations of the shader. Because the property location is specific to a shader program, they can be stored together for easy delivery.
195
196   ```js
197   const programInfo = {
198     program: shaderProgram,
199     attribLocations: {
200       vertexPosition: gl.getAttribLocation(shaderProgram, "aVertexPosition"),
201     },
202     uniformLocations: {
203       projectionMatrix: gl.getUniformLocation(shaderProgram, "uProjectionMatrix"),
204       modelViewMatrix: gl.getUniformLocation(shaderProgram, "uModelViewMatrix"),
205     },
206   };
207   ```
208
2097. Create a buffer object.
210
211   - Before drawing the square, create a buffer to store its vertices.
212
213   - Call the **createBuffer()** function of **gl** to obtain a buffer object and store it in the vertex buffer. Then call the **bindBuffer()** function to bind the context.
214
215   - Create a JavaScript array to record each vertex of the square, convert the JavaScript array into an array of the WebGL floating-point type, and pass the latter to the **bufferData()** function of **gl** to establish the vertices of the object.
216
217   ```js
218   function initBuffers(gl) {
219     const positionBuffer = initPositionBuffer(gl);
220     return {
221       position: positionBuffer,
222     };
223   }
224   function initPositionBuffer(gl) {
225     // Create a position buffer for the square.
226     const positionBuffer = gl.createBuffer();
227     // Bind the position buffer to the application buffer.
228     gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
229     // Create an array to hold the vertices of the square.
230     const positions = [1.0, 1.0, -1.0, 1.0, 1.0, -1.0, -1.0, -1.0];
231     // Pass the position array to WebGL.
232     gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);
233     return positionBuffer;
234   }
235   export { initBuffers };
236   ```
237
2388. Start render.
239
240   - Erase the canvas with the background color, and then build a camera perspective projection matrix. Set the view angle to 45 degrees and set an aspect ratio suitable for the actual image. Specify that objects within the range of 0.1 to 100 units from the camera are visible.
241
242   - Load a specific position and place the square in a position six units away from the camera. Then, bind the square's vertex buffer to the context, configure the buffer, and call the **drawArrays()** method to draw the square.
243
244   ```js
245   function drawScene(gl, programInfo, buffers) {
246     gl.clearColor(0.0, 0.0, 0.0, 1.0);
247     gl.clearDepth(1.0); // Clear all content.
248     gl.depthFunc(gl.LEQUAL);
249     // Clear the canvas.
250      gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
251     // Create a perspective projection matrix to simulate perspective deformation in the camera.
252     const fieldOfView = (45 * Math.PI) / 180;
253     const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
254     const zNear = 0.1;
255     const zFar = 100.0;
256     const projectionMatrix = mat4.create();
257     mat4.perspective(projectionMatrix, fieldOfView, aspect, zNear, zFar);
258     // Set the drawing position to the center of the scene.
259     const modelViewMatrix = mat4.create();
260     // Start to draw the square.
261     mat4.translate(
262       modelViewMatrix, // Target matrix.
263       modelViewMatrix, // Matrix to be converted.
264       [-0.0, 0.0, -6.0],
265     );
266     {
267       const numComponents = 2;
268       const type = gl.FLOAT;
269       const normalize = false;
270       const stride = 0; // Number of bytes required from a group of values to the next group of values.
271       const offset = 0;
272       gl.bindBuffer(gl.ARRAY_BUFFER, buffers.position);
273       gl.vertexAttribPointer(
274         programInfo.attribLocations.vertexPosition,
275         numComponents,
276         type,
277         normalize,
278         stride,
279         offset,
280       );
281       gl.enableVertexAttribArray(programInfo.attribLocations.vertexPosition);
282     }
283     gl.useProgram(programInfo.program);
284     gl.uniformMatrix4fv(
285       programInfo.uniformLocations.projectionMatrix,
286       false,
287       projectionMatrix,
288     );
289     gl.uniformMatrix4fv(
290       programInfo.uniformLocations.modelViewMatrix,
291       false,
292       modelViewMatrix,
293     );
294     {
295       const offset = 0;
296       const vertexCount = 4;
297       gl.drawArrays(gl.TRIANGLE_STRIP, offset, vertexCount);
298     }
299   }
300   // Tell WebGL how to pull the position buffer to the vertexPosition attribute.
301   function setPositionAttribute(gl, buffers, programInfo) {
302     const numComponents = 2;
303     const type = gl.FLOAT;
304     const normalize = false;
305     const stride = 0; // Number of bytes required from a group of values to the next group of values.
306     const offset = 0;
307     gl.bindBuffer(gl.ARRAY_BUFFER, buffers.position);
308     gl.vertexAttribPointer(
309       programInfo.attribLocations.vertexPosition,
310       numComponents,
311       type,
312       normalize,
313       stride,
314       offset,
315     );
316     gl.enableVertexAttribArray(programInfo.attribLocations.vertexPosition);
317   }
318   export { drawScene };
319   ```
320
321The following figure shows the implementation effect.
322
323![Implementation effect](figures/square.png)
324