1e41f4b71Sopenharmony_ci# Using WebGL to Draw Graphics 2e41f4b71Sopenharmony_ci 3e41f4b71Sopenharmony_ci## When to Use 4e41f4b71Sopenharmony_ci 5e41f4b71Sopenharmony_ciWeb 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/). 6e41f4b71Sopenharmony_ci 7e41f4b71Sopenharmony_ci> **NOTE** 8e41f4b71Sopenharmony_ci> 9e41f4b71Sopenharmony_ci> WebGL can be used only in the JavaScript-compatible web-like development paradigm. 10e41f4b71Sopenharmony_ci 11e41f4b71Sopenharmony_ci## Basic Concepts 12e41f4b71Sopenharmony_ci 13e41f4b71Sopenharmony_ci### Shader program 14e41f4b71Sopenharmony_ci 15e41f4b71Sopenharmony_ciThe 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. 16e41f4b71Sopenharmony_ci 17e41f4b71Sopenharmony_ci### Shader 18e41f4b71Sopenharmony_ci 19e41f4b71Sopenharmony_ciShaders are instructions and data that run in a graphics card. In WebGL, shaders are written in the OpenGL Shading Language (GLSL). 20e41f4b71Sopenharmony_ci 21e41f4b71Sopenharmony_ciThere are vertex shaders and fragment shaders. The interaction between vertex shaders and fragment shaders involves rasterization. 22e41f4b71Sopenharmony_ci 23e41f4b71Sopenharmony_ci- 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. 24e41f4b71Sopenharmony_ci 25e41f4b71Sopenharmony_ci- The fragment shader is mainly used to output a color value for each pixel being processed. 26e41f4b71Sopenharmony_ci 27e41f4b71Sopenharmony_ci### Rasterization 28e41f4b71Sopenharmony_ci 29e41f4b71Sopenharmony_ciRasterization 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. 30e41f4b71Sopenharmony_ci 31e41f4b71Sopenharmony_ci### Frame buffer 32e41f4b71Sopenharmony_ci 33e41f4b71Sopenharmony_ciThe 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. 34e41f4b71Sopenharmony_ci 35e41f4b71Sopenharmony_ci### Texture 36e41f4b71Sopenharmony_ci 37e41f4b71Sopenharmony_ciA 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. 38e41f4b71Sopenharmony_ci 39e41f4b71Sopenharmony_ci 40e41f4b71Sopenharmony_ci## Variables and APIs 41e41f4b71Sopenharmony_ci 42e41f4b71Sopenharmony_ci### Variables 43e41f4b71Sopenharmony_ci 44e41f4b71Sopenharmony_ci| Type | Web IDL Type | Description | 45e41f4b71Sopenharmony_ci| ------------ | -------------------- | ------------------------------------------------------------ | 46e41f4b71Sopenharmony_ci| GLenum | unsigned long | Enum. | 47e41f4b71Sopenharmony_ci| GLboolean | boolean | Boolean, either **true** or **false**.| 48e41f4b71Sopenharmony_ci| GLbitfield | unsigned long | Unsigned integer. Multiple bit flags can be contained, and each bit flag represents a specific option.| 49e41f4b71Sopenharmony_ci| GLbyte | byte | Signed integer represented by 2's complement of 8 bits (one byte). | 50e41f4b71Sopenharmony_ci| GLshort | short | Signed integer represented by 2's complement of 16 bits. | 51e41f4b71Sopenharmony_ci| GLint | long | Signed integer represented by 2's complement of 32 bits. | 52e41f4b71Sopenharmony_ci| GLsizei | long | Size, for example, the width and height of the drawing buffer. | 53e41f4b71Sopenharmony_ci| GLintptr | long long | A special type used to represent a pointer. It is usually used to specify the offset of a buffer object. | 54e41f4b71Sopenharmony_ci| GLsizeiptr | long long | A special type used to represent a pointer. It is usually used to specify the size of a buffer object. | 55e41f4b71Sopenharmony_ci| GLubyte | octet | Unsigned integer represented by 2's complement of 8 bits (one byte). | 56e41f4b71Sopenharmony_ci| GLushort | unsigned short | Unsigned integer represented by 2's complement of 16 bits. | 57e41f4b71Sopenharmony_ci| GLuint | unsigned short | Signed integer represented by 2's complement of 32 bits. | 58e41f4b71Sopenharmony_ci| GLfloat | unrestricted float | 32-bit IEEE floating point number. | 59e41f4b71Sopenharmony_ci| GLclampf | unrestricted float | 32-bit IEEE floating point number. | 60e41f4b71Sopenharmony_ci 61e41f4b71Sopenharmony_ci### Available APIs 62e41f4b71Sopenharmony_ci 63e41f4b71Sopenharmony_ci| API | Description | 64e41f4b71Sopenharmony_ci| ------------------------------------------------------------ | ------------------------------------------------------ | 65e41f4b71Sopenharmony_ci| canvas.getContext | Obtains the canvas context. | 66e41f4b71Sopenharmony_ci| webgl.createBuffer(): WebGLBuffer \| null | Creates and initializes a WebGL buffer. | 67e41f4b71Sopenharmony_ci| webgl.bindBuffer(target: GLenum, buffer: WebGLBuffer \| null): void | Binds a WebGL buffer to the target. | 68e41f4b71Sopenharmony_ci| webgl.bufferData(target: GLenum, srcData: ArrayBufferView, usage: GLenum, srcOffset: GLuint, length?: GLuint): void | Creates and initializes the WebGL buffer's data store. | 69e41f4b71Sopenharmony_ci| webgl.getAttribLocation(program: WebGLProgram, name: string): GLint | Obtains the address of the **attribute** variable in the shader from the given WebGL program.| 70e41f4b71Sopenharmony_ci| webgl.vertexAttribPointer(index GLuint, size: GLint, type: GLenum, normalized: GLboolean, stride: GLsizei, offset: GLintptr): void | Assigns a **Buffer** object to a variable. | 71e41f4b71Sopenharmony_ci| webgl.enableVertexAttribArray(index: GLuint): void | Connects a variable to the **Buffer** object allocated to it. | 72e41f4b71Sopenharmony_ci| webgl.clearColor(red: GLclampf, green:GLclampf, blue: GLclampf, alpha: GLclampf): void | Clears the specified color on the canvas. | 73e41f4b71Sopenharmony_ci| webgl.clear(mask: GLbitfield): void | Clears the canvas. | 74e41f4b71Sopenharmony_ci| webgl.drawArrays(mode: GLenum, first:;GLint, count: GLsizei): void | Draws data. | 75e41f4b71Sopenharmony_ci| webgl.flush(): void | Flushes data to the GPU and clears the buffer. | 76e41f4b71Sopenharmony_ci| webgl.createProgram(): WebGLProgram \| null | Creates a **WebGLProgram** object. | 77e41f4b71Sopenharmony_ci 78e41f4b71Sopenharmony_ci## How to Develop 79e41f4b71Sopenharmony_ci 80e41f4b71Sopenharmony_ci The following uses a color square as an example to describe how to draw a 2D graphic using WebGL. 81e41f4b71Sopenharmony_ci 82e41f4b71Sopenharmony_ci1. 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. 83e41f4b71Sopenharmony_ci 84e41f4b71Sopenharmony_ci ```hml 85e41f4b71Sopenharmony_ci <div class="container"> 86e41f4b71Sopenharmony_ci <canvas ref="canvas1" style="width : 400px; height : 400px; background-color : lightyellow;"></canvas> 87e41f4b71Sopenharmony_ci <button class="btn-button" onclick="BtnColorTriangle">BtnColorTriangle</button> 88e41f4b71Sopenharmony_ci </div> 89e41f4b71Sopenharmony_ci ``` 90e41f4b71Sopenharmony_ci 91e41f4b71Sopenharmony_ci2. Set the WebGL context. 92e41f4b71Sopenharmony_ci 93e41f4b71Sopenharmony_ci - Call the **main()** function in the JavaScript code after loading to set the WebGL context and start rendering. 94e41f4b71Sopenharmony_ci 95e41f4b71Sopenharmony_ci - 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. 96e41f4b71Sopenharmony_ci 97e41f4b71Sopenharmony_ci ```js 98e41f4b71Sopenharmony_ci function main() { 99e41f4b71Sopenharmony_ci const canvas = document.querySelector("#glcanvas"); 100e41f4b71Sopenharmony_ci // Initialize the WebGL context. 101e41f4b71Sopenharmony_ci const gl = canvas.getContext("webgl"); 102e41f4b71Sopenharmony_ci 103e41f4b71Sopenharmony_ci // Check the support for WebGL. 104e41f4b71Sopenharmony_ci if (!gl) { 105e41f4b71Sopenharmony_ci alert("Your browser, operating system, or hardware may not support WebGL."); 106e41f4b71Sopenharmony_ci return; 107e41f4b71Sopenharmony_ci } 108e41f4b71Sopenharmony_ci // Use completely opaque black to clear all images. 109e41f4b71Sopenharmony_ci gl.clearColor(0.0, 0.0, 0.0, 1.0); 110e41f4b71Sopenharmony_ci // Clear the buffer with the color specified above. 111e41f4b71Sopenharmony_ci gl.clear(gl.COLOR_BUFFER_BIT); 112e41f4b71Sopenharmony_ci } 113e41f4b71Sopenharmony_ci ``` 114e41f4b71Sopenharmony_ci3. Define the vertex shader. 115e41f4b71Sopenharmony_ci 116e41f4b71Sopenharmony_ci 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. 117e41f4b71Sopenharmony_ci 118e41f4b71Sopenharmony_ci ```js 119e41f4b71Sopenharmony_ci const vsSource = ` 120e41f4b71Sopenharmony_ci attribute vec4 aVertexPosition; 121e41f4b71Sopenharmony_ci uniform mat4 uModelViewMatrix; 122e41f4b71Sopenharmony_ci uniform mat4 uProjectionMatrix; 123e41f4b71Sopenharmony_ci void main() { 124e41f4b71Sopenharmony_ci gl_Position = uProjectionMatrix * uModelViewMatrix * aVertexPosition; 125e41f4b71Sopenharmony_ci } 126e41f4b71Sopenharmony_ci `; 127e41f4b71Sopenharmony_ci ``` 128e41f4b71Sopenharmony_ci 129e41f4b71Sopenharmony_ci4. Define the fragment shader. 130e41f4b71Sopenharmony_ci 131e41f4b71Sopenharmony_ci After the vertex shader processes the vertices, the fragment shader is called once by each pixel to be drawn. 132e41f4b71Sopenharmony_ci 133e41f4b71Sopenharmony_ci ```js 134e41f4b71Sopenharmony_ci const fsSource = ` 135e41f4b71Sopenharmony_ci void main() { 136e41f4b71Sopenharmony_ci gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0); 137e41f4b71Sopenharmony_ci } 138e41f4b71Sopenharmony_ci `; 139e41f4b71Sopenharmony_ci ``` 140e41f4b71Sopenharmony_ci5. Pass the shader to WebGL. 141e41f4b71Sopenharmony_ci 142e41f4b71Sopenharmony_ci Pass the vertex shader and fragment shader defined to WebGL and compile them together. 143e41f4b71Sopenharmony_ci 144e41f4b71Sopenharmony_ci 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. 145e41f4b71Sopenharmony_ci 146e41f4b71Sopenharmony_ci ```js 147e41f4b71Sopenharmony_ci // Initialize the shader program so that WebGL knows how to draw data. 148e41f4b71Sopenharmony_ci function initShaderProgram(gl, vsSource, fsSource) { 149e41f4b71Sopenharmony_ci const vertexShader = loadShader(gl, gl.VERTEX_SHADER, vsSource); 150e41f4b71Sopenharmony_ci const fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, fsSource); 151e41f4b71Sopenharmony_ci // Create a shader program. 152e41f4b71Sopenharmony_ci const shaderProgram = gl.createProgram(); 153e41f4b71Sopenharmony_ci gl.attachShader(shaderProgram, vertexShader); 154e41f4b71Sopenharmony_ci gl.attachShader(shaderProgram, fragmentShader); 155e41f4b71Sopenharmony_ci gl.linkProgram(shaderProgram); 156e41f4b71Sopenharmony_ci // An alert is displayed if the creation fails. 157e41f4b71Sopenharmony_ci if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { 158e41f4b71Sopenharmony_ci alert( 159e41f4b71Sopenharmony_ci "Unable to initialize the shader program: "+ 160e41f4b71Sopenharmony_ci gl.getProgramInfoLog(shaderProgram), 161e41f4b71Sopenharmony_ci ); 162e41f4b71Sopenharmony_ci return null; 163e41f4b71Sopenharmony_ci } 164e41f4b71Sopenharmony_ci return shaderProgram; 165e41f4b71Sopenharmony_ci } 166e41f4b71Sopenharmony_ci // Create a shader of the specified type, upload the source code, and compile the source code. 167e41f4b71Sopenharmony_ci function loadShader(gl, type, source) { 168e41f4b71Sopenharmony_ci const shader = gl.createShader(type); 169e41f4b71Sopenharmony_ci // Send the resource to the shader object. 170e41f4b71Sopenharmony_ci gl.shaderSource(shader, source); 171e41f4b71Sopenharmony_ci // Compile the shader program. 172e41f4b71Sopenharmony_ci gl.compileShader(shader); 173e41f4b71Sopenharmony_ci // Check whether the compilation is successful. 174e41f4b71Sopenharmony_ci if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { 175e41f4b71Sopenharmony_ci alert( 176e41f4b71Sopenharmony_ci "Error occurred when compiling the shader: "+ gl.getShaderInfoLog (shader), 177e41f4b71Sopenharmony_ci ); 178e41f4b71Sopenharmony_ci gl.deleteShader(shader); 179e41f4b71Sopenharmony_ci return null; 180e41f4b71Sopenharmony_ci } 181e41f4b71Sopenharmony_ci return shader; 182e41f4b71Sopenharmony_ci } 183e41f4b71Sopenharmony_ci ``` 184e41f4b71Sopenharmony_ci6. Find the input location assigned by WebGL. 185e41f4b71Sopenharmony_ci 186e41f4b71Sopenharmony_ci - After creating the shader program, find the input location allocated by WebGL. There is one property and two Uniforms. 187e41f4b71Sopenharmony_ci 188e41f4b71Sopenharmony_ci - The property value is assigned by the buffer. For each iteration of the vertex shader, a new value is assigned. 189e41f4b71Sopenharmony_ci 190e41f4b71Sopenharmony_ci - 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. 191e41f4b71Sopenharmony_ci 192e41f4b71Sopenharmony_ci ```js 193e41f4b71Sopenharmony_ci const programInfo = { 194e41f4b71Sopenharmony_ci program: shaderProgram, 195e41f4b71Sopenharmony_ci attribLocations: { 196e41f4b71Sopenharmony_ci vertexPosition: gl.getAttribLocation(shaderProgram, "aVertexPosition"), 197e41f4b71Sopenharmony_ci }, 198e41f4b71Sopenharmony_ci uniformLocations: { 199e41f4b71Sopenharmony_ci projectionMatrix: gl.getUniformLocation(shaderProgram, "uProjectionMatrix"), 200e41f4b71Sopenharmony_ci modelViewMatrix: gl.getUniformLocation(shaderProgram, "uModelViewMatrix"), 201e41f4b71Sopenharmony_ci }, 202e41f4b71Sopenharmony_ci }; 203e41f4b71Sopenharmony_ci ``` 204e41f4b71Sopenharmony_ci 205e41f4b71Sopenharmony_ci7. Create a buffer object. 206e41f4b71Sopenharmony_ci 207e41f4b71Sopenharmony_ci - Before drawing the square, create a buffer to store its vertices. 208e41f4b71Sopenharmony_ci 209e41f4b71Sopenharmony_ci - 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. 210e41f4b71Sopenharmony_ci 211e41f4b71Sopenharmony_ci - 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. 212e41f4b71Sopenharmony_ci 213e41f4b71Sopenharmony_ci ```js 214e41f4b71Sopenharmony_ci function initBuffers(gl) { 215e41f4b71Sopenharmony_ci const positionBuffer = initPositionBuffer(gl); 216e41f4b71Sopenharmony_ci return { 217e41f4b71Sopenharmony_ci position: positionBuffer, 218e41f4b71Sopenharmony_ci }; 219e41f4b71Sopenharmony_ci } 220e41f4b71Sopenharmony_ci function initPositionBuffer(gl) { 221e41f4b71Sopenharmony_ci // Create a position buffer for the square. 222e41f4b71Sopenharmony_ci const positionBuffer = gl.createBuffer(); 223e41f4b71Sopenharmony_ci // Bind the position buffer to the application buffer. 224e41f4b71Sopenharmony_ci gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer); 225e41f4b71Sopenharmony_ci // Create an array to hold the vertices of the square. 226e41f4b71Sopenharmony_ci const positions = [1.0, 1.0, -1.0, 1.0, 1.0, -1.0, -1.0, -1.0]; 227e41f4b71Sopenharmony_ci // Pass the position array to WebGL. 228e41f4b71Sopenharmony_ci gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW); 229e41f4b71Sopenharmony_ci return positionBuffer; 230e41f4b71Sopenharmony_ci } 231e41f4b71Sopenharmony_ci export { initBuffers }; 232e41f4b71Sopenharmony_ci ``` 233e41f4b71Sopenharmony_ci 234e41f4b71Sopenharmony_ci8. Start render. 235e41f4b71Sopenharmony_ci 236e41f4b71Sopenharmony_ci - 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. 237e41f4b71Sopenharmony_ci 238e41f4b71Sopenharmony_ci - 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. 239e41f4b71Sopenharmony_ci 240e41f4b71Sopenharmony_ci ```js 241e41f4b71Sopenharmony_ci function drawScene(gl, programInfo, buffers) { 242e41f4b71Sopenharmony_ci gl.clearColor(0.0, 0.0, 0.0, 1.0); 243e41f4b71Sopenharmony_ci gl.clearDepth(1.0); // Clear all content. 244e41f4b71Sopenharmony_ci gl.depthFunc(gl.LEQUAL); 245e41f4b71Sopenharmony_ci // Clear the canvas. 246e41f4b71Sopenharmony_ci gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); 247e41f4b71Sopenharmony_ci // Create a perspective projection matrix to simulate perspective deformation in the camera. 248e41f4b71Sopenharmony_ci const fieldOfView = (45 * Math.PI) / 180; 249e41f4b71Sopenharmony_ci const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight; 250e41f4b71Sopenharmony_ci const zNear = 0.1; 251e41f4b71Sopenharmony_ci const zFar = 100.0; 252e41f4b71Sopenharmony_ci const projectionMatrix = mat4.create(); 253e41f4b71Sopenharmony_ci mat4.perspective(projectionMatrix, fieldOfView, aspect, zNear, zFar); 254e41f4b71Sopenharmony_ci // Set the drawing position to the center of the scene. 255e41f4b71Sopenharmony_ci const modelViewMatrix = mat4.create(); 256e41f4b71Sopenharmony_ci // Start to draw the square. 257e41f4b71Sopenharmony_ci mat4.translate( 258e41f4b71Sopenharmony_ci modelViewMatrix, // Target matrix. 259e41f4b71Sopenharmony_ci modelViewMatrix, // Matrix to be converted. 260e41f4b71Sopenharmony_ci [-0.0, 0.0, -6.0], 261e41f4b71Sopenharmony_ci ); 262e41f4b71Sopenharmony_ci { 263e41f4b71Sopenharmony_ci const numComponents = 2; 264e41f4b71Sopenharmony_ci const type = gl.FLOAT; 265e41f4b71Sopenharmony_ci const normalize = false; 266e41f4b71Sopenharmony_ci const stride = 0; // Number of bytes required from a group of values to the next group of values. 267e41f4b71Sopenharmony_ci const offset = 0; 268e41f4b71Sopenharmony_ci gl.bindBuffer(gl.ARRAY_BUFFER, buffers.position); 269e41f4b71Sopenharmony_ci gl.vertexAttribPointer( 270e41f4b71Sopenharmony_ci programInfo.attribLocations.vertexPosition, 271e41f4b71Sopenharmony_ci numComponents, 272e41f4b71Sopenharmony_ci type, 273e41f4b71Sopenharmony_ci normalize, 274e41f4b71Sopenharmony_ci stride, 275e41f4b71Sopenharmony_ci offset, 276e41f4b71Sopenharmony_ci ); 277e41f4b71Sopenharmony_ci gl.enableVertexAttribArray(programInfo.attribLocations.vertexPosition); 278e41f4b71Sopenharmony_ci } 279e41f4b71Sopenharmony_ci gl.useProgram(programInfo.program); 280e41f4b71Sopenharmony_ci gl.uniformMatrix4fv( 281e41f4b71Sopenharmony_ci programInfo.uniformLocations.projectionMatrix, 282e41f4b71Sopenharmony_ci false, 283e41f4b71Sopenharmony_ci projectionMatrix, 284e41f4b71Sopenharmony_ci ); 285e41f4b71Sopenharmony_ci gl.uniformMatrix4fv( 286e41f4b71Sopenharmony_ci programInfo.uniformLocations.modelViewMatrix, 287e41f4b71Sopenharmony_ci false, 288e41f4b71Sopenharmony_ci modelViewMatrix, 289e41f4b71Sopenharmony_ci ); 290e41f4b71Sopenharmony_ci { 291e41f4b71Sopenharmony_ci const offset = 0; 292e41f4b71Sopenharmony_ci const vertexCount = 4; 293e41f4b71Sopenharmony_ci gl.drawArrays(gl.TRIANGLE_STRIP, offset, vertexCount); 294e41f4b71Sopenharmony_ci } 295e41f4b71Sopenharmony_ci } 296e41f4b71Sopenharmony_ci // Tell WebGL how to pull the position buffer to the vertexPosition attribute. 297e41f4b71Sopenharmony_ci function setPositionAttribute(gl, buffers, programInfo) { 298e41f4b71Sopenharmony_ci const numComponents = 2; 299e41f4b71Sopenharmony_ci const type = gl.FLOAT; 300e41f4b71Sopenharmony_ci const normalize = false; 301e41f4b71Sopenharmony_ci const stride = 0; // Number of bytes required from a group of values to the next group of values. 302e41f4b71Sopenharmony_ci const offset = 0; 303e41f4b71Sopenharmony_ci gl.bindBuffer(gl.ARRAY_BUFFER, buffers.position); 304e41f4b71Sopenharmony_ci gl.vertexAttribPointer( 305e41f4b71Sopenharmony_ci programInfo.attribLocations.vertexPosition, 306e41f4b71Sopenharmony_ci numComponents, 307e41f4b71Sopenharmony_ci type, 308e41f4b71Sopenharmony_ci normalize, 309e41f4b71Sopenharmony_ci stride, 310e41f4b71Sopenharmony_ci offset, 311e41f4b71Sopenharmony_ci ); 312e41f4b71Sopenharmony_ci gl.enableVertexAttribArray(programInfo.attribLocations.vertexPosition); 313e41f4b71Sopenharmony_ci } 314e41f4b71Sopenharmony_ci export { drawScene }; 315e41f4b71Sopenharmony_ci ``` 316e41f4b71Sopenharmony_ci 317e41f4b71Sopenharmony_ciThe following figure shows the implementation effect. 318e41f4b71Sopenharmony_ci 319e41f4b71Sopenharmony_ci 320