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 324