1# OpenGL ES
2
3OpenGL is a cross-platform graphics API that specifies a standard software interface for 3D graphics processing hardware. [OpenGL ES](https://www.khronos.org/opengles/) is a flavor of the OpenGL specification intended for embedded devices. OpenHarmony now supports OpenGL ES 3.2.
4
5## Supported Capabilities
6
7OpenGL ES 3.2
8
9## Symbols Exported from the Standard Library
10
11[OpenGL ES 3.2 Symbols Exported](openglesv3-symbol.md)
12
13## Introducing OpenGL
14
15To use OpenGL capabilities, you must add related dynamic link libraries (DLLs) and header files.
16
17**Adding Dynamic Link Libraries**
18
19Add the following libraries to **CMakeLists.txt**.
20
21```txt
22libace_ndk.z.so
23libace_napi.z.so
24libGLESv3.so
25libEGL.so
26```
27
28**Including Header Files**
29
30```c++
31#include <ace/xcomponent/native_interface_xcomponent.h>
32#include <EGL/egl.h>
33#include <EGL/eglext.h>
34#include <EGL/eglplatform.h>
35#include <GLES3/gl3.h>
36```
37
38## References
39
40To use the OpenGL ES API in your application development, familiarize yourself with the NDK development process and the **XComponent** usage, which are described in the following topics:
41
42- [Getting Started with the NDK](../../napi/ndk-development-overview.md)
43
44- [Node-API](./napi.md)
45
46- [XComponentNode](../apis-arkui/js-apis-arkui-xcomponentNode.md)
47
48- [XComponent](../apis-arkui/arkui-ts/ts-basic-components-xcomponent.md)
49
50## OpenGL ES Extensions
51
52- To obtain the official reference document for OpenGL ES extensions, visit [Khronos OpenGL ES Registry](https://registry.khronos.org/OpenGL/index_es.php).
53- You can call **glGetString** to query the extensions supported by the chip. Before calling **glGetString**, you must initialize the context. The following is an example:
54
55```c++
56EGLDisplay display;
57EGLConfig config;
58EGLContext context;
59EGLSurface surface;
60EGLint majorVersion;
61EGLint minorVersion;
62EGLNativeWindowType win;
63display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
64eglInitialize(display, &majorVersion, &minorVersion);
65display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
66eglInitialize(display, &majorVersion, &minorVersion);
67EGLint attribs[] = {
68    EGL_RENDERABLE_TYPE,
69    EGL_OPENGL_ES2_BIT,
70    EGL_BLUE_SIZE, 8,
71    EGL_GREEN_SIZE, 8,
72    EGL_RED_SIZE, 8,
73    EGL_NONE
74};
75eglChooseConfig(display, attribs, &config, 1, &numConfigs);
76context = eglCreateContext(display, config, EGL_NO_CONTEXT, NULL);
77surface = eglCreatePbufferSurface(display, config, NULL);
78eglMakeCurrent(display, surface, surface, context);
79
80char *strTest = new char[1024];
81strTest = (char *)glGetString(GL_EXTENSIONS); // The return value of strTest lists all extensions supported, separated by spaces.
82bool isHave = strTest.find("GL_OES_matrix_palette") != -1 ?
83    true :
84    false; // Check whether an extension exists. If yes, the value of isHave is true. If no, the value of isHave is false.
85```
86
87## Example
88
89```cpp
90#include <EGL/egl.h>
91#include <GLES3/gl3.h>
92#include <iostream>
93
94#define WINDOW_WIDTH 800
95#define WINDOW_HEIGHT 600
96
97int main() {
98    // Initialize EGL.
99    EGLDisplay display;
100    EGLConfig config;
101    EGLContext context;
102    EGLSurface surface;
103    EGLint numConfigs;
104    EGLint majorVersion;
105    EGLint minorVersion;
106
107    // Initialize the EGL display.
108    display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
109    eglInitialize(display, &majorVersion, &minorVersion);
110
111    // Configure EGL.
112    EGLint attribs[] = {
113        EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT,
114        EGL_BLUE_SIZE, 6,
115        EGL_GREEN_SIZE, 8,
116        EGL_RED_SIZE, 8,
117        EGL_NONE
118    };
119    eglChooseConfig(display, attribs, &config, 1, &numConfigs);
120
121    // Create an EGL context.
122    EGLint contextAttribs[] = {
123        EGL_CONTEXT_CLIENT_VERSION, 3,
124        EGL_NONE
125    };
126
127    // Create an EGL surface.
128    surface = eglCreateWindowSurface(display, config, nativeWindow, NULL);
129
130    context = eglCreateContext(display, config, EGL_NO_CONTEXT, contextAttribs);
131
132    // Bind the EGL context to the surface.
133    eglMakeCurrent(display, surface, surface, context);
134
135    // Set the viewport.
136    glViewport(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT);
137
138    // Clear the color buffer.
139    glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
140    glClear(GL_COLOR_BUFFER_BIT);
141
142    // Define vertex data.
143    GLfloat vertices[] = {
144        -0.5f, -0.5f, 0.0f, // Lower left corner.
145         0.5f, -0.5f, 0.0f, // Lower right corner.
146         0.0f, 0.5f, 0.0f // Top.
147    };
148
149    // Create and bind a Vertex Buffer Object (VBO).
150    GLuint VAO[0];
151    GLuint VBO;
152    glGenVertexArrays(1, VAO);
153	glBindVertexArray(VAO[0]);
154    glGenBuffers(1, &VBO);
155    glBindBuffer(GL_ARRAY_BUFFER, VBO);
156    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
157
158    // Create a shader program.
159    const char* vertexShaderSource = R"(
160        #version 300 es
161        precision mediump float;
162        layout (location = 0) in vec3 aPos;
163        void main() {
164            gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);
165        }
166    )";
167
168    const char* fragmentShaderSource = R"(
169        #version 300 es
170        precision mediump float;
171        out vec4 FragColor;
172        void main() {
173            FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);
174        }
175    )";
176
177    GLuint vertexShader, fragmentShader, shaderProgram;
178    // Create a vertex shader.
179    vertexShader = glCreateShader(GL_VERTEX_SHADER);
180    glShaderSource(vertexShader, 1, &vertexShaderSource, nullptr);
181    glCompileShader(vertexShader);
182
183    // Create a fragment shader.
184    fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
185    glShaderSource(fragmentShader, 1, &fragmentShaderSource, nullptr);
186    glCompileShader(fragmentShader);
187
188    // Create a shader program.
189    shaderProgram = glCreateProgram();
190    glAttachShader(shaderProgram, vertexShader);
191    glAttachShader(shaderProgram, fragmentShader);
192    glLinkProgram(shaderProgram);
193
194    // Use the shader program.
195    glUseProgram(shaderProgram);
196
197    // Bind the vertex data.
198    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid*)0);
199    glEnableVertexAttribArray(0);
200
201    // Draw a triangle.
202    glDrawArrays(GL_TRIANGLES, 0, 3);
203
204    // Swap the buffers.
205    eglSwapBuffers(display, surface);
206
207    // Clear the resources.
208    glDeleteShader(vertexShader);
209    glDeleteShader(fragmentShader);
210    glDeleteBuffers(1, &VBO);
211
212    // Wait for exit.
213    std::cout << "Press Enter to exit..." << std::endl;
214    std::cin.get();
215
216    // Clear EGL.
217    eglDestroyContext(display, context);
218    eglDestroySurface(display, surface);
219    eglTerminate(display);
220
221    return 0;
222}
223
224```
225
226This example uses EGL to create a render surface, which can be a window surface, pbuffer, or pixmap. The following explains every step in detail.
227
228### Using eglGetDisplay to Obtain an EGL Display Connection
229```cpp
230EGLDisplay eglGetDisplay(EGLNativeDisplayType display_id);
231```
232
233The **eglGetDisplay** function returns an **EGLDisplay** object, which represents the connection to an EGL display. If no connection is available, **EGL_NO_DISPLAY** is returned.
234
235The **display_id** parameter indicates the local display type of the display. The **EGLNativeDisplayType** parameter is the native window display type, which has different definitions on different platforms. If you just want to use the default display, use **EGL_DEFAULT_DISPLAY** without explicitly specifying **display_id**.
236
237### Using eglInitialize to Initialize the EGL Display Connection
238Call **eglInitialize** to initialize the EGL display connection obtained.
239```cpp
240EGLBoolean eglInitialize(EGLDisplay display, // EGL display connection.
241                         EGLint *majorVersion, // Major version number of the EGL implementation. The value may be NULL.
242                         EGLint *minorVersion);// Minor version number of the EGL implementation. The value may be NULL.
243```
244The function is used to initialize the internal data structure of the EGL, return the EGL version numbers, and save them in **majorVersion** and **minorVersion**.
245If the initialization is successful, **EGL_TRUE** is returned. Otherwise, **EGL_FALSE** is returned. You can also call **EGLint eglGetError()** to query the EGL error status.
246
247- **EGL_BAD_DISPLAY**: The specified EGL display is invalid.
248
249- **EGL_NOT_INITIALIZED**: EGL cannot be initialized.
250
251### Using eglChooseConfig to Determine the Rendering Configuration
252After the EGL display connection is initialized, determine the type and configuration of the available surface in either of the following ways:
253- Specify a set of required configurations and use **eglChooseConfig** to enable EGL to recommend the optimal configuration.
254Generally, you can use this method because it is easier to obtain the optimal configuration.
255
256    ```cpp
257    EGLBoolean eglChooseConfig(EGLDisplay dpy, // Handle to the EGL display connection for which configurations are selected.
258                        const EGLint *attrib_list, // An integer array of pointers to attributes. Each element in the array consists of an attribute name (for example, EGL_RED_SIZE) and attribute value, and the array is terminated with EGL_NONE. An example attribute array is {EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, EGL_NONE}.
259                           EGLConfig *configs, // An array of pointers to the selected configurations. The eglChooseConfig function selects the configurations that match the attributes from the available configurations and stores them in this array.
260                           EGLint config_size,// Size of the configs array.
261                           EGLint *num_config); // Number of configurations that match the attributes.
262    ```
263
264    ```cpp
265    // Here, the following attributes are used:
266    EGLint attribs[] = {EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT, -// The renderable type is OpenGL ES 3.
267                        EGL_BLUE_SIZE, 6, // The number of bits in the blue buffer is 6.
268                        EGL_GREEN_SIZE, 8, // The number of bits in the green buffer is 8.
269                        EGL_RED_SIZE, 8, // The number of bits in the red buffer is 8.
270                        EGL_NONE};
271    eglChooseConfig(display, attribs, &config, 1, &numConfigs);
272    ```
273  In this example, the number of bits in the blue buffer is 6. To use six bits to represent the blue value 200 in the case of 8-bit RGB (ranging from 0 to 255), use the following formula for calculation: 64 x 200/256, where 64 is the maximum value that can be represented by six bits (2^6 = 64). After **eglChooseConfig** is called, the configurations that match the attributes are returned and stored in the **config** array. In the sample code, **config_size** is set to **1**, indicating that the size of the **config** array is 1. Only one set of configurations can be stored, but that's enough. **numconfigs** specifies the number of configurations that match the attributes. In this way, the desired **config** array is obtained.
274
275- Use **eglGetConfigs** to query all supported configurations and use **eglGetConfigAttrib** to filter the desired ones.
276  The following describes how to use this method to obtain the desired configurations.
277
278  ```cpp
279  #include <EGL/egl.h>
280  #include <iostream>
281  #include <vector>
282  int main() {
283      // Initialize EGL.
284      EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
285      eglInitialize(display, nullptr, nullptr);
286
287      // Obtain all the configurations.
288      EGLint numConfigs;
289      eglGetConfigs(display, nullptr, 0, &numConfigs);
290      std::vector<EGLConfig> configs(numConfigs);
291      eglGetConfigs(display, configs.data(), numConfigs, &numConfigs);
292
293      // Select a proper configuration.
294      EGLConfig chosenConfig = nullptr;
295      for (const auto& config : configs) {
296          EGLint redSize, greenSize, blueSize;
297          eglGetConfigAttrib(display, config, EGL_RED_SIZE, &redSize);
298          eglGetConfigAttrib(display, config, EGL_GREEN_SIZE, &greenSize);
299          eglGetConfigAttrib(display, config, EGL_BLUE_SIZE, &blueSize);
300          if (redSize == 8 && greenSize == 8 && blueSize == 6) {
301              chosenConfig = config;
302              break;
303          }
304      }
305
306      // If no configuration is selected, print the error information and exit.
307      if (!chosenConfig) {
308          std::cerr << "Failed to find a suitable EGL configuration." << std::endl;
309          return 1;
310      }
311      return 0;
312  }
313  ```
314
315  ```cpp
316  EGLBoolean eglGetConfigs(EGLDisplay display, // Handle to the EGL display connection for which configurations are selected.
317                           EGLConfig *configs, // Array for storing the obtained configurations.
318                           EGLint config_size, // Size of the configs array.
319                           EGLint *num_config); // Number of available configurations.
320  ```
321
322   The **eglGetConfigs** function can be used in either of the following ways:
323
324  - If a null pointer is passed in to **configs**, **EGL_TRUE** is returned and the number of available configurations obtained is saved in **num_config**. In this case, **configs** can be initialized based on the number to store the configurations. For details, see the preceding code.
325  - If **configs** is configured to accept all configurations, all configurations obtained are saved in **configs**. You can filter them as required and store the desired ones.
326
327  ```cpp
328  // Select a proper configuration.
329     EGLConfig chosenConfig = nullptr;
330         for (const auto& config : configs) {
331             EGLint redSize, greenSize, blueSize;
332             eglGetConfigAttrib(display, config, EGL_RED_SIZE, &redSize);
333             eglGetConfigAttrib(display, config, EGL_GREEN_SIZE, &greenSize);
334             eglGetConfigAttrib(display, config, EGL_BLUE_SIZE, &blueSize);
335             if (redSize == 8 && greenSize == 8 && blueSize == 6) {
336                 chosenConfig = config;
337                 break;
338             }
339         }
340  ```
341
342  The preceding code snippet traverses each configuration in **configs** and uses **eglGetConfigAttrib** to query the value of a specific attribute in the configuration, save the value in the fourth parameter, check whether the configuration is the desired one, and if yes, save the configuration. If the call is successful, **EGL_TRUE** is returned. Otherwise, **EGL_FALSE** is returned. In the latter case, you can use **eglGetError** to obtain the failure cause. If **EGL_BAD ATTRIBUTE** is returned, the attribute is invalid.
343
344  ```cpp
345  EGLBoolean eglGetConfigAttrib(EGLDisplay display, // Handle to the EGL display connection for which configurations are selected.
346                                     EGLConfig config, // EGL configuration to query.
347                                     EGLint attribute, // Attribute identifier of the EGLint type, indicating the attribute to query.
348                                     EGLint *value); // Pointer to the variable of the EGLint type, which is used to store the attribute value obtained.
349  ```
350
351
352### Using eglCreateWindowSurface to Create a Window Surface
353
354After obtaining the EGL configurations that meet the rendering requirements, use **eglCreateWindowSurface** to create a window surface.
355```cpp
356EGLSurface eglCreateWindowSurface(EGLDisplay dpy, // EGL display connection to be associated with the window surface.
357                                  EGLConfig config, // EGL configuration of the window surface to create.
358                                  EGLNativeWindowType win, // Parameter of the EGLNativeWindowType type. It is the handle or identifier of the native window and is used to associate with the EGL surface.
359                                  const EGLint *attrib_list); // Pointer to the EGL attribute list. It specifies the attributes of the window surface. It is an integer array terminating with EGL_NONE.
360```
361The following values can be passed in to **attrib_list** of **eglCreateWindowSurface**:
362
363```cpp
364EGL_RENDER_BUFFER EGL_SINGLE_BUFFER or EGL_BACK_BUFFER
365EGL_SINGLE_BUFFER // There is only one render buffer on the EGL surface. After the rendering is complete, the content in the render buffer is directly displayed on the screen. As a result, screen flickering or tearing may occur.
366EGL_BACK_BUFFER // There are a front buffer and a back buffer. After the rendering is complete, the content in the render buffer is first rendered to the back buffer, and then the content in the back buffer is displayed on the screen by means of buffer swapping. In this way, screen flickering or tearing can be avoided.
367// The default value is EGL_BACK_BUFFER. If this parameter is set to null, the default value is used.
368```
369The possible causes of a failure to call **eglCreateWindowSurface** are as follows:
370
371- **EGL_BAD_MATCH**: The native window attributes do not match the EGL configuration. This may be because the EGL configuration does not support rendering to the window (the **EGL_SURFACE_TYPE** attribute is not set to **EGL_WINDOW_BIT**).
372
373- **EGL_BAD_CONFIG**: The EGL configuration is not supported by the system.
374
375- **EGL_BAD_NATIVE_WINDOW**: The native window handle is invalid.
376
377- **EGL_BAD_ALLOC**: Resources cannot be created for a new EGL window or there is already an EGL configuration associated with the native window.
378
379```cpp
380EGLint attribList[] = { EGL_RENDER_BUFFER, EGL_BACK_BUFFER, EGL_NONE };
381EGLSurface surface = eglCreateWindowSurface(display, config, nativeWindow, attribList);
382if (surface == EGL_NO_SURFACE) {
383    switch (eglGetError()) {
384        case EGL_BAD_MATCH:
385            // Check the window and EGL configuration to determine the compatibility, or check whether the EGL configuration supports rendering to the window.
386            break;
387        case EGL_BAD_CONFIG:
388            // Check whether the EGL configuration is valid.
389            break;
390        case EGL_BAD_NATIVE_WINDOW:
391            // Check whether the EGL native window is valid.
392            break;
393        case EGL_BAD_ALLOC:
394            // Resources are insufficient. Handle and rectify the fault.
395            break;
396        default:
397            // Handle other errors.
398            break;
399    }
400}
401```
402The process of using the **XComponent** to obtain a native window is as follows:
4031. Define the **XComponent** and set the **XComponentController** in ArkTS. The **XComponent** is used to embed native rendering elements, such as OpenGL or Vulkan, into the UI.
404
405   ```typescript
406   Column() {
407       XComponent({
408           id: 'myXComponent',
409           type: XComponentType.SURFACE,
410           controller: this.xComponentController
411       })
412   }
413   ```
414
4152. Create an **XComponentController** subclass and implement its callbacks.
416
417   ```typescript
418   class MyXComponentController extends XComponentController {
419       onSurfaceCreated(surfaceId: string): void {
420           console.log(`onSurfaceCreated surfaceId: ${surfaceId}`);
421           nativeRender.SetSurfaceId(BigInt(surfaceId));
422           // The surface ID will be used to associate with the native window.
423       }
424
425       onSurfaceChanged(surfaceId: string, rect: SurfaceRect): void {
426           console.log(`onSurfaceChanged surfaceId: ${surfaceId}`);
427       }
428
429       onSurfaceDestroyed(surfaceId: string): void {
430           console.log(`onSurfaceDestroyed surfaceId: ${surfaceId}`);
431       }
432   }
433   ```
434
4353. Use the surface ID to obtain a native window.
436
437   The surface ID is generated during the creation of the **XComponent**. In the **onSurfaceCreated** callback, you can use **OH_NativeWindow_CreateNativeWindowFromSurfaceId** to obtain a native window based on the surface ID.
438
439   ```cpp
440   napi_value PluginManager::SetSurfaceId(napi_env env, napi_callback_info info)
441   {
442       int64_t surfaceId = ParseId(env, info);
443       OHNativeWindow *nativeWindow;
444       PluginRender *pluginRender;
445       if (windowMap_.find(surfaceId) == windowMap_.end()) {
446           OH_NativeWindow_CreateNativeWindowFromSurfaceId(surfaceId, &nativeWindow);
447           windowMap_[surfaceId] = nativeWindow;
448       }
449       if (pluginRenderMap_.find(surfaceId) == pluginRenderMap_.end()) {
450           pluginRender = new PluginRender(surfaceId);
451           pluginRenderMap_[surfaceId] = pluginRender;
452       }
453       pluginRender->InitNativeWindow(nativeWindow);
454       return nullptr;
455   }
456   ```
457
458For details about how to use the **XComponent**, see [ArkTS XComponent Usage Example](https://gitee.com/openharmony/applications_app_samples/tree/master/code/BasicFeature/Native/XComponent).
459### Using eglCreateContext to Create a Rendering Context
460
461The **eglCreateContext** function is used to create an EGL rendering context and associate it with a specific display and configuration. You can specify a shared context to share status information with an existing OpenGL context. The parameters in the function are described as follows:
462
463```cpp
464EGLContext eglCreateContext(EGLDisplay display, // Type of the EGL display connection for which the context is to be created.
465                            EGLConfig config, // Type of the EGL configuration associated with the context.
466                            EGLContext shareContext, // Type of the EGL context whose status information is to be shared with the newly created context. If you do not want to share the status information, pass in EGL_NO_CONTEXT.
467                            const EGLint *attribList); // Pointer to the attribute list. It specifies the attributes of the context. An attribute list is a series of attribute-value pairs terminating with EGL_NONE.
468```
469The value of **attribList** in **eglCreateContext** is as follows:
470```cpp
471EGLint contextAttribs[] = {
472    EGL_CONTEXT_CLIENT_VERSION, 3, // Context type related to OpenGL ES version 3.
473};
474```
475
476If **eglCreateContext** fails to create the rendering context, the possible cause is **EGL_BAD_CONFIG**, which means that the EGL configuration is invalid.
477
478### Using eglMakeCurrent to Attach the EGL Rendering Context to the EGL Surface
479
480```cpp
481EGLBoolean eglMakeCurrent(EGLDisplay display, // Handle to the EGL display connection.
482                          EGLSurface draw, // Handle to the EGL draw surface.
483                          EGLSurface read, // Handle to the EGL read surface. It is used to read pixels. Generally, you can set this parameter to the same value as draw.
484                          EGLContext context); // Handle to the EGL rendering context to be attached to the surface.
485```
486
487### Using glViewport to Set the Viewport
488
489```cpp
490void glViewport(GLint x, GLint y, GLsizei width, GLsizei height)
491```
492
493The **glViewport** function is used to set the viewport and specify the position and size of the OpenGL ES rendering area in the window. The **x** and **y** parameters specify the coordinates of the lower left corner of the viewport in the window. The **width** and **height** parameters specify the width and height of the viewport.
494
495### Using glClearColor to Set the Color Used to Clear the Color Buffer
496
497```cpp
498void glClearColor(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha);
499```
500In the **glClearColor(0.2f, 0.3f, 0.3f, 1.0f)** function, the color used for clearing the color buffer is set to (0.2, 0.3, 0.3). That is, the red component is 0.2, the green component is 0.3, the blue component is 0.3, and the alpha value is 1.0 (opaque).
501
502### Using glClear to Clear Buffers
503
504```cpp
505void glClear(GLbitfield mask);
506```
507The **glClear** function is used to clear a buffer. The **mask** parameter specifies the buffer to clear. It can be a combination of the following values:
508- **GL_COLOR_BUFFER_BIT**: clears the color buffer.
509- **GL_DEPTH_BUFFER_BIT**: clears the depth buffer.
510- **GL_STENCIL_BUFFER_BIT**: clears the stencil buffer.
511
512You can call **glClear(GL_COLOR_BUFFER_BIT)** to clear the color buffer and fill the buffer with the color set by **glClearColor**. Clearing the color buffer is a common operation before you start frame rendering. This operation ensures that each pixel on the screen is initialized to the specified color value. It is also a mandatory preparation for drawing a new frame, similar to painting a background color on the canvas to start a new painting.
513
514### Defining Vertex Data
515```cpp
516  // Define vertex data.
517    GLfloat vertices[] = {
518        -0.5f, -0.5f, 0.0f, // Lower left corner.
519         0.5f, -0.5f, 0.0f, // Lower right corner.
520         0.0f, 0.5f, 0.0f // Top.
521    };
522```
523
524In OpenGL, Normalized Device Coordinates (NDCs) are usually used to represent the position of a vertex. NDC is a coordinate space in the screen. In this space, the lower left corner is (-1, -1), and the upper right corner is (1, 1). This coordinate space makes the position of the vertex independent of the size and aspect ratio of the screen.
525### Managing Vertex Data
526
527You can save the vertex data on the GPU to minimize data transfer between the CPU and GPU.
528
529```cpp
530GLuint VAO[1];
531GLuint VBO;
532glGenVertexArrays(1, VAO); // Generate Vertex Array Object (VAO) names. In this example, one VBO is generated.
533glBindVertexArray(VAO[0]); // Bind the VAO to the current OpenGL context.
534glGenBuffers(1, &VBO); // Generate VBO names. The first parameter indicates the number of VBO names to generate, and it is set to 1 in this example. The passed-in value &VBO is the pointer to the array that stores the generated VBO names.
535glBindBuffer(GL_ARRAY_BUFFER, VBO); // void glBindBuffer(GLenum target, GLuint buffer), where target indicates the buffer to be bound and can be one of the following values:
536                                   // GL_ARRAY_BUFFER: stores vertex attribute data.
537                                  // GL_ELEMENT_ARRAY_BUFFER: stores index data and other data.
538                                 // buffer is the name of the VBO to be bound.
539glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
540```
541```cpp
542void glBufferData(GLenum target, // target specifies the type of the buffer object. The value can be one of the following:
543                                // GL_ARRAY_BUFFER: stores vertex attribute data.
544                               // GL_ELEMENT_ARRAY_BUFFER: stores index data.
545                  GLsizeiptr size, // Size (in bytes) of the buffer to be allocated.
546                  const GLvoid* data, // Pointer to the initial data to be copied to the buffer.
547                  GLenum usage); // Expected buffer usage mode. The value can be one of the following:
548                                // GL_STATIC_DRAW: The data is not or almost not modified and is used many times as the source for the drawing commands.
549                               // GL_DYNAMIC_DRAW: The data is frequently modified and used many times as the source for the drawing commands.
550                              // GL_STREAM_DRAW: The data is modified and is seldom used as the source for the drawing commands.
551```
552
553Once the **glBufferData** function is called, the data is copied to the OpenGL buffer object and stored in the GPU memory. This means that data can be efficiently accessed and processed on the GPU without frequent data transfer with the CPU memory.
554
555```cpp
556    const char* vertexShaderSource = R"(
557        #version 320 es // Shader of OpenGL ES 3.2 is used.
558        precision mediump float; // The floating point number uses the medium precision.
559        layout (location = 0) in vec3 aPos; // Vertex attribute variable. The variable name is aPos, the type is vec3, and the index in the vertex shader is 0. This variable receives the vertex data from the VBO. Each time the vertex shader is called, aPos is set to the position of the currently processed vertex. (The data is obtained from the VBO and stored in the GPU.)
560        void main() {
561            // gl_Position, a built-in variable of OpenGL ES, specifies the final position of each vertex. The position is the coordinates in the clip space after perspective projection transformation.
562            // After a value is assigned to gl_Position in the vertex shader, the rendering pipeline further processes the vertex and projects the vertex to the two-dimensional coordinates on the screen.
563            // When w is a non-zero value, perspective division is performed on the vertex coordinates. That is, (x/w, y/w, z/w) in (x, y, z, w) is used as the final coordinates in the clip space.
564            // Therefore, when the value of w is 1.0, perspective division does not change the coordinates.
565            gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);
566        }
567    )";
568```
569- Fragment is an element generated by rasterization. It represents a potential screen pixel, including all information related to the pixel, such as the color, depth, and stencil value. Each fragment is processed by a fragment shader, which also determines whether to write the fragment to the frame buffer.
570- A fragment shader runs on each fragment. In this example, it is used to calculate the final color value of the fragment. It can access the interpolated vertex data and perform complex operations such as lighting calculation and texture sampling.
571
572```cpp
573
574const char* fragmentShaderSource = R"(
575    #version 320 es // Shader of OpenGL ES 3.2 is used.
576    precision mediump float; // The floating point number uses the medium precision.
577    out vec4 FragColor; // Color of the output fragment.
578
579    void main() {
580        // Set the color of each fragment to vec4(1.0f, 0.5f, 0.2f, 1.0f),
581        // indicating the red, green, blue, and alpha values, respectively.
582        // This means that the output color is light orange, completely opaque.
583        // The color here is not obtained from the vertex shader through rasterization by linear interpolation. It is directly assigned.
584        FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);
585    }
586)";
587
588```
589In the OpenGL ES rendering pipeline, the following steps describe the entire process from vertex data to pixel output:
590
5911. Vertex shader processing
592
593   The vertex data in the buffer is passed into the vertex shader program and undergone the following processing:
594
595   - Matrix transformation: uses the model view (MV) matrix and projection matrix to transform the vertex position.
596
597   - Lighting calculation: calculates the color or other attributes of vertices based on the lighting formula.
598
5992. Primitive assembly
600
601   In the primitive assembly phase, the vertex data is assembled into geometric primitive, such as points, line segments, or triangles.
602
6033. Rasterization
604
605   Rasterization is performed to convert a geometric primitive (for example, a triangle) into a set of pixels on the screen. This process includes interpolation. Specifically, if a color or other attributes are set for a vertex, linear interpolation is performed on these attributes in the rasterization phase to generate fragment (pixel) data.
606
6074. Fragment shader processing
608
609   The fragment data output by rasterization is used as the input variable of the fragment shader. The following operations are carried out in the fragment shader:
610
611   - Lighting calculation: calculates the lighting effect of a fragment.
612
613   - Texture sampling: obtains color data from textures.
614
615   - Color mixing: generates new colors, depths, and screen coordinates based on lighting and texture data.
616
6175. Fragment-by-fragment processing
618
619   The output of the fragment shader is then undergone fragment-by-fragment processing as follows:
620
621   - Pixel ownership test: determines whether the fragment belongs to the current pixel area to draw.
622
623   - Scissor test: determines whether the fragment is in the visible area.
624
625   - Stencil test: uses the stencil buffer for test.
626
627   - Depth-buffer test: compares the depth values of the fragment to determine whether it visible.
628
629   - Blending: combines the newly calculated color with the existing color in the frame buffer.
630
631   - Dithering: reduces color quantization errors by applying small, random, or ordered noise to the original image to distribute these quantization errors.
632
6336. Writing the frame to the buffer
634
635   After all the preceding tests and processing, the final fragment data is written into the frame buffer and displayed as an image on the screen.
636
637### Creating and Using a Shader Program
638
639```cpp
640GLuint vertexShader, fragmentShader, shaderProgram;
641// Create a vertex shader.
642vertexShader = glCreateShader(GL_VERTEX_SHADER);
643glShaderSource(vertexShader, 1, &vertexShaderSource, nullptr);
644glCompileShader(vertexShader);
645
646// Create a fragment shader.
647fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
648glShaderSource(fragmentShader, 1, &fragmentShaderSource, nullptr);
649glCompileShader(fragmentShader);
650
651// Create a shader program.
652shaderProgram = glCreateProgram();
653glAttachShader(shaderProgram, vertexShader);
654glAttachShader(shaderProgram, fragmentShader);
655glLinkProgram(shaderProgram);
656
657// Use the shader program.
658glUseProgram(shaderProgram);
659```
660
661```cpp
662GLuint glCreateShader(GLenum shaderType);
663```
664The **glCreateShader** function is used to create a shader object of a specified type and return a handle to the object. The **shaderType** parameter specifies the type of shader to create, which can be **GL_VERTEX_SHADER** (vertex shader) or **GL_FRAGMENT_SHADER** (fragment shader).
665
666```cpp
667void glShaderSource(GLuint shader, GLsizei count, const GLchar \**string, const GLint *length);
668```
669
670The **glShaderSource** function is used to set the source code of the shader object. The following parameters are available in the function:
671
672- **shader**: identifier of the shader object for which the source code is set.
673- **count**: number of source code strings.
674- **string**: array of pointers to the source code strings.
675- **length**: pointer to an integer array that contains the length of each source code string. The value can be a null pointer, indicating that each string ends with **null**.
676
677```cpp
678void glCompileShader(GLuint shader);
679```
680
681The **glCompileShader** function is used to compile a shader object, where the **shader** parameter is the identifier of the target shader object.
682
683```cpp
684GLuint glCreateProgram(void);
685```
686
687The **glCreateProgram** function is used to create a shader program object and return the object identifier.
688
689```cpp
690void glAttachShader(GLuint program, GLuint shader);
691```
692
693The **glAttachShader** function is used to attach a shader object to a shader program object. The **program** parameter is the identifier of the target shader program object, and the **shader** parameter is the identifier of the target shader object.
694
695```cpp
696void glLinkProgram(GLuint program);
697```
698
699The **glLinkProgram** function is used to link a shader program object, that is, to link the shader attached to the program object to an executable rendering pipeline.
700
701The **program** parameter is the identifier of the target shader program object. After the shader program is linked, OpenGL merges the code in each individual shader object into an executable rendering pipeline, performs connector optimization to optimize the performance of the rendering pipeline, and binds the **Uniform** variable to the information about the Uniform block.
702
703```cpp
704void glUseProgram(GLuint program);
705```
706The **glUseProgram** function is used to activate a shader program object. After **glUseProgram** is called, all rendering calls are processed using the activated shader program.
707
708You can use the following code to check whether the call of **glCompileShader** is normal:
709
710```cpp
711// Compile the shader.
712glCompileShader(shader);
713
714// Check the compilation status.
715glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
716
717if (!compiled)
718{
719    GLint infoLen = 0;
720
721    // Obtain the length of the shader information log.
722    glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
723
724    if ( infoLen > 1 )
725    {
726        // Allocate the memory for storing the information log.
727        char *infoLog = malloc(sizeof(char) * infoLen);
728
729        // Obtain and print the shader information log.
730        glGetShaderInfoLog(shader, infoLen, NULL, infoLog);
731        esLogMessage("Error compiling shader:\n%s\n", infoLog);
732
733        // Release the allocated memory.
734        free(infoLog);
735    }
736
737    // Delete the shader that fails to be compiled.
738    glDeleteShader(shader);
739    return 0;
740}
741```
742
743You can use the following code to check whether the call of **glLinkProgram** is normal:
744
745```cpp
746// Link the program object.
747glLinkProgram(programObject);
748
749// Check the linking status.
750glGetProgramiv(programObject, GL_LINK_STATUS, &linked);
751
752if (!linked)
753{
754    GLint infoLen = 0;
755
756    // Obtain the length of the program object information log.
757    glGetProgramiv(programObject, GL_INFO_LOG_LENGTH, &infoLen);
758
759    if (infoLen > 1)
760    {
761        // Allocate the memory for storing the information log.
762        char *infoLog = malloc(sizeof(char) * infoLen);
763
764        // Obtain and print the information log of the program object.
765        glGetProgramInfoLog(programObject, infoLen, NULL, infoLog);
766        esLogMessage("Error linking program:\n%s\n", infoLog);
767
768        // Release the allocated memory.
769        free(infoLog);
770    }
771
772    // Delete the program object that fails to be linked.
773    glDeleteProgram(programObject);
774    return FALSE;
775}
776```
777
778### Determining the Configuration of the Vertex Attribute Array
779
780Determine the layout and format of the vertex attributes in the buffer.
781
782```cpp
783void glVertexAttribPointer(GLuint index, // Start index of the vertex array. The index is bound to the attribute variable in the vertex shader. (layout (location = 0) in vec3 aPos;)
784                           GLint size, // Number of components of each vertex attribute.
785                           GLenum type, // Type of each component.
786                           GLboolean normalized, // Whether to map the vertex data to [0, 1] or [-1, 1] when accessing the data.
787                           GLsizei stride, // Stride between the vertex attributes. For precision arrangement, set this parameter to 0.
788                           const void *offset); // Offset of the attribute in the buffer. It is the position from which data reading starts in the buffer.
789```
790
791```cpp
792void glEnableVertexAttribArray(GLuint index);
793```
794
795The **glEnableVertexAttribArray** function is used to enable an array of vertex attributes with a specified index. For example, call **glEnableVertexAttribArray(0)** to enable an array of vertex attributes with index 0. This array is associated with layout (location = 0) in vec3 aPos in the vertex shader program.
796
797In the sample code, the first parameter **index** of **glVertexAttribPointer** corresponds to **aPos** in the vertex shader, that is, position 0. The other parameters set the format of the vertex attribute, telling OpenGL that the attribute contains three components (x, y, and z), the data type is GL_FLOAT, and the first attribute of each vertex starts from offset 0.
798
799The **glBindBuffer** function binds the current VBO, **glBufferData** transfers vertex data to the GPU, and **glVertexAttribPointer** describes how to interpret the data. When using the VBO, vertex data is usually stored in a buffer. It is not automatically passed to the vertex shader. Therefore, the vertex attribute pointer is required to tell OpenGL ES how to interpret the data. The **glEnableVertexAttribArray** function is used to enable an array of vertex attributes at a specified position. For example, to enable an array of vertex properties at position 0, you can call **glEnableVertexAttribArray(0)**.
800
801
802### Drawing and Displaying Graphics
803
804```cpp
805void glDrawArrays(GLenum mode, // Type of the graphic to draw. For example, GL_TRIANGLES indicates that a triangle will be drawn.
806                  GLint first, // Start index of the vertex array to draw.
807                  GLsizei count // Number of vertices to draw.
808                  );
809```
810
811The **glDrawArrays** function is used to draw graphics based on the currently bound vertex array, vertex attributes, and other settings.
812
813```cpp
814EGLBoolean eglSwapBuffers(EGLDisplay dpy, // EGL display connection.
815                          EGLSurface surface); // EGL surface whose buffers are to be swapped.
816```
817
818The **eglSwapBuffers** function is used to swap the front and back buffers and display the rendering result on the screen.
819