1# Using WebGL to Draw Graphics 2 3## When to Use 4 5Web Graphic 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 ```js 119 const vsSource = ` 120 attribute vec4 aVertexPosition; 121 uniform mat4 uModelViewMatrix; 122 uniform mat4 uProjectionMatrix; 123 void main() { 124 gl_Position = uProjectionMatrix * uModelViewMatrix * aVertexPosition; 125 } 126 `; 127 ``` 128 1294. Define the fragment shader. 130 131 After the vertex shader processes the vertices, the fragment shader is called once by each pixel to be drawn. 132 133 ```js 134 const fsSource = ` 135 void main() { 136 gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0); 137 } 138 `; 139 ``` 1405. Pass the shader to WebGL. 141 142 Pass the vertex shader and fragment shader defined to WebGL and compile them together. 143 144 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. 145 146 ```js 147 // Initialize the shader program so that WebGL knows how to draw data. 148 function initShaderProgram(gl, vsSource, fsSource) { 149 const vertexShader = loadShader(gl, gl.VERTEX_SHADER, vsSource); 150 const fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, fsSource); 151 // Create a shader program. 152 const shaderProgram = gl.createProgram(); 153 gl.attachShader(shaderProgram, vertexShader); 154 gl.attachShader(shaderProgram, fragmentShader); 155 gl.linkProgram(shaderProgram); 156 // An alert is displayed if the creation fails. 157 if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { 158 alert( 159 "Unable to initialize the shader program: "+ 160 gl.getProgramInfoLog(shaderProgram), 161 ); 162 return null; 163 } 164 return shaderProgram; 165 } 166 // Create a shader of the specified type, upload the source code, and compile the source code. 167 function loadShader(gl, type, source) { 168 const shader = gl.createShader(type); 169 // Send the resource to the shader object. 170 gl.shaderSource(shader, source); 171 // Compile the shader program. 172 gl.compileShader(shader); 173 // Check whether the compilation is successful. 174 if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { 175 alert( 176 "Error occurred when compiling the shader: "+ gl.getShaderInfoLog (shader), 177 ); 178 gl.deleteShader(shader); 179 return null; 180 } 181 return shader; 182 } 183 ``` 1846. Find the input location assigned by WebGL. 185 186 - After creating the shader program, find the input location allocated by WebGL. There is one property and two Uniforms. 187 188 - The property value is assigned by the buffer. For each iteration of the vertex shader, a new value is assigned. 189 190 - 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. 191 192 ```js 193 const programInfo = { 194 program: shaderProgram, 195 attribLocations: { 196 vertexPosition: gl.getAttribLocation(shaderProgram, "aVertexPosition"), 197 }, 198 uniformLocations: { 199 projectionMatrix: gl.getUniformLocation(shaderProgram, "uProjectionMatrix"), 200 modelViewMatrix: gl.getUniformLocation(shaderProgram, "uModelViewMatrix"), 201 }, 202 }; 203 ``` 204 2057. Create a buffer object. 206 207 - Before drawing the square, create a buffer to store its vertices. 208 209 - 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. 210 211 - 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. 212 213 ```js 214 function initBuffers(gl) { 215 const positionBuffer = initPositionBuffer(gl); 216 return { 217 position: positionBuffer, 218 }; 219 } 220 function initPositionBuffer(gl) { 221 // Create a position buffer for the square. 222 const positionBuffer = gl.createBuffer(); 223 // Bind the position buffer to the application buffer. 224 gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer); 225 // Create an array to hold the vertices of the square. 226 const positions = [1.0, 1.0, -1.0, 1.0, 1.0, -1.0, -1.0, -1.0]; 227 // Pass the position array to WebGL. 228 gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW); 229 return positionBuffer; 230 } 231 export { initBuffers }; 232 ``` 233 2348. Start render. 235 236 - 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. 237 238 - 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. 239 240 ```js 241 function drawScene(gl, programInfo, buffers) { 242 gl.clearColor(0.0, 0.0, 0.0, 1.0); 243 gl.clearDepth(1.0); // Clear all content. 244 gl.depthFunc(gl.LEQUAL); 245 // Clear the canvas. 246 gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); 247 // Create a perspective projection matrix to simulate perspective deformation in the camera. 248 const fieldOfView = (45 * Math.PI) / 180; 249 const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight; 250 const zNear = 0.1; 251 const zFar = 100.0; 252 const projectionMatrix = mat4.create(); 253 mat4.perspective(projectionMatrix, fieldOfView, aspect, zNear, zFar); 254 // Set the drawing position to the center of the scene. 255 const modelViewMatrix = mat4.create(); 256 // Start to draw the square. 257 mat4.translate( 258 modelViewMatrix, // Target matrix. 259 modelViewMatrix, // Matrix to be converted. 260 [-0.0, 0.0, -6.0], 261 ); 262 { 263 const numComponents = 2; 264 const type = gl.FLOAT; 265 const normalize = false; 266 const stride = 0; // Number of bytes required from a group of values to the next group of values. 267 const offset = 0; 268 gl.bindBuffer(gl.ARRAY_BUFFER, buffers.position); 269 gl.vertexAttribPointer( 270 programInfo.attribLocations.vertexPosition, 271 numComponents, 272 type, 273 normalize, 274 stride, 275 offset, 276 ); 277 gl.enableVertexAttribArray(programInfo.attribLocations.vertexPosition); 278 } 279 gl.useProgram(programInfo.program); 280 gl.uniformMatrix4fv( 281 programInfo.uniformLocations.projectionMatrix, 282 false, 283 projectionMatrix, 284 ); 285 gl.uniformMatrix4fv( 286 programInfo.uniformLocations.modelViewMatrix, 287 false, 288 modelViewMatrix, 289 ); 290 { 291 const offset = 0; 292 const vertexCount = 4; 293 gl.drawArrays(gl.TRIANGLE_STRIP, offset, vertexCount); 294 } 295 } 296 // Tell WebGL how to pull the position buffer to the vertexPosition attribute. 297 function setPositionAttribute(gl, buffers, programInfo) { 298 const numComponents = 2; 299 const type = gl.FLOAT; 300 const normalize = false; 301 const stride = 0; // Number of bytes required from a group of values to the next group of values. 302 const offset = 0; 303 gl.bindBuffer(gl.ARRAY_BUFFER, buffers.position); 304 gl.vertexAttribPointer( 305 programInfo.attribLocations.vertexPosition, 306 numComponents, 307 type, 308 normalize, 309 stride, 310 offset, 311 ); 312 gl.enableVertexAttribArray(programInfo.attribLocations.vertexPosition); 313 } 314 export { drawScene }; 315 ``` 316 317The following figure shows the implementation effect. 318 319 320