目录

OpenGL ES

OpenGL is a cross-platform graphics API that specifies a standard software interface for 3D graphics processing hardware. OpenGL ES is a flavor of the OpenGL specification intended for embedded devices. HarmonyOS now supports OpenGL ES 3.2.

Supported Capabilities

OpenGL ES 3.2

Symbols Exported from the Standard Library

OpenGL ES 3.2 Symbols Exported from Native APIs

Introducing OpenGL

To use OpenGL capabilities, you must add related dynamic link libraries (DLLs) and header files.

Adding Dynamic Link Libraries

Add the following libraries to CMakeLists.txt.

libace_ndk.z.so
libace_napi.z.so
libGLESv3.so
libEGL.so

Including Header Files

#include <ace/xcomponent/native_interface_xcomponent.h>
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <EGL/eglplatform.h>
#include <GLES3/gl3.h>

References

To use OpenGL ES and perform related development, you need to understand the NDK development process and the use of components such as XComponent. For details, see:

OpenGL ES Extensions

EGLDisplay display;
EGLConfig config;
EGLContext context;
EGLSurface surface;
EGLint majorVersion;
EGLint minorVersion;
EGLNativeWindowType win;
display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
eglInitialize(display, &majorVersion, &minorVersion);
EGLint attribs[] = {
    EGL_RENDERABLE_TYPE,
    EGL_OPENGL_ES2_BIT,
    EGL_BLUE_SIZE, 8,
    EGL_GREEN_SIZE, 8,
    EGL_RED_SIZE, 8,
    EGL_NONE
};
eglChooseConfig(display, attribs, &config, 1, &numConfigs);
context = eglCreateContext(display, config, EGL_NO_CONTEXT, NULL);
surface = eglCreatePbufferSurface(display, config, NULL);
eglMakeCurrent(display, surface, surface, context);

char *strTest = new char[1024];
strTest = (char *)glGetString(GL_EXTENSIONS); // The return value of strTest lists all extensions supported, separated by spaces.
bool isHave = strTest.find("GL_OES_matrix_palette") != -1 ?
    true :
    false; // Check whether an extension exists. If yes, the value of isHave is true. If no, the value of isHave is false.

Example

void enableVertexAttrib(GLuint index, float *data, int32_t len)
{
    GLuint buffer;
    glGenBuffers(1, &buffer);
    glBindBuffer(GL_ARRAY_BUFFER, buffer);
    glBufferData(GL_ARRAY_BUFFER, len, data, GL_STATIC_DRAW);
    glVertexAttribPointer(index, TRIANGLES_POINT, GL_FLOAT, GL_FALSE, 0, 0);
    glEnableVertexAttribArray(index);
    return;
}

int32_t Init(void *window, int32_t width,  int32_t height)
{
    EGLNativeWindowType mEglWindow;
    EGLDisplay mEGLDisplay = EGL_NO_DISPLAY;
    EGLConfig mEGLConfig = nullptr;
    EGLContext mEGLContext = EGL_NO_CONTEXT;
    EGLContext mSharedEGLContext = EGL_NO_CONTEXT;
    EGLSurface mEGLSurface = nullptr;
    EGLint configsNum;
    mEglWindow = reinterpret_cast<EGLNativeWindowType>(window);
    
    // Initialize the EGL display.
    mEGLDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
    EGLint eglMajVers;
    EGLint eglMinVers;
    eglInitialize(mEGLDisplay, &eglMajVers, &eglMinVers);

    // Configure EGL.
    EGLint attribList[] = {
            EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT,
            EGL_RED_SIZE, 8,
            EGL_GREEN_SIZE, 8,
            EGL_BLUE_SIZE, 8,
            EGL_NONE
    };
    eglChooseConfig(mEGLDisplay, attribList, &mEGLConfig, 1, &configsNum);

    EGLint winAttribs[] = {EGL_GL_COLORSPACE_KHR, EGL_GL_COLORSPACE_SRGB_KHR, EGL_NONE};
    
    // Create an EGL surface.
    mEGLSurface = eglCreateWindowSurface(mEGLDisplay, mEGLConfig, mEglWindow, winAttribs);
    
    // Create an EGL context.
    EGLint attrib3_list[] = {
        EGL_CONTEXT_CLIENT_VERSION, 3,
    };
    mEGLContext = eglCreateContext(mEGLDisplay, mEGLConfig, mSharedEGLContext, attrib3_list);
    
    // Bind the EGL context to the surface.
    eglMakeCurrent(mEGLDisplay, mEGLSurface, mEGLSurface, mEGLContext);
    
    // Create the shader program.
    const char* g_vertexShader =
        "#version 300 es\n"
        "in vec4 a_pos;\n"
        "in vec4 a_color;\n"
        "uniform mat4 a_mx;\n"
        "uniform mat4 a_my;\n"
        "out vec4 v_color;\n"
        "void main() {\n"
        "    gl_Position = a_mx * a_my * a_pos;\n"
        "    v_color = a_color;\n"
        "}";
    
    const char* g_fragmentShader =
        "#version 300 es\n"
        "precision mediump float;\n"
        "in vec4 v_color;\n"
        "out vec4 fragColor;\n"
        "void main() {\n"
        "    fragColor = v_color;\n"
        "}";
    
    // Create a vertex shader.
    GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vertexShader, 1, &g_vertexShader, nullptr);
    glCompileShader(vertexShader);

    // Create a fragment shader.
    GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fragmentShader, 1, &g_fragmentShader, nullptr);
    glCompileShader(fragmentShader);

    
    // Create a shader program.
    mProgramHandle = glCreateProgram();
    glAttachShader(mProgramHandle, vertexShader);
    glAttachShader(mProgramHandle, fragmentShader);
    glLinkProgram(mProgramHandle);
    
    // Use the shader program.
    glUseProgram(mProgramHandle);
    
    // Clear the resources.
    glDeleteShader(vertexShader);
    glDeleteShader(fragmentShader);
    
    return 0;
}

void Update(float angleXOffset, float angleYOffset)
{
    const float pi = 3.141592;
    
    // Create the vertex position data array vertexData.
    float g_vertexData[] = {
        -0.75, -0.50, -0.43, 0.75, -0.50, -0.43, 0.00,  -0.50, 0.87,  0.75, -0.50, -0.43,
        0.00,  -0.50, 0.87,  0.00, 1.00,  0.00,  0.00,  -0.50, 0.87,  0.00, 1.00,  0.00,
        -0.75, -0.50, -0.43, 0.00, 1.00,  0.00,  -0.75, -0.50, -0.43, 0.75, -0.50, -0.43,
    };
    
    // Create the vertex color array colorData.
    float g_colorData[] = {
        1, 0, 0, 1, 0, 0, 1, 0, 0, /*Red - face 1*/
        1, 0, 0, 1, 0, 0, 1, 0, 0, /*Red - face 2*/
        1, 0, 0, 1, 0, 0, 1, 0, 0, /*Red - face 3*/
        1, 0, 0, 1, 0, 0, 1, 0, 0 /*Red - face 4*/
    };
    
    // Vertex normal array normalData.
    float g_normalData[] = {
        0.00,  -1.00, 0.00,  0.00,  -1.00, 0.00,  0.00,  -1.00, 0.00, -0.83, -0.28, -0.48,
        -0.83, -0.28, -0.48, -0.83, -0.28, -0.48, -0.83, 0.28,  0.48, -0.83, 0.28,  0.48,
        -0.83, 0.28,  0.48,  0.00,  -0.28, 0.96,  0.00,  -0.28, 0.96, 0.00,  -0.28, 0.96,
    };
    
    // Set the viewport size.
    glViewport(0, 0, m_width, m_height);
    
    // Clear the color buffer.
    glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);
    
    // Obtain the location handles of the variables in the shader program.
    GLint aPos = glGetAttribLocation(mProgramHandle, "a_pos");
    GLint aColor = glGetAttribLocation(mProgramHandle, "a_color");
    GLint aNormal = glGetAttribLocation(mProgramHandle, "a_normal");
    GLint uLightColor = glGetUniformLocation(mProgramHandle, "u_lightColor");
    GLint uLightDirection = glGetUniformLocation(mProgramHandle, "u_lightDirection");
    GLint aMx = glGetUniformLocation(mProgramHandle, "a_mx");
    GLint aMy = glGetUniformLocation(mProgramHandle, "a_my");

    angleX = angleXOffset;
    angleY = angleYOffset;

    // Y-axis rotation angle.
    float radianY = (angleY * pi) / 180.0;
    float cosY = cosf(radianY);
    float sinY = sinf(radianY);
    float myArr[] = {
        cosY, 0, -sinY, 0,
        0, 1, 0, 0,
        sinY, 0, cosY, 0,
        0, 0, 0, 1
    };

    // Pass 4x4 matrix data to the shader.
    glUniformMatrix4fv(aMy, 1, false, myArr);

    // X-axis rotation angle.
    float radianX = (angleX * pi) / 180.0;
    float cosX = cosf(radianX);
    float sinX = sinf(radianX);
    float mxArr[] = {
        1, 0, 0, 0, 0, cosX, -sinX, 0, 0, sinX, cosX, 0, 0, 0, 0, 1
    };

    glUniformMatrix4fv(aMx, 1, false, mxArr);

    // Pass the color and direction data (RGB(1,1,1), unit vector (x,y,z)) to the parallel light.
    glUniform3f(uLightColor, 1.0, 1.0, 1.0);

    // Ensure that the vector (x,y,z) has a length of 1, that is, a unit vector.
    float x = 2.0 / sqrt(15);
    float y = 2.0 / sqrt(15);
    float z = 3.0 / sqrt(15);

    glUniform3f(uLightDirection, x, -y, z);

    // Create a buffer and pass the vertex position data g_vertexData.
    enableVertexAttrib(aPos, g_vertexData, sizeof(g_vertexData));
    enableVertexAttrib(aNormal, g_normalData, sizeof(g_normalData));
    // Create a color buffer and pass the vertex color data g_colorData.
    enableVertexAttrib(aColor, g_colorData, sizeof(g_colorData));

    glEnable(GL_DEPTH_TEST);

    // Draw the tetrahedron.
    glDrawArrays(GL_TRIANGLES, 0, TETRAHEDRON_POINT);
    
    // Swap the buffers.
    eglSwapBuffers(mEGLDisplay, mEGLSurface);
}

This example walks through the process of creating and setting up an OpenGL ES rendering environment using EGL to render a 3D tetrahedron. Below is a detailed breakdown of each step.

[h2]Using eglGetDisplay to Obtain an EGL Display Connection

EGLDisplay eglGetDisplay(EGLNativeDisplayType display_id);

eglGetDisplay is a function in the EGL library. It returns an EGLDisplay object to represent the connection to the rendering target device. If the display connection is unavailable, EGL_NO_DISPLAY is returned.

The display_id parameter indicates the local display type of the display. The EGLNativeDisplayType parameter is the 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.

[h2]Using eglInitialize to Initialize the EGL Display Connection

Call eglInitialize to initialize the EGL display connection obtained.

EGLBoolean eglInitialize(EGLDisplay display,    // EGL display connection.
                         EGLint *majorVersion,  // Major version number of the EGL implementation. The value may be NULL.
                         EGLint *minorVersion); // Minor version number of the EGL implementation. The value may be NULL.

The function is used to initialize the internal data structure of the EGL, return the EGL version numbers, and save them in majorVersion and minorVersion.

If 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.

[h2]Using eglChooseConfig to Determine the Rendering Configuration

After the EGL display connection is initialized, determine the type and configuration of the available surface in either of the following ways:

[h2]Using eglCreateWindowSurface to Create a Window Surface

After obtaining the EGL configurations that meet the rendering requirements, use eglCreateWindowSurface to create a window surface.

EGLSurface eglCreateWindowSurface(EGLDisplay dpy,             // EGL display connection to be associated with the window surface.
                                  EGLConfig config,           // EGL configuration of the window surface to create.
                                  EGLNativeWindowType win,    // Parameter of the EGLNativeWindowType type. It is the handle or identifier of the window and is used to associate with the EGL surface.
                                  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.

The following values can be passed in to attrib_list of eglCreateWindowSurface:

EGL_RENDER_BUFFER EGL_SINGLE_BUFFER or EGL_BACK_BUFFER
EGL_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.
EGL_BACK_BUFFER // There is 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.
// The default value is EGL_BACK_BUFFER. If this parameter is set to null, the default value is used.

The possible causes of a failure to call eglCreateWindowSurface are as follows:

EGLint attribList[] = { EGL_RENDER_BUFFER, EGL_BACK_BUFFER, EGL_NONE };
EGLSurface surface = eglCreateWindowSurface(display, config, nativeWindow, attribList);
if (surface == EGL_NO_SURFACE) {
    switch (eglGetError()) {
        case EGL_BAD_MATCH:
            // Check the window and EGL configuration to determine the compatibility, or check whether the EGL configuration supports rendering to the window.
            break;
        case EGL_BAD_CONFIG:
            // Check whether the EGL configuration is valid.
            break;
        case EGL_BAD_NATIVE_WINDOW:
            // Check whether the EGL native window is valid.
            break;
        case EGL_BAD_ALLOC:
            // Resources are insufficient. Handle and rectify the fault.
            break;
        default:
            // Handle other errors.
            break;
    }
}

The process of using the XComponent to obtain a native window is as follows:

  1. Define the XComponent and set the XComponentController in ArkTS. The XComponent is used to embed rendering content implemented based on graphics APIs such as OpenGL or Vulkan in the UI.
    Column() {
        XComponent({
            id: 'myXComponent',
            type: XComponentType.SURFACE,
            controller: this.xComponentController
        })
    }
  2. Create an XComponentController child class and implement its callbacks.
    class MyXComponentController extends XComponentController {
        onSurfaceCreated(surfaceId: string): void {
            console.info(`onSurfaceCreated surfaceId: ${surfaceId}`);
            nativeRender.SetSurfaceId(BigInt(surfaceId));
            // The surface ID will be used to associate with the native window.
        }
    
        onSurfaceChanged(surfaceId: string, rect: SurfaceRect): void {
            console.info(`onSurfaceChanged surfaceId: ${surfaceId}`);
        }
    
        onSurfaceDestroyed(surfaceId: string): void {
            console.info(`onSurfaceDestroyed surfaceId: ${surfaceId}`);
        }
    }
  3. Use the surface ID to obtain the native window. The surface ID is generated during the XComponent creation. In the onSurfaceCreated callback, you can use OH_NativeWindow_CreateNativeWindowFromSurfaceId to obtain a native window based on the surface ID.
    napi_value PluginManager::SetSurfaceId(napi_env env, napi_callback_info info)
    {
        int64_t surfaceId = ParseId(env, info);
        OHNativeWindow *nativeWindow;
        PluginRender *pluginRender;
        if (windowMap_.find(surfaceId) == windowMap_.end()) {
            OH_NativeWindow_CreateNativeWindowFromSurfaceId(surfaceId, &nativeWindow);
            windowMap_[surfaceId] = nativeWindow;
        } else {
            return nullptr;
        }
        if (pluginRenderMap_.find(surfaceId) == pluginRenderMap_.end()) {
            pluginRender = new PluginRender(surfaceId);
            pluginRenderMap_[surfaceId] = pluginRender;
        }
        pluginRender->InitNativeWindow(nativeWindow);
        return nullptr;
    }

[h2]Using eglCreateContext to Create a Rendering Context

Use eglCreateContext 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:

EGLContext eglCreateContext(EGLDisplay display,        // Type of the EGL display connection for which the context is to be created.
                            EGLConfig config,          // Type of the EGL configuration associated with the context.
                            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.
                            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.

The value of attribList in eglCreateContext is as follows:

EGLint attrib3_list[] = {
    EGL_CONTEXT_CLIENT_VERSION, 3, // Context type related to OpenGL ES version 3.
    EGL_NONE
};

If eglCreateContext fails to create the rendering context, the possible cause is EGL_BAD_CONFIG, which means that the EGL configuration is invalid.

[h2]Using eglMakeCurrent to Attach the EGL Rendering Context to the EGL Surface

EGLBoolean eglMakeCurrent(EGLDisplay display, // Handle to the EGL display connection.
                          EGLSurface draw, // Handle to the EGL draw surface.
                          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.
                          EGLContext context); // Handle to the EGL rendering context to be attached to the surface.

[h2]Creating and Using a Shader Program

// Create a vertex shader.
GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &g_vertexShader, nullptr);
glCompileShader(vertexShader);

// Create a fragment shader.
GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &g_fragmentShader, nullptr);
glCompileShader(fragmentShader);

// Create a shader program.
mProgramHandle = glCreateProgram();
glAttachShader(mProgramHandle, vertexShader);
glAttachShader(mProgramHandle, fragmentShader);
glLinkProgram(mProgramHandle);

// Use the shader program.
glUseProgram(mProgramHandle);
GLuint glCreateShader(GLenum shaderType);

Use glCreateShader 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).

void glShaderSource(GLuint shader, GLsizei count, const GLchar \**string, const GLint *length);

Use glShaderSource set the source code of the shader object. The following parameters are available in the function:

void glCompileShader(GLuint shader);

Use glCompileShader to compile a shader object, where the shader parameter is the identifier of the target shader object.

GLuint glCreateProgram(void);

Use glCreateProgram to create a shader program object and return the object identifier.

void glAttachShader(GLuint program, GLuint shader);

Use glAttachShader 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.

void glLinkProgram(GLuint program);

Use glLinkProgram to link a shader program object, that is, to link the shader attached to the program object to an executable rendering pipeline.

The parameter program is the identifier of the shader program object to be linked. When linking a shader program, OpenGL performs the following operations: merges the code in each shader object into an executable rendering pipeline, performs linker optimization to optimize the performance of the rendering pipeline, and binds the information of Uniform variables and Uniform blocks.

void glUseProgram(GLuint program);

Use glUseProgram to activate a shader program object. After glUseProgram is called, all rendering calls are processed using the activated shader program.

You can use the following code to check whether the call of glCompileShader is normal:

// Compile the shader.
glCompileShader(shader);

// Check the compilation status.
glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);

if (!compiled)
{
    GLint infoLen = 0;
    // Obtain the length of the shader information log.
    glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
    if ( infoLen > 1 )
    {
        // Allocate the memory for storing the information log.
        char *infoLog = malloc(sizeof(char) * infoLen);
        // Obtain and print the shader information log.
        glGetShaderInfoLog(shader, infoLen, NULL, infoLog);
        esLogMessage("Error compiling shader:\n%s\n", infoLog);
        // Release the allocated memory.
        free(infoLog);
    }
    // Delete the shader that fails to be compiled.
    glDeleteShader(shader);
    return 0;
}

You can use the following code to check whether the call of glLinkProgram is normal:

// Link the program object.
glLinkProgram(programObject);

// Check the linking status.
glGetProgramiv(programObject, GL_LINK_STATUS, &linked);

if (!linked)
{
    GLint infoLen = 0;
    // Obtain the length of the program object information log.
    glGetProgramiv(programObject, GL_INFO_LOG_LENGTH, &infoLen);
    if (infoLen > 1)
    {
        // Allocate the memory for storing the information log.
        char *infoLog = malloc(sizeof(char) * infoLen);
        // Obtain and print the information log of the program object.
        glGetProgramInfoLog(programObject, infoLen, NULL, infoLog);
        esLogMessage("Error linking program:\n%s\n", infoLog);
        // Release the allocated memory.
        free(infoLog);
    }
    // Delete the program object that fails to be linked.
    glDeleteProgram(programObject);
    return FALSE;
}

[h2]Using glViewport to Set the Viewport

void glViewport(GLint x, GLint y, GLsizei width, GLsizei height)

Use glViewport 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.

[h2]Using glClearColor to Set the Color Used to Clear the Color Buffer

void glClearColor(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha);

In the glClearColor(1.0f, 1.0f, 1.0f, 1.0f) function, the color used for clearing the color buffer is set to (1.0, 1.0, 1.0). That is, the red component is 1.0, the green component is 1.0, the blue component is 1.0, and the alpha value is 1.0 (opaque).

[h2]Using glClear to Clear Buffers

void glClear(GLbitfield mask);

Use glClear to clear a buffer. The mask parameter specifies the buffer to clear. It can be a combination of the following values:

You 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.

[h2]Using glGetAttribLocation to Obtain the Location of an Attribute Variable

GLint glGetAttribLocation(GLuint program, const GLchar *name);

Use glGetAttribLocation to obtain the location of an attribute variable in a vertex shader. This location is determined based on the attribute name when the vertex shader program is compiled and linked. program is the program object to be queried, and name is the name of the attribute variable whose location is to be queried.

[h2]Using glGetUniformLocation to Get the Uniform Variable Location

GLint glGetUniformLocation(GLuint program, const GLchar *name);

The glGetUniformLocation function is used to query the location of a specific uniform variable in a program object. The parameters are described as follows:

The return value of glGetUniformLocation is the location of the uniform variable to be queried (of the GLint type). If -1 is returned, the specified uniform variable is not found. Note that the program object must be successfully linked (by calling glLinkProgram) before this function is called. In addition, if the uniform variable is optimized out in the shader or does not exist, this function also returns -1.

After the uniform variable location is obtained, you can use functions such as glUniformMatrix4fv and glUniform3f to pass data to the uniform variable.

[h2]Using glUniformMatrix4fv to Pass a 4 x 4 Matrix

void glUniformMatrix4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);

The glUniformMatrix4fv function is used to pass 4 x 4 matrix data to a uniform variable. The parameters are described as follows:

[h2]Using glUniform3f to Pass Colors and Directions to a Shader

void glUniform3f(GLint location, GLfloat v0, GLfloat v1, GLfloat v2);

Use glUniform3f to set the value of a uniform variable for the current program object. location is the position of the variable, and v0, v1, and v2 are the new values.

[h2]Creating a Buffer and Uploading Data to the GPU

GLuint buffer;
glGenBuffers(1, &buffer);                                                  // Generate a buffer object.
glBindBuffer(GL_ARRAY_BUFFER, buffer);                                     // Bind the buffer, and set it as the current one.
glBufferData(GL_ARRAY_BUFFER, len, data, GL_STATIC_DRAW);                  // Upload data to the GPU.
glVertexAttribPointer(index, TRIANGLES_POINT, GL_FLOAT, GL_FALSE, 0, 0);   // Set the vertex attribute pointer.
glEnableVertexAttribArray(index);                                          // Enable the vertex attribute array.
void glBindBuffer(GLenum target,   // target specifies the buffer target to bind. The value can be one of the following:
                                   // GL_ARRAY_BUFFER: stores vertex attribute data.
                                   // GL_ELEMENT_ARRAY_BUFFER: stores index data and other data.
                  GLuint buffer);  // buffer specifies the name of the buffer object to bind.
void glBufferData(GLenum target,       // target specifies the type of the buffer object. The value can be one of the following:
                                       // GL_ARRAY_BUFFER: stores vertex attribute data.
                                       // GL_ELEMENT_ARRAY_BUFFER: stores index data.
                  GLsizeiptr size,     // Size (in bytes) of the buffer to be allocated.
                  const GLvoid* data,  // Pointer to the initial data to be copied to the buffer.
                  GLenum usage);       // Expected buffer usage mode. The value can be one of the following:
                                       // GL_STATIC_DRAW: Data is rarely modified and used multiple times.
                                       // GL_DYNAMIC_DRAW: Data is frequently modified and used multiple times.
                                       // GL_STREAM_DRAW: Data is modified and used a few times.

Once glBufferData is called, the data is copied into an OpenGL buffer and stored in the GPU's memory. This allows the data to be efficiently accessed and processed on the GPU without frequent transfers from CPU memory, improving rendering performance.

void 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;)
                           GLint size,           // Number of components of each vertex attribute.
                           GLenum type,          // Type of each component.
                           GLboolean normalized, // Whether to map the vertex data to [0, 1] or [-1, 1] when accessing the data.
                           GLsizei stride,       // Specifies the offset between vertex attributes. Set it to 0 if they are tightly packed.
                           const void *offset);  // Offset of the attribute in the buffer. It is the position from which data reading starts in the buffer.
void glEnableVertexAttribArray(GLuint index);

Use glEnableVertexAttribArray to enable an array of vertex attributes with a specified index. For example, you can call glEnableVertexAttribArray(0) to enable the vertex attribute array with the location index of 0.

[h2]Enabling Features

void glEnable(GLenum cap);

Use glEnable to activate various features, with the specific feature determined by the cap parameter:

[h2]Drawing and Displaying Primitives

void glDrawArrays(GLenum mode,   // Type of the primitive to draw. For example, GL_TRIANGLES indicates that a triangle will be drawn.
                  GLint first,   // Start index of the vertex array to draw.
                  GLsizei count // Number of vertices to draw.
                  );

Use glDrawArrays to draw primitives based on the currently bound vertex array, vertex attributes, and other settings.

EGLBoolean eglSwapBuffers(EGLDisplay dpy,      // EGL display connection.
                          EGLSurface surface); // EGL surface whose buffers are to be swapped.

Use eglSwapBuffers to swap the front and back buffers and display the rendering result on the screen.