1e41f4b71Sopenharmony_ci# OpenGL ES 2e41f4b71Sopenharmony_ci 3e41f4b71Sopenharmony_ciOpenGL 是一种跨平台的图形 API,用于为 3D 图形处理硬件指定标准的软件接口。[OpenGL ES](https://www.khronos.org/opengles/) 是 OpenGL 规范的一种形式,适用于嵌入式设备。OpenHarmony 现已支持 OpenGL ES 3.2。 4e41f4b71Sopenharmony_ci 5e41f4b71Sopenharmony_ci## 支持的能力 6e41f4b71Sopenharmony_ci 7e41f4b71Sopenharmony_ciOpenGL ES 3.2 8e41f4b71Sopenharmony_ci 9e41f4b71Sopenharmony_ci## 标准库中导出的符号列表 10e41f4b71Sopenharmony_ci 11e41f4b71Sopenharmony_ci[native api中导出的OpenGL ES 3.2符号列表](openglesv3-symbol.md) 12e41f4b71Sopenharmony_ci 13e41f4b71Sopenharmony_ci## 引入OpenGL能力 14e41f4b71Sopenharmony_ci 15e41f4b71Sopenharmony_ci如果开发者需要使用OpenGL的相关能力,需要添加相关动态链接库和头文件。 16e41f4b71Sopenharmony_ci 17e41f4b71Sopenharmony_ci**添加动态链接库** 18e41f4b71Sopenharmony_ci 19e41f4b71Sopenharmony_ciCMakeLists.txt中添加以下lib。 20e41f4b71Sopenharmony_ci 21e41f4b71Sopenharmony_ci```txt 22e41f4b71Sopenharmony_cilibace_ndk.z.so 23e41f4b71Sopenharmony_cilibace_napi.z.so 24e41f4b71Sopenharmony_cilibGLESv3.so 25e41f4b71Sopenharmony_cilibEGL.so 26e41f4b71Sopenharmony_ci``` 27e41f4b71Sopenharmony_ci 28e41f4b71Sopenharmony_ci**头文件** 29e41f4b71Sopenharmony_ci 30e41f4b71Sopenharmony_ci```c++ 31e41f4b71Sopenharmony_ci#include <ace/xcomponent/native_interface_xcomponent.h> 32e41f4b71Sopenharmony_ci#include <EGL/egl.h> 33e41f4b71Sopenharmony_ci#include <EGL/eglext.h> 34e41f4b71Sopenharmony_ci#include <EGL/eglplatform.h> 35e41f4b71Sopenharmony_ci#include <GLES3/gl3.h> 36e41f4b71Sopenharmony_ci``` 37e41f4b71Sopenharmony_ci 38e41f4b71Sopenharmony_ci## 相关参考 39e41f4b71Sopenharmony_ci 40e41f4b71Sopenharmony_ci针对OpenGL ES的使用和相关开发,需要同步了解NDK的开发过程,以及XComponent组件等的使用,具体可参考: 41e41f4b71Sopenharmony_ci 42e41f4b71Sopenharmony_ci- [NDK开发参考](../../napi/ndk-development-overview.md) 43e41f4b71Sopenharmony_ci 44e41f4b71Sopenharmony_ci- [NodeAPI参考](./napi.md) 45e41f4b71Sopenharmony_ci 46e41f4b71Sopenharmony_ci- [XComponentNode参考](../apis-arkui/js-apis-arkui-xcomponentNode.md) 47e41f4b71Sopenharmony_ci 48e41f4b71Sopenharmony_ci- [XComponent参考](../apis-arkui/arkui-ts/ts-basic-components-xcomponent.md) 49e41f4b71Sopenharmony_ci 50e41f4b71Sopenharmony_ci## OpenGL ES扩展接口 51e41f4b71Sopenharmony_ci 52e41f4b71Sopenharmony_ci- OpenGL ES扩展接口的官方参考文档:[OpenGL ES扩展接口](https://registry.khronos.org/OpenGL/index_es.php) 53e41f4b71Sopenharmony_ci- 开发者可以调用`glGetString`查询芯片厂商支持的扩展接口,调用之前务必初始化上下文,具体示例如下: 54e41f4b71Sopenharmony_ci 55e41f4b71Sopenharmony_ci```c++ 56e41f4b71Sopenharmony_ciEGLDisplay display; 57e41f4b71Sopenharmony_ciEGLConfig config; 58e41f4b71Sopenharmony_ciEGLContext context; 59e41f4b71Sopenharmony_ciEGLSurface surface; 60e41f4b71Sopenharmony_ciEGLint majorVersion; 61e41f4b71Sopenharmony_ciEGLint minorVersion; 62e41f4b71Sopenharmony_ciEGLNativeWindowType win; 63e41f4b71Sopenharmony_cidisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); 64e41f4b71Sopenharmony_cieglInitialize(display, &majorVersion, &minorVersion); 65e41f4b71Sopenharmony_cidisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); 66e41f4b71Sopenharmony_cieglInitialize(display, &majorVersion, &minorVersion); 67e41f4b71Sopenharmony_ciEGLint attribs[] = { 68e41f4b71Sopenharmony_ci EGL_RENDERABLE_TYPE, 69e41f4b71Sopenharmony_ci EGL_OPENGL_ES2_BIT, 70e41f4b71Sopenharmony_ci EGL_BLUE_SIZE, 8, 71e41f4b71Sopenharmony_ci EGL_GREEN_SIZE, 8, 72e41f4b71Sopenharmony_ci EGL_RED_SIZE, 8, 73e41f4b71Sopenharmony_ci EGL_NONE 74e41f4b71Sopenharmony_ci}; 75e41f4b71Sopenharmony_cieglChooseConfig(display, attribs, &config, 1, &numConfigs); 76e41f4b71Sopenharmony_cicontext = eglCreateContext(display, config, EGL_NO_CONTEXT, NULL); 77e41f4b71Sopenharmony_cisurface = eglCreatePbufferSurface(display, config, NULL); 78e41f4b71Sopenharmony_cieglMakeCurrent(display, surface, surface, context); 79e41f4b71Sopenharmony_ci 80e41f4b71Sopenharmony_cichar *strTest = new char[1024]; 81e41f4b71Sopenharmony_cistrTest = (char *)glGetString(GL_EXTENSIONS); // 返回值strTest中会列出所有的扩展接口,并且用空格分隔开 82e41f4b71Sopenharmony_cibool isHave = strTest.find("GL_OES_matrix_palette") != -1 ? 83e41f4b71Sopenharmony_ci true : 84e41f4b71Sopenharmony_ci false; // 查询是否有某个扩展接口,有则isHave为true,没有则为false 85e41f4b71Sopenharmony_ci``` 86e41f4b71Sopenharmony_ci 87e41f4b71Sopenharmony_ci## 简单示例 88e41f4b71Sopenharmony_ci 89e41f4b71Sopenharmony_ci```cpp 90e41f4b71Sopenharmony_ci#include <EGL/egl.h> 91e41f4b71Sopenharmony_ci#include <GLES3/gl3.h> 92e41f4b71Sopenharmony_ci#include <iostream> 93e41f4b71Sopenharmony_ci 94e41f4b71Sopenharmony_ci#define WINDOW_WIDTH 800 95e41f4b71Sopenharmony_ci#define WINDOW_HEIGHT 600 96e41f4b71Sopenharmony_ci 97e41f4b71Sopenharmony_ciint main() { 98e41f4b71Sopenharmony_ci // 初始化 EGL 99e41f4b71Sopenharmony_ci EGLDisplay display; 100e41f4b71Sopenharmony_ci EGLConfig config; 101e41f4b71Sopenharmony_ci EGLContext context; 102e41f4b71Sopenharmony_ci EGLSurface surface; 103e41f4b71Sopenharmony_ci EGLint numConfigs; 104e41f4b71Sopenharmony_ci EGLint majorVersion; 105e41f4b71Sopenharmony_ci EGLint minorVersion; 106e41f4b71Sopenharmony_ci 107e41f4b71Sopenharmony_ci // 初始化 EGL Display 108e41f4b71Sopenharmony_ci display = eglGetDisplay(EGL_DEFAULT_DISPLAY); 109e41f4b71Sopenharmony_ci eglInitialize(display, &majorVersion, &minorVersion); 110e41f4b71Sopenharmony_ci 111e41f4b71Sopenharmony_ci // 配置 EGL 112e41f4b71Sopenharmony_ci EGLint attribs[] = { 113e41f4b71Sopenharmony_ci EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT, 114e41f4b71Sopenharmony_ci EGL_BLUE_SIZE, 6, 115e41f4b71Sopenharmony_ci EGL_GREEN_SIZE, 8, 116e41f4b71Sopenharmony_ci EGL_RED_SIZE, 8, 117e41f4b71Sopenharmony_ci EGL_NONE 118e41f4b71Sopenharmony_ci }; 119e41f4b71Sopenharmony_ci eglChooseConfig(display, attribs, &config, 1, &numConfigs); 120e41f4b71Sopenharmony_ci 121e41f4b71Sopenharmony_ci // 创建 EGL Context 122e41f4b71Sopenharmony_ci EGLint contextAttribs[] = { 123e41f4b71Sopenharmony_ci EGL_CONTEXT_CLIENT_VERSION, 3, 124e41f4b71Sopenharmony_ci EGL_NONE 125e41f4b71Sopenharmony_ci }; 126e41f4b71Sopenharmony_ci 127e41f4b71Sopenharmony_ci // 创建 EGL Surface 128e41f4b71Sopenharmony_ci surface = eglCreateWindowSurface(display, config, nativeWindow, NULL); 129e41f4b71Sopenharmony_ci 130e41f4b71Sopenharmony_ci context = eglCreateContext(display, config, EGL_NO_CONTEXT, contextAttribs); 131e41f4b71Sopenharmony_ci 132e41f4b71Sopenharmony_ci // 绑定 EGL Context 和 Surface 133e41f4b71Sopenharmony_ci eglMakeCurrent(display, surface, surface, context); 134e41f4b71Sopenharmony_ci 135e41f4b71Sopenharmony_ci // 设置视口大小 136e41f4b71Sopenharmony_ci glViewport(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT); 137e41f4b71Sopenharmony_ci 138e41f4b71Sopenharmony_ci // 清除颜色缓冲 139e41f4b71Sopenharmony_ci glClearColor(0.2f, 0.3f, 0.3f, 1.0f); 140e41f4b71Sopenharmony_ci glClear(GL_COLOR_BUFFER_BIT); 141e41f4b71Sopenharmony_ci 142e41f4b71Sopenharmony_ci // 定义顶点数据 143e41f4b71Sopenharmony_ci GLfloat vertices[] = { 144e41f4b71Sopenharmony_ci -0.5f, -0.5f, 0.0f, // 左下角 145e41f4b71Sopenharmony_ci 0.5f, -0.5f, 0.0f, // 右下角 146e41f4b71Sopenharmony_ci 0.0f, 0.5f, 0.0f // 顶部 147e41f4b71Sopenharmony_ci }; 148e41f4b71Sopenharmony_ci 149e41f4b71Sopenharmony_ci // 创建并绑定顶点缓冲对象 150e41f4b71Sopenharmony_ci GLuint VAO[0]; 151e41f4b71Sopenharmony_ci GLuint VBO; 152e41f4b71Sopenharmony_ci glGenVertexArrays(1, VAO); 153e41f4b71Sopenharmony_ci glBindVertexArray(VAO[0]); 154e41f4b71Sopenharmony_ci glGenBuffers(1, &VBO); 155e41f4b71Sopenharmony_ci glBindBuffer(GL_ARRAY_BUFFER, VBO); 156e41f4b71Sopenharmony_ci glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); 157e41f4b71Sopenharmony_ci 158e41f4b71Sopenharmony_ci // 创建着色器程序 159e41f4b71Sopenharmony_ci const char* vertexShaderSource = R"( 160e41f4b71Sopenharmony_ci #version 300 es 161e41f4b71Sopenharmony_ci precision mediump float; 162e41f4b71Sopenharmony_ci layout (location = 0) in vec3 aPos; 163e41f4b71Sopenharmony_ci void main() { 164e41f4b71Sopenharmony_ci gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0); 165e41f4b71Sopenharmony_ci } 166e41f4b71Sopenharmony_ci )"; 167e41f4b71Sopenharmony_ci 168e41f4b71Sopenharmony_ci const char* fragmentShaderSource = R"( 169e41f4b71Sopenharmony_ci #version 300 es 170e41f4b71Sopenharmony_ci precision mediump float; 171e41f4b71Sopenharmony_ci out vec4 FragColor; 172e41f4b71Sopenharmony_ci void main() { 173e41f4b71Sopenharmony_ci FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f); 174e41f4b71Sopenharmony_ci } 175e41f4b71Sopenharmony_ci )"; 176e41f4b71Sopenharmony_ci 177e41f4b71Sopenharmony_ci GLuint vertexShader, fragmentShader, shaderProgram; 178e41f4b71Sopenharmony_ci // 创建顶点着色器 179e41f4b71Sopenharmony_ci vertexShader = glCreateShader(GL_VERTEX_SHADER); 180e41f4b71Sopenharmony_ci glShaderSource(vertexShader, 1, &vertexShaderSource, nullptr); 181e41f4b71Sopenharmony_ci glCompileShader(vertexShader); 182e41f4b71Sopenharmony_ci 183e41f4b71Sopenharmony_ci // 创建片段着色器 184e41f4b71Sopenharmony_ci fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); 185e41f4b71Sopenharmony_ci glShaderSource(fragmentShader, 1, &fragmentShaderSource, nullptr); 186e41f4b71Sopenharmony_ci glCompileShader(fragmentShader); 187e41f4b71Sopenharmony_ci 188e41f4b71Sopenharmony_ci // 创建着色器程序 189e41f4b71Sopenharmony_ci shaderProgram = glCreateProgram(); 190e41f4b71Sopenharmony_ci glAttachShader(shaderProgram, vertexShader); 191e41f4b71Sopenharmony_ci glAttachShader(shaderProgram, fragmentShader); 192e41f4b71Sopenharmony_ci glLinkProgram(shaderProgram); 193e41f4b71Sopenharmony_ci 194e41f4b71Sopenharmony_ci // 使用着色器程序 195e41f4b71Sopenharmony_ci glUseProgram(shaderProgram); 196e41f4b71Sopenharmony_ci 197e41f4b71Sopenharmony_ci // 绑定顶点数据 198e41f4b71Sopenharmony_ci glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid*)0); 199e41f4b71Sopenharmony_ci glEnableVertexAttribArray(0); 200e41f4b71Sopenharmony_ci 201e41f4b71Sopenharmony_ci // 绘制三角形 202e41f4b71Sopenharmony_ci glDrawArrays(GL_TRIANGLES, 0, 3); 203e41f4b71Sopenharmony_ci 204e41f4b71Sopenharmony_ci // 交换缓冲区 205e41f4b71Sopenharmony_ci eglSwapBuffers(display, surface); 206e41f4b71Sopenharmony_ci 207e41f4b71Sopenharmony_ci // 清理 208e41f4b71Sopenharmony_ci glDeleteShader(vertexShader); 209e41f4b71Sopenharmony_ci glDeleteShader(fragmentShader); 210e41f4b71Sopenharmony_ci glDeleteBuffers(1, &VBO); 211e41f4b71Sopenharmony_ci 212e41f4b71Sopenharmony_ci // 等待退出 213e41f4b71Sopenharmony_ci std::cout << "Press Enter to exit..." << std::endl; 214e41f4b71Sopenharmony_ci std::cin.get(); 215e41f4b71Sopenharmony_ci 216e41f4b71Sopenharmony_ci // 清理 EGL 217e41f4b71Sopenharmony_ci eglDestroyContext(display, context); 218e41f4b71Sopenharmony_ci eglDestroySurface(display, surface); 219e41f4b71Sopenharmony_ci eglTerminate(display); 220e41f4b71Sopenharmony_ci 221e41f4b71Sopenharmony_ci return 0; 222e41f4b71Sopenharmony_ci} 223e41f4b71Sopenharmony_ci 224e41f4b71Sopenharmony_ci``` 225e41f4b71Sopenharmony_ci 226e41f4b71Sopenharmony_ci该示例首先使用了EGL创建了渲染表面,EGL可以用于管理绘图表面(window surface只是一种类型,还有pbuffer、pixmap)。下面详细地解释下每个步骤。 227e41f4b71Sopenharmony_ci 228e41f4b71Sopenharmony_ci### 使用eglGetDisplay连接渲染目标设备 229e41f4b71Sopenharmony_ci```cpp 230e41f4b71Sopenharmony_ciEGLDisplay eglGetDisplay(EGLNativeDisplayType display_id); 231e41f4b71Sopenharmony_ci``` 232e41f4b71Sopenharmony_ci 233e41f4b71Sopenharmony_cieglGetDisplay是EGL库中的一个函数,函数返回EGLDisplay对象,它代表了与渲染目标设备的连接,如果显示连接不可用,eglGetDisplay将返回 EGL_NO_DISPLAY,这个错误表示显示连接不可用。 234e41f4b71Sopenharmony_ci 235e41f4b71Sopenharmony_cidisplay_id 参数通常是一个表示显示设备的本地显示类型,EGLNativeDisplayType是为了匹配原生窗口显示类型,在各个平台有不同的定义。如果您只是希望使用默认的显示设备,那么您可以直接使用 EGL_DEFAULT_DISPLAY,而不需要显式地指定 display_id。 236e41f4b71Sopenharmony_ci 237e41f4b71Sopenharmony_ci### 使用eglInitialize初始化EGL 238e41f4b71Sopenharmony_ci当成功打开连接之后则需要调用eglInitialize初始化EGL。 239e41f4b71Sopenharmony_ci```cpp 240e41f4b71Sopenharmony_ciEGLBoolean eglInitialize(EGLDisplay display, // 指定EGL显示连接 241e41f4b71Sopenharmony_ci EGLint *majorVersion, // 指定EGL实现返回的主版本号,可能为NULL 242e41f4b71Sopenharmony_ci EGLint *minorVersion);// 指定EGL实现返回的次版本号,可能为NULL 243e41f4b71Sopenharmony_ci``` 244e41f4b71Sopenharmony_ci这个函数用于初始化EGL内部数据结构,将返回EGL的版本号,并将其保存在majorVersion、minorVersion。 245e41f4b71Sopenharmony_ci如果初始化成功,则返回EGL_TRUE,否则返回EGL_FALSE。另外还可以通过EGLint eglGetError(),查询EGL的错误状态: 246e41f4b71Sopenharmony_ci 247e41f4b71Sopenharmony_ci- EGL_BAD_DISPLAY:表示没有指定有效的EGLDisplay。 248e41f4b71Sopenharmony_ci 249e41f4b71Sopenharmony_ci- EGL_NOT_INITIALIZED:表示EGL不能初始化。 250e41f4b71Sopenharmony_ci 251e41f4b71Sopenharmony_ci### 使用eglChooseConfig确定渲染配置 252e41f4b71Sopenharmony_ciEGL初始化成功之后,需要确定可用渲染表面的类型和配置,目前支持两种方法: 253e41f4b71Sopenharmony_ci- 可以指定一组需要的配置,使用eglChooseConfig使EGL推荐最佳配置。 254e41f4b71Sopenharmony_ci一般情况下使用此种方法,因为这样更容易获得最佳配置。 255e41f4b71Sopenharmony_ci 256e41f4b71Sopenharmony_ci ```cpp 257e41f4b71Sopenharmony_ci EGLBoolean eglChooseConfig(EGLDisplay dpy, // EGL显示连接句柄,标识了要进行配置选择的显示连接。 258e41f4b71Sopenharmony_ci const EGLint *attrib_list, // 一个以EGL_NONE结尾的整数数组,用于指定所需配置的属性。属性列表中的每个元素都由属性名称(如EGL_RED_SIZE)和相应的属性值组成。如{EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, EGL_NONE}。 259e41f4b71Sopenharmony_ci EGLConfig *configs, // 一个用于存储选择配置的指针数组。eglChooseConfig函数将从可用配置中选择适合条件的配置,并将其存储在此数组中。 260e41f4b71Sopenharmony_ci EGLint config_size,// configs数组的大小 261e41f4b71Sopenharmony_ci EGLint *num_config); // 存储满足attrib_list需求,得到的满足需求的实际配置数量。 262e41f4b71Sopenharmony_ci ``` 263e41f4b71Sopenharmony_ci 264e41f4b71Sopenharmony_ci ```cpp 265e41f4b71Sopenharmony_ci // 如以上代码所示这里指定所需配置的属性为 266e41f4b71Sopenharmony_ci EGLint attribs[] = {EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT, -// 指定了渲染类型为 OpenGL ES 3 267e41f4b71Sopenharmony_ci EGL_BLUE_SIZE, 6, // 指定蓝色缓冲区的位数是6位 268e41f4b71Sopenharmony_ci EGL_GREEN_SIZE, 8, // 指定绿色缓冲区的位数是8位 269e41f4b71Sopenharmony_ci EGL_RED_SIZE, 8, // 指定红色缓冲区的位数是8位 270e41f4b71Sopenharmony_ci EGL_NONE}; 271e41f4b71Sopenharmony_ci eglChooseConfig(display, attribs, &config, 1, &numConfigs); 272e41f4b71Sopenharmony_ci ``` 273e41f4b71Sopenharmony_ci 以上示例中,蓝色缓冲区的位数被指定为6位。这意味着在8位RGB(范围从0到255)的情况下,要表示蓝色值200,可以使用6位来表示。具体计算如下:因为6位可以表示的最大数值是2^6= 64,因此需要将8位的数值映射到6位的范围,即使用公式64* 200 / 256进行计算。使用了eglChooseConfig后根据指定的配置属性attribs 将返回满足需求的配置,存放在config中。示例代码config_size传入了1,表明config数组的大小为1。只能保存一组可用配置,但那也是足够的。而numconfigs 保存满足指定配置的所有配置数量。 这样我们得到了满足我们需求的config。 274e41f4b71Sopenharmony_ci 275e41f4b71Sopenharmony_ci- 也可以使用eglGetConfigs查询支持的所有配置,并使用eglGetConfigAttrib筛选需要的配置。 276e41f4b71Sopenharmony_ci 以下提供使用此种方法得到满足需求的配置,具体可见示例: 277e41f4b71Sopenharmony_ci 278e41f4b71Sopenharmony_ci ```cpp 279e41f4b71Sopenharmony_ci #include <EGL/egl.h> 280e41f4b71Sopenharmony_ci #include <iostream> 281e41f4b71Sopenharmony_ci #include <vector> 282e41f4b71Sopenharmony_ci int main() { 283e41f4b71Sopenharmony_ci // 初始化 EGL 284e41f4b71Sopenharmony_ci EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); 285e41f4b71Sopenharmony_ci eglInitialize(display, nullptr, nullptr); 286e41f4b71Sopenharmony_ci 287e41f4b71Sopenharmony_ci // 获取所有配置 288e41f4b71Sopenharmony_ci EGLint numConfigs; 289e41f4b71Sopenharmony_ci eglGetConfigs(display, nullptr, 0, &numConfigs); 290e41f4b71Sopenharmony_ci std::vector<EGLConfig> configs(numConfigs); 291e41f4b71Sopenharmony_ci eglGetConfigs(display, configs.data(), numConfigs, &numConfigs); 292e41f4b71Sopenharmony_ci 293e41f4b71Sopenharmony_ci // 选择合适的配置 294e41f4b71Sopenharmony_ci EGLConfig chosenConfig = nullptr; 295e41f4b71Sopenharmony_ci for (const auto& config : configs) { 296e41f4b71Sopenharmony_ci EGLint redSize, greenSize, blueSize; 297e41f4b71Sopenharmony_ci eglGetConfigAttrib(display, config, EGL_RED_SIZE, &redSize); 298e41f4b71Sopenharmony_ci eglGetConfigAttrib(display, config, EGL_GREEN_SIZE, &greenSize); 299e41f4b71Sopenharmony_ci eglGetConfigAttrib(display, config, EGL_BLUE_SIZE, &blueSize); 300e41f4b71Sopenharmony_ci if (redSize == 8 && greenSize == 8 && blueSize == 6) { 301e41f4b71Sopenharmony_ci chosenConfig = config; 302e41f4b71Sopenharmony_ci break; 303e41f4b71Sopenharmony_ci } 304e41f4b71Sopenharmony_ci } 305e41f4b71Sopenharmony_ci 306e41f4b71Sopenharmony_ci // 如果未选择配置,则打印错误信息并退出 307e41f4b71Sopenharmony_ci if (!chosenConfig) { 308e41f4b71Sopenharmony_ci std::cerr << "Failed to find a suitable EGL configuration." << std::endl; 309e41f4b71Sopenharmony_ci return 1; 310e41f4b71Sopenharmony_ci } 311e41f4b71Sopenharmony_ci return 0; 312e41f4b71Sopenharmony_ci } 313e41f4b71Sopenharmony_ci ``` 314e41f4b71Sopenharmony_ci 315e41f4b71Sopenharmony_ci ```cpp 316e41f4b71Sopenharmony_ci EGLBoolean eglGetConfigs(EGLDisplay display, // EGL显示连接句柄,标识了要进行配置选择的显示连接。 317e41f4b71Sopenharmony_ci EGLConfig *configs, // 用于保存得到配置的数组 318e41f4b71Sopenharmony_ci EGLint config_size, // configs的数组大小 319e41f4b71Sopenharmony_ci EGLint *num_config);// 得到的EGL所有可用配置数量 320e41f4b71Sopenharmony_ci ``` 321e41f4b71Sopenharmony_ci 322e41f4b71Sopenharmony_ci eglGetConfigs接口有以下两种用法: 323e41f4b71Sopenharmony_ci 324e41f4b71Sopenharmony_ci - 当我们传递configs为nullptr时,接口会返回EGL_TRUE,并将得到的EGL所有可用配置数量保存在num_config中,这时即可根据得到的数量初始化configs来保存这些配置了,具体见如上代码。 325e41f4b71Sopenharmony_ci - 当传递configs数组接受所有配置时,将得到所有配置并保存在configs,这样即可得到所有的可用配置,接下来可以根据具体需求筛选一组config保存下来。 326e41f4b71Sopenharmony_ci 327e41f4b71Sopenharmony_ci ```cpp 328e41f4b71Sopenharmony_ci // 选择合适的配置 329e41f4b71Sopenharmony_ci EGLConfig chosenConfig = nullptr; 330e41f4b71Sopenharmony_ci for (const auto& config : configs) { 331e41f4b71Sopenharmony_ci EGLint redSize, greenSize, blueSize; 332e41f4b71Sopenharmony_ci eglGetConfigAttrib(display, config, EGL_RED_SIZE, &redSize); 333e41f4b71Sopenharmony_ci eglGetConfigAttrib(display, config, EGL_GREEN_SIZE, &greenSize); 334e41f4b71Sopenharmony_ci eglGetConfigAttrib(display, config, EGL_BLUE_SIZE, &blueSize); 335e41f4b71Sopenharmony_ci if (redSize == 8 && greenSize == 8 && blueSize == 6) { 336e41f4b71Sopenharmony_ci chosenConfig = config; 337e41f4b71Sopenharmony_ci break; 338e41f4b71Sopenharmony_ci } 339e41f4b71Sopenharmony_ci } 340e41f4b71Sopenharmony_ci ``` 341e41f4b71Sopenharmony_ci 342e41f4b71Sopenharmony_ci 如上所示遍历configs每个配置 ,使用eglGetConfigAttrib查询该配置下特定属性的值,将该值保存在第4个参数中,并判断值是否是自己需要的,如果需要则保存该配置,以待使用。调用成功则返EGL_TRUE,调用失败则返回EGL_FALSE。 如果返回EGL_FALSE,可以使用eglGetError查询失败的原因,如果返回EGL_BAD ATTRIBUTE则attribute不是有效的属性。 343e41f4b71Sopenharmony_ci 344e41f4b71Sopenharmony_ci ```cpp 345e41f4b71Sopenharmony_ci EGLBoolean eglGetConfigAttrib(EGLDisplay display, //EGL 显示连接句柄,标识了要进行配置选择的显示连接 346e41f4b71Sopenharmony_ci EGLConfig config, //EGLConfig 对象,表示要查询的 EGL 配置 347e41f4b71Sopenharmony_ci EGLint attribute, //EGLint 类型的属性标识符,表示要查询的属性 348e41f4b71Sopenharmony_ci EGLint *value); //指向 EGLint 类型变量的指针,用于存储查询到的属性值。 349e41f4b71Sopenharmony_ci ``` 350e41f4b71Sopenharmony_ci 351e41f4b71Sopenharmony_ci 352e41f4b71Sopenharmony_ci### 使用eglCreateWindowSurface创建窗口表面 353e41f4b71Sopenharmony_ci 354e41f4b71Sopenharmony_ci得到符合渲染需求的EGLConfig之后,可以使用eglCreateWindowSurface创建窗口表面。 355e41f4b71Sopenharmony_ci```cpp 356e41f4b71Sopenharmony_ciEGLSurface eglCreateWindowSurface(EGLDisplay dpy, // EGLDisplay对象,表示与窗口表面关联的显示连接。 357e41f4b71Sopenharmony_ci EGLConfig config, // EGLConfig对象,表示要创建窗口表面的EGL配置。 358e41f4b71Sopenharmony_ci EGLNativeWindowType win, // EGLNativeWindowType类型的参数,表示原生窗口的句柄或标识符,用于与EGL表面关联。 359e41f4b71Sopenharmony_ci const EGLint *attrib_list); // 指向EGL属性列表的指针,用于指定窗口表面的属性。是一个以EGL_NONE结尾的整数数组。 360e41f4b71Sopenharmony_ci``` 361e41f4b71Sopenharmony_cieglCreateWindowSurface接受的属性attrib_list的值如下所示: 362e41f4b71Sopenharmony_ci 363e41f4b71Sopenharmony_ci```cpp 364e41f4b71Sopenharmony_ciEGL_RENDER_BUFFER EGL_SINGLE_BUFFER或EGL_BACK_BUFFER 365e41f4b71Sopenharmony_ciEGL_SINGLE_BUFFER // 表示渲染表面将只有一个渲染缓冲区,在绘制完成后,渲染缓冲区中的内容将直接显示到屏幕上,不会进行双缓冲,可能会出现闪烁或撕裂的现象。 366e41f4b71Sopenharmony_ciEGL_BACK_BUFFER // 表示渲染表面将具有双缓冲区,即前缓冲区和后缓冲区。在绘制完成后,渲染缓冲区中的内容首先会绘制到后缓冲区,然后通过交换缓冲区的操作将后缓冲区的内容显示到屏幕上,这样可以避免闪烁和撕裂现象。 367e41f4b71Sopenharmony_ci// 默认情况下是EGL_BACK_BUFFER,当设置为null,则为默认属性。 368e41f4b71Sopenharmony_ci``` 369e41f4b71Sopenharmony_cieglCreateWindowSurface创建窗口表面失败的可能如下: 370e41f4b71Sopenharmony_ci 371e41f4b71Sopenharmony_ci- EGL_BAD_MATCH:表示原生窗口属性与提供的 EGLConfig 不匹配。这可能是因为EGLConfig不支持渲染到窗口(即EGL_SURFACE_TYPE 属性没有设置为 EGL_WINDOW_BIT)。 372e41f4b71Sopenharmony_ci 373e41f4b71Sopenharmony_ci- EGL_BAD_CONFIG:如果提供的EGLConfig没有得到系统的支持,则会发生这种错误。 374e41f4b71Sopenharmony_ci 375e41f4b71Sopenharmony_ci- EGL_BAD_NATIVE_WINDOW:如果提供的原生窗口句柄无效,则会发生这种错误。 376e41f4b71Sopenharmony_ci 377e41f4b71Sopenharmony_ci- EGL_BAD_ALLOC:如果eglCreateWindowSurface无法为新的EGL窗口分配资源,或者已经有与提供的原生窗口关联的EGLConfig,则会发生这种错误。 378e41f4b71Sopenharmony_ci 379e41f4b71Sopenharmony_ci 380e41f4b71Sopenharmony_ci 381e41f4b71Sopenharmony_ci```cpp 382e41f4b71Sopenharmony_ciEGLint attribList[] = { EGL_RENDER_BUFFER, EGL_BACK_BUFFER, EGL_NONE }; 383e41f4b71Sopenharmony_ciEGLSurface surface = eglCreateWindowSurface(display, config, nativeWindow, attribList); 384e41f4b71Sopenharmony_ciif (surface == EGL_NO_SURFACE) { 385e41f4b71Sopenharmony_ci switch (eglGetError()) { 386e41f4b71Sopenharmony_ci case EGL_BAD_MATCH: 387e41f4b71Sopenharmony_ci // 检查窗口和 EGLConfig 属性以确定兼容性,或者验证 EGLConfig 是否支持渲染到窗口 388e41f4b71Sopenharmony_ci break; 389e41f4b71Sopenharmony_ci case EGL_BAD_CONFIG: 390e41f4b71Sopenharmony_ci // 验证提供的 EGLConfig 是否有效 391e41f4b71Sopenharmony_ci break; 392e41f4b71Sopenharmony_ci case EGL_BAD_NATIVE_WINDOW: 393e41f4b71Sopenharmony_ci // 验证提供的 EGLNativeWindow 是否有效 394e41f4b71Sopenharmony_ci break; 395e41f4b71Sopenharmony_ci case EGL_BAD_ALLOC: 396e41f4b71Sopenharmony_ci // 资源不足;处理并恢复 397e41f4b71Sopenharmony_ci break; 398e41f4b71Sopenharmony_ci default: 399e41f4b71Sopenharmony_ci // 处理任何其他错误 400e41f4b71Sopenharmony_ci break; 401e41f4b71Sopenharmony_ci } 402e41f4b71Sopenharmony_ci} 403e41f4b71Sopenharmony_ci``` 404e41f4b71Sopenharmony_ci在使用XComponent获取nativeWindow的过程中,通常涉及以下步骤: 405e41f4b71Sopenharmony_ci1. 首先需要在ArkTS 中定义XComponent并设置 XComponentController。XComponent组件用于在UI中嵌入原生的渲染内容如OpenGL或Vulkan。 406e41f4b71Sopenharmony_ci```typescript 407e41f4b71Sopenharmony_ciColumn() { 408e41f4b71Sopenharmony_ci XComponent({ 409e41f4b71Sopenharmony_ci id: 'myXComponent', 410e41f4b71Sopenharmony_ci type: XComponentType.SURFACE, 411e41f4b71Sopenharmony_ci controller: this.xComponentController 412e41f4b71Sopenharmony_ci }) 413e41f4b71Sopenharmony_ci} 414e41f4b71Sopenharmony_ci``` 415e41f4b71Sopenharmony_ci2. 创建 XComponentController子类,实现回调方法: 416e41f4b71Sopenharmony_ci```typescript 417e41f4b71Sopenharmony_ciclass MyXComponentController extends XComponentController { 418e41f4b71Sopenharmony_ci onSurfaceCreated(surfaceId: string): void { 419e41f4b71Sopenharmony_ci console.log(`onSurfaceCreated surfaceId: ${surfaceId}`); 420e41f4b71Sopenharmony_ci nativeRender.SetSurfaceId(BigInt(surfaceId)); 421e41f4b71Sopenharmony_ci // 之后会使用 surfaceId 关联 native window 422e41f4b71Sopenharmony_ci } 423e41f4b71Sopenharmony_ci 424e41f4b71Sopenharmony_ci onSurfaceChanged(surfaceId: string, rect: SurfaceRect): void { 425e41f4b71Sopenharmony_ci console.log(`onSurfaceChanged surfaceId: ${surfaceId}`); 426e41f4b71Sopenharmony_ci } 427e41f4b71Sopenharmony_ci 428e41f4b71Sopenharmony_ci onSurfaceDestroyed(surfaceId: string): void { 429e41f4b71Sopenharmony_ci console.log(`onSurfaceDestroyed surfaceId: ${surfaceId}`); 430e41f4b71Sopenharmony_ci } 431e41f4b71Sopenharmony_ci} 432e41f4b71Sopenharmony_ci``` 433e41f4b71Sopenharmony_ci3. 使用surfaceId获取NativeWindow: 434e41f4b71Sopenharmony_cisurfaceId是在XComponent创建过程中生成的。在onSurfaceCreated 回调中,可以使用OH_NativeWindow_CreateNativeWindowFromSurfaceId函数通过surfaceId获取nativeWindow。 435e41f4b71Sopenharmony_ci```cpp 436e41f4b71Sopenharmony_cinapi_value PluginManager::SetSurfaceId(napi_env env, napi_callback_info info) 437e41f4b71Sopenharmony_ci{ 438e41f4b71Sopenharmony_ci int64_t surfaceId = ParseId(env, info); 439e41f4b71Sopenharmony_ci OHNativeWindow *nativeWindow; 440e41f4b71Sopenharmony_ci PluginRender *pluginRender; 441e41f4b71Sopenharmony_ci if (windowMap_.find(surfaceId) == windowMap_.end()) { 442e41f4b71Sopenharmony_ci OH_NativeWindow_CreateNativeWindowFromSurfaceId(surfaceId, &nativeWindow); 443e41f4b71Sopenharmony_ci windowMap_[surfaceId] = nativeWindow; 444e41f4b71Sopenharmony_ci } 445e41f4b71Sopenharmony_ci if (pluginRenderMap_.find(surfaceId) == pluginRenderMap_.end()) { 446e41f4b71Sopenharmony_ci pluginRender = new PluginRender(surfaceId); 447e41f4b71Sopenharmony_ci pluginRenderMap_[surfaceId] = pluginRender; 448e41f4b71Sopenharmony_ci } 449e41f4b71Sopenharmony_ci pluginRender->InitNativeWindow(nativeWindow); 450e41f4b71Sopenharmony_ci return nullptr; 451e41f4b71Sopenharmony_ci} 452e41f4b71Sopenharmony_ci``` 453e41f4b71Sopenharmony_ci有关ArkTS XComponent 组件的使用,请参考:[ArkTS XComponent组件使用示例](https://gitee.com/openharmony/applications_app_samples/blob/master/code/BasicFeature/Native/XComponent/README_zh.md#)。 454e41f4b71Sopenharmony_ci### 使用eglCreateContext创建渲染上下文 455e41f4b71Sopenharmony_ci 456e41f4b71Sopenharmony_cieglCreateContext函数用于创建一个新的EGL上下文,并将其与特定的显示设备(display)和配置(config)关联起来。允许指定共享上下文(shareContext),以便与已经存在的OpenGL上下文共享状态信息。该函数的参数说明如下: 457e41f4b71Sopenharmony_ci 458e41f4b71Sopenharmony_ci```cpp 459e41f4b71Sopenharmony_ciEGLContext eglCreateContext(EGLDisplay display, // EGLDisplay类型,表示要创建上下文的EGL显示连接。 460e41f4b71Sopenharmony_ci EGLConfig config, // EGLConfig类型,表示与上下文关联的EGL配置。 461e41f4b71Sopenharmony_ci EGLContext shareContext, // EGLContext类型,表示要与新创建的上下文共享状态信息的现有上下文。如果不想共享状态信息,可以传递EGL_NO_CONTEXT。 462e41f4b71Sopenharmony_ci const EGLint *attribList); // 指向属性列表的指针,用于指定上下文的属性。属性列表是以EGL_NONE结尾的一系列属性值对。 463e41f4b71Sopenharmony_ci``` 464e41f4b71Sopenharmony_cieglCreateContext 的attribList属性列表如下: 465e41f4b71Sopenharmony_ci```cpp 466e41f4b71Sopenharmony_ciEGLint contextAttribs[] = { 467e41f4b71Sopenharmony_ci EGL_CONTEXT_CLIENT_VERSION, 3, //指定使用的openglES版本3相关的上下文类型 468e41f4b71Sopenharmony_ci}; 469e41f4b71Sopenharmony_ci``` 470e41f4b71Sopenharmony_ci 471e41f4b71Sopenharmony_cieglCreateContext 创建渲染上下文失败的可能为:EGL_BAD_CONFIG,即提供的EGLconfig无效。 472e41f4b71Sopenharmony_ci 473e41f4b71Sopenharmony_ci### 使用eglMakeCurrent将EGL上下文与绘图表面进行关联 474e41f4b71Sopenharmony_ci 475e41f4b71Sopenharmony_ci```cpp 476e41f4b71Sopenharmony_ciEGLBoolean eglMakeCurrent(EGLDisplay display, // EGL显示连接的句柄,用于标识渲染设备。 477e41f4b71Sopenharmony_ci EGLSurface draw, // EGL绘图表面的句柄,指定要渲染到的目标表面。 478e41f4b71Sopenharmony_ci EGLSurface read, // EGL读取表面的句柄,用于像素读取等操作。通常情况下,可以将其设为与 draw 相同的值。 479e41f4b71Sopenharmony_ci EGLContext context);// 要与指定表面关联的 EGL 上下文的句柄。 480e41f4b71Sopenharmony_ci``` 481e41f4b71Sopenharmony_ci 482e41f4b71Sopenharmony_ci### 使用glViewport设置视口大小 483e41f4b71Sopenharmony_ci 484e41f4b71Sopenharmony_ci```cpp 485e41f4b71Sopenharmony_civoid glViewport(GLint x, GLint y, GLsizei width, GLsizei height) 486e41f4b71Sopenharmony_ci``` 487e41f4b71Sopenharmony_ci 488e41f4b71Sopenharmony_ciglViewport函数用于设置视口,指定OpenGL ES渲染区域在窗口的位置和大小。其中x、y指定视口的左下角在窗口中的坐标,width、height参数则指定视口的宽度和高度。 489e41f4b71Sopenharmony_ci 490e41f4b71Sopenharmony_ci### 使用glClearColor设置清除颜色缓冲区时使用的颜色 491e41f4b71Sopenharmony_ci 492e41f4b71Sopenharmony_ci```cpp 493e41f4b71Sopenharmony_civoid glClearColor(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); 494e41f4b71Sopenharmony_ci``` 495e41f4b71Sopenharmony_ci`glClearColor(0.2f, 0.3f, 0.3f, 1.0f)`此时设置清除颜色缓冲区时使用的颜色为 (0.2, 0.3, 0.3),即红色分量为0.2、绿色分量为0.3、蓝色分量为0.3、透明度为1.0(不透明)。 496e41f4b71Sopenharmony_ci 497e41f4b71Sopenharmony_ci### 使用glClear执行清除操作 498e41f4b71Sopenharmony_ci 499e41f4b71Sopenharmony_ci```cpp 500e41f4b71Sopenharmony_civoid glClear(GLbitfield mask); 501e41f4b71Sopenharmony_ci``` 502e41f4b71Sopenharmony_ciglClear函数用于清除指定的缓冲区。参数mask指定需要清除的缓冲区,可以是以下值的组合: 503e41f4b71Sopenharmony_ci- GL_COLOR_BUFFER_BIT:清除颜色缓冲区。 504e41f4b71Sopenharmony_ci- GL_DEPTH_BUFFER_BIT:清除深度缓冲区。 505e41f4b71Sopenharmony_ci- GL_STENCIL_BUFFER_BIT:清除模板缓冲区。 506e41f4b71Sopenharmony_ci 507e41f4b71Sopenharmony_ci可调用glClear(GL_COLOR_BUFFER_BIT)清除颜色缓冲区,并用之前glClearColor设置的颜色填充整个缓冲区。清除颜色缓冲区是在开始绘制新帧之前的一个常见操作,这可以确保屏幕上的每个像素都被初始化为指定的颜色值,以便绘制新的图像。也是绘制新帧的准备工作,类似于在画布上涂上底色,以便开始新的绘画。 508e41f4b71Sopenharmony_ci 509e41f4b71Sopenharmony_ci### 定义顶点数据 510e41f4b71Sopenharmony_ci```cpp 511e41f4b71Sopenharmony_ci // 定义顶点数据 512e41f4b71Sopenharmony_ci GLfloat vertices[] = { 513e41f4b71Sopenharmony_ci -0.5f, -0.5f, 0.0f, // 左下角 514e41f4b71Sopenharmony_ci 0.5f, -0.5f, 0.0f, // 右下角 515e41f4b71Sopenharmony_ci 0.0f, 0.5f, 0.0f // 顶部 516e41f4b71Sopenharmony_ci }; 517e41f4b71Sopenharmony_ci``` 518e41f4b71Sopenharmony_ci 519e41f4b71Sopenharmony_ci在OpenGL中,通常会使用标准化设备坐标(Normalized Device Coordinates, NDC)来表示顶点的位置。NDC是一个以屏幕为单位的坐标空间,在这个空间中,左下角是(-1, -1),右上角是(1, 1)。这种坐标范围使得顶点的位置不依赖于屏幕的大小和比例。 520e41f4b71Sopenharmony_ci### 管理顶点数据 521e41f4b71Sopenharmony_ci 522e41f4b71Sopenharmony_ci将顶点数据保存在GPU,减少CPU与GPU数据传输次数。 523e41f4b71Sopenharmony_ci 524e41f4b71Sopenharmony_ci```cpp 525e41f4b71Sopenharmony_ciGLuint VAO[1]; 526e41f4b71Sopenharmony_ciGLuint VBO; 527e41f4b71Sopenharmony_ciglGenVertexArrays(1, VAO); // 要生成1个顶点数组对象(VAO) VAO 用于存储生成的顶点数组对象名称的数组 528e41f4b71Sopenharmony_ciglBindVertexArray(VAO[0]); // 用于绑定顶点数组对象(VAO)到当前OpenGL上下文中。 529e41f4b71Sopenharmony_ciglGenBuffers(1, &VBO); // 用于生成顶点缓冲对象, 1:要生成的顶点缓冲对象的数量这里设置1个 ,&VBO 指向存储生成的顶点缓冲对象名称的数组的指针。 530e41f4b71Sopenharmony_ciglBindBuffer(GL_ARRAY_BUFFER, VBO); // void glBindBuffer(GLenum target, GLuint buffer);其中target为指定要绑定的缓冲目标,可为以下值之一: 531e41f4b71Sopenharmony_ci // GL_ARRAY_BUFFER:用于存储顶点属性数据; 532e41f4b71Sopenharmony_ci // GL_ELEMENT_ARRAY_BUFFER:用于存储索引数据等其他。 533e41f4b71Sopenharmony_ci // buffer为要绑定的顶点缓冲对象的名称。 534e41f4b71Sopenharmony_ciglBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); 535e41f4b71Sopenharmony_ci``` 536e41f4b71Sopenharmony_ci```cpp 537e41f4b71Sopenharmony_civoid glBufferData(GLenum target, // target:指定缓冲对象的类型,可为以下值之一: 538e41f4b71Sopenharmony_ci // GL_ARRAY_BUFFER:用于存储顶点属性数据; 539e41f4b71Sopenharmony_ci // GL_ELEMENT_ARRAY_BUFFER:用于存储索引数据。 540e41f4b71Sopenharmony_ci GLsizeiptr size, // 指定要分配的缓冲区的大小(以字节为单位)。 541e41f4b71Sopenharmony_ci const GLvoid* data, // 指定要复制到缓冲区的初始数据。 542e41f4b71Sopenharmony_ci GLenum usage); // 指定缓冲区的预期使用方式,可为以下值之一: 543e41f4b71Sopenharmony_ci // GL_STATIC_DRAW:数据不会或几乎不会被修改,并且被绘制命令多次使用; 544e41f4b71Sopenharmony_ci // GL_DYNAMIC_DRAW:数据会被频繁修改,并且被绘制命令多次使用; 545e41f4b71Sopenharmony_ci // GL_STREAM_DRAW:数据会被修改,并且被绘制命令少量使用。 546e41f4b71Sopenharmony_ci``` 547e41f4b71Sopenharmony_ci 548e41f4b71Sopenharmony_ci一旦调用glBufferData函数,数据就被复制到了OpenGL的缓冲对象中,并存储在GPU的显存中。这意味着数据可以在GPU上被高效地访问和处理,而无需频繁地从CPU内存传输数据,从而提高了渲染性能。 549e41f4b71Sopenharmony_ci 550e41f4b71Sopenharmony_ci```cpp 551e41f4b71Sopenharmony_ci const char* vertexShaderSource = R"( 552e41f4b71Sopenharmony_ci #version 320 es // 这是指定使用OpenGL ES 3.2版本的着色器语言 553e41f4b71Sopenharmony_ci precision mediump float; // 指定浮点数的精度为中等精度 554e41f4b71Sopenharmony_ci layout (location = 0) in vec3 aPos; // 顶点属性变量,名称为aPos,类型为vec3,并且指定它在顶点着色器中的位置索引为0。这个变量aPos接收刚才设置的来自顶点缓冲对象中的顶点数据。每次顶点着色器被调用时,aPos就会被设置为当前处理的顶点的位置属性值。(数据从刚才顶点缓冲对象中获取,且已经存到GPU) 555e41f4b71Sopenharmony_ci void main() { 556e41f4b71Sopenharmony_ci // gl_Position是opengles内置变量,来指定每个顶点的最终位置,这个位置是经过透视投影变换后的裁剪空间坐标。 557e41f4b71Sopenharmony_ci // 在顶点着色器中对gl_Position赋值后,图形渲染管线会将其进行进一步处理,然后将顶点投影到屏幕上的二维坐标。 558e41f4b71Sopenharmony_ci // w为非零值时,顶点坐标会进行透视除法操作,即将 (x, y, z, w) 中的 (x/w, y/w, z/w) 作为最终的裁剪空间坐标。 559e41f4b71Sopenharmony_ci // 因此,当w的值为1.0时,透视除法不会对坐标进行任何变换,坐标值保持不变。 560e41f4b71Sopenharmony_ci gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0); 561e41f4b71Sopenharmony_ci } 562e41f4b71Sopenharmony_ci )"; 563e41f4b71Sopenharmony_ci``` 564e41f4b71Sopenharmony_ci- 片段(Fragment):是光栅化阶段生成的元素,它代表一个潜在的屏幕像素,包括所有与这个像素相关的信息,比如颜色、深度、模板值等。每个片段会经过片段着色器处理,并最终决定是否写入到帧缓冲区中。 565e41f4b71Sopenharmony_ci- 片段着色器是一个运行在每个片段上的程序,这里用于计算片段的最终颜色值。片段着色器可以访问插值后的顶点数据,并执行复杂的光照计算、纹理采样等操作。 566e41f4b71Sopenharmony_ci 567e41f4b71Sopenharmony_ci```cpp 568e41f4b71Sopenharmony_ci 569e41f4b71Sopenharmony_ciconst char* fragmentShaderSource = R"( 570e41f4b71Sopenharmony_ci #version 320 es // 这是指定使用OpenGL ES 3.2版本的着色器语言 571e41f4b71Sopenharmony_ci precision mediump float; // 设置浮点数的精度为中等精度 572e41f4b71Sopenharmony_ci out vec4 FragColor; // 输出片段颜色 573e41f4b71Sopenharmony_ci 574e41f4b71Sopenharmony_ci void main() { 575e41f4b71Sopenharmony_ci // 这里将每个片段的颜色设置为vec4(1.0f, 0.5f, 0.2f, 1.0f) 576e41f4b71Sopenharmony_ci // 其中分别代表红色、绿色、蓝色和alpha值。 577e41f4b71Sopenharmony_ci // 这意味着输出的颜色为浅橙色,完全不透明。 578e41f4b71Sopenharmony_ci // 这里的颜色不是从顶点着色器经过光栅化线性插值而获得的,而是直接赋值。 579e41f4b71Sopenharmony_ci FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f); 580e41f4b71Sopenharmony_ci } 581e41f4b71Sopenharmony_ci)"; 582e41f4b71Sopenharmony_ci 583e41f4b71Sopenharmony_ci``` 584e41f4b71Sopenharmony_ci在 OpenGL ES渲染管线中,以下步骤描述了从顶点数据到最终像素输出的整个过程: 585e41f4b71Sopenharmony_ci 586e41f4b71Sopenharmony_ci1. 顶点着色器处理。 587e41f4b71Sopenharmony_ci 588e41f4b71Sopenharmony_ci 首先,将缓冲区中的顶点数据传入顶点着色器程序。在顶点着色器中进行以下操作: 589e41f4b71Sopenharmony_ci 590e41f4b71Sopenharmony_ci - 矩阵转换:使用用模型视图矩阵(MV矩阵)和投影矩阵(透视矩阵)对顶点位置进行变换。 591e41f4b71Sopenharmony_ci 592e41f4b71Sopenharmony_ci - 照明计算:根据光照公式计算顶点的颜色或其他属性。 593e41f4b71Sopenharmony_ci 594e41f4b71Sopenharmony_ci2. 图元装配。 595e41f4b71Sopenharmony_ci 596e41f4b71Sopenharmony_ci 顶点着色器处理后的顶点数据被送入图元装配阶段:在这个阶段,将顶点数据组装成几何图元,例如点、线段或三角形。 597e41f4b71Sopenharmony_ci 598e41f4b71Sopenharmony_ci3. 光栅化。 599e41f4b71Sopenharmony_ci 600e41f4b71Sopenharmony_ci 接下来,进行光栅化,将几何图元(例如三角形)转换为屏幕上的像素集合。这个过程包括插值:如果顶点设置了颜色或其他属性,光栅化阶段会对这些属性进行线性插值,生成片段(像素)数据。 601e41f4b71Sopenharmony_ci 602e41f4b71Sopenharmony_ci4. 片段着色器处理。 603e41f4b71Sopenharmony_ci 604e41f4b71Sopenharmony_ci 光栅化输出的片段数据作为片段着色器的输入变量。在片段着色器中进行以下操作: 605e41f4b71Sopenharmony_ci 606e41f4b71Sopenharmony_ci - 光照计算:计算片段的光照效果。 607e41f4b71Sopenharmony_ci 608e41f4b71Sopenharmony_ci - 纹理采样:从纹理中获取颜色数据。 609e41f4b71Sopenharmony_ci 610e41f4b71Sopenharmony_ci - 颜色混合:结合光照和纹理数据生成新的颜色、深度和屏幕坐标位置等。 611e41f4b71Sopenharmony_ci 612e41f4b71Sopenharmony_ci5. 逐片段操作。 613e41f4b71Sopenharmony_ci 614e41f4b71Sopenharmony_ci 片段着色器的输出被送入逐片段操作阶段,包括: 615e41f4b71Sopenharmony_ci 616e41f4b71Sopenharmony_ci - 像素归属测试:确定片段是否属于当前绘制的像素区域。 617e41f4b71Sopenharmony_ci 618e41f4b71Sopenharmony_ci - 剪裁测试:确定片段是否在可视区域内。 619e41f4b71Sopenharmony_ci 620e41f4b71Sopenharmony_ci - 模板测试:使用模板缓冲区进行测试。 621e41f4b71Sopenharmony_ci 622e41f4b71Sopenharmony_ci - 深度测试:比较片段的深度值,以确定其是否可见。 623e41f4b71Sopenharmony_ci 624e41f4b71Sopenharmony_ci - 混合:将新计算的颜色与帧缓冲区中已有的颜色进行混合。 625e41f4b71Sopenharmony_ci 626e41f4b71Sopenharmony_ci - 抖动:减少颜色量化误差,在原始图像上添加小的、随机或有序的噪声,使得颜色的量化误差在空间上被分散,而不是集中在某些特定的区域。 627e41f4b71Sopenharmony_ci 628e41f4b71Sopenharmony_ci6. 写入帧缓冲区。 629e41f4b71Sopenharmony_ci 630e41f4b71Sopenharmony_ci 经过上述所有测试和处理后,最终的片段数据被写入帧缓冲区,形成最终显示在屏幕上的图像。 631e41f4b71Sopenharmony_ci 632e41f4b71Sopenharmony_ci### 创建并使用着色器程序 633e41f4b71Sopenharmony_ci 634e41f4b71Sopenharmony_ci```cpp 635e41f4b71Sopenharmony_ciGLuint vertexShader, fragmentShader, shaderProgram; 636e41f4b71Sopenharmony_ci// 创建顶点着色器 637e41f4b71Sopenharmony_civertexShader = glCreateShader(GL_VERTEX_SHADER); 638e41f4b71Sopenharmony_ciglShaderSource(vertexShader, 1, &vertexShaderSource, nullptr); 639e41f4b71Sopenharmony_ciglCompileShader(vertexShader); 640e41f4b71Sopenharmony_ci 641e41f4b71Sopenharmony_ci// 创建片段着色器 642e41f4b71Sopenharmony_cifragmentShader = glCreateShader(GL_FRAGMENT_SHADER); 643e41f4b71Sopenharmony_ciglShaderSource(fragmentShader, 1, &fragmentShaderSource, nullptr); 644e41f4b71Sopenharmony_ciglCompileShader(fragmentShader); 645e41f4b71Sopenharmony_ci 646e41f4b71Sopenharmony_ci// 创建着色器程序 647e41f4b71Sopenharmony_cishaderProgram = glCreateProgram(); 648e41f4b71Sopenharmony_ciglAttachShader(shaderProgram, vertexShader); 649e41f4b71Sopenharmony_ciglAttachShader(shaderProgram, fragmentShader); 650e41f4b71Sopenharmony_ciglLinkProgram(shaderProgram); 651e41f4b71Sopenharmony_ci 652e41f4b71Sopenharmony_ci// 使用着色器程序 653e41f4b71Sopenharmony_ciglUseProgram(shaderProgram); 654e41f4b71Sopenharmony_ci``` 655e41f4b71Sopenharmony_ci 656e41f4b71Sopenharmony_ci```cpp 657e41f4b71Sopenharmony_ciGLuint glCreateShader(GLenum shaderType); 658e41f4b71Sopenharmony_ci``` 659e41f4b71Sopenharmony_ciglCreateShader用于创建一个指定类型(顶点着色器、片段着色器等)的着色器对象,并返回该对象的句柄。其中shaderType参数指定要创建的着色器类型,可以是GL_VERTEX_SHADER(顶点着色器)或 GL_FRAGMENT_SHADER(片段着色器)等。 660e41f4b71Sopenharmony_ci 661e41f4b71Sopenharmony_ci```cpp 662e41f4b71Sopenharmony_civoid glShaderSource(GLuint shader, GLsizei count, const GLchar \**string, const GLint *length); 663e41f4b71Sopenharmony_ci``` 664e41f4b71Sopenharmony_ci 665e41f4b71Sopenharmony_ciglShaderSource函数用于设置着色器对象的源代码。其中各参数含义如下: 666e41f4b71Sopenharmony_ci 667e41f4b71Sopenharmony_ci- shader:要设置源代码的着色器对象的标识符。 668e41f4b71Sopenharmony_ci- count:源代码字符串的数量。 669e41f4b71Sopenharmony_ci- string:指向源代码字符串的指针数组。 670e41f4b71Sopenharmony_ci- length:指向包含每个源代码字符串长度的整数数组,可以为nullptr,表示每个字符串都以null结尾。 671e41f4b71Sopenharmony_ci 672e41f4b71Sopenharmony_ci```cpp 673e41f4b71Sopenharmony_civoid glCompileShader(GLuint shader); 674e41f4b71Sopenharmony_ci``` 675e41f4b71Sopenharmony_ci 676e41f4b71Sopenharmony_ciglCompileShader函数用于编译指定的着色器对象,其中shader参数是要编译的着色器对象的标识符。 677e41f4b71Sopenharmony_ci 678e41f4b71Sopenharmony_ci```cpp 679e41f4b71Sopenharmony_ciGLuint glCreateProgram(void); 680e41f4b71Sopenharmony_ci``` 681e41f4b71Sopenharmony_ci 682e41f4b71Sopenharmony_ciglCreateProgram函数用于创建一个新的着色器程序对象,该函数返回一个新创建的着色器程序对象的标识符。 683e41f4b71Sopenharmony_ci 684e41f4b71Sopenharmony_ci```cpp 685e41f4b71Sopenharmony_civoid glAttachShader(GLuint program, GLuint shader); 686e41f4b71Sopenharmony_ci``` 687e41f4b71Sopenharmony_ci 688e41f4b71Sopenharmony_ciglAttachShader函数用于将一个着色器对象附加到一个着色器程序对象上,参数program是目标着色器程序对象的标识符,参数shader是要附加的着色器对象的标识符。 689e41f4b71Sopenharmony_ci 690e41f4b71Sopenharmony_ci```cpp 691e41f4b71Sopenharmony_civoid glLinkProgram(GLuint program); 692e41f4b71Sopenharmony_ci``` 693e41f4b71Sopenharmony_ci 694e41f4b71Sopenharmony_ciglLinkProgram函数用于链接一个着色器程序对象,将附加到该程序对象的着色器链接成一个可执行的渲染管线。 695e41f4b71Sopenharmony_ci 696e41f4b71Sopenharmony_ci参数program是要链接的着色器程序对象的标识符。链接着色器程序时,OpenGL将会执行以下操作:将各个着色器对象中的代码合并成一个可执行的渲染管线。执行连接器优化,以优化渲染管线的性能。并将Uniform变量和Uniform块的信息进行绑定。 697e41f4b71Sopenharmony_ci 698e41f4b71Sopenharmony_ci```cpp 699e41f4b71Sopenharmony_civoid glUseProgram(GLuint program); 700e41f4b71Sopenharmony_ci``` 701e41f4b71Sopenharmony_ciglUseProgram函数用于激活指定的着色器程序对象。在调用glUseProgram 之后,所有的渲染调用将会使用该着色器程序进行处理。 702e41f4b71Sopenharmony_ci 703e41f4b71Sopenharmony_ci在使用glCompileShader时可以使用以下代码检查是否正常。 704e41f4b71Sopenharmony_ci 705e41f4b71Sopenharmony_ci```cpp 706e41f4b71Sopenharmony_ci// 编译着色器 707e41f4b71Sopenharmony_ciglCompileShader(shader); 708e41f4b71Sopenharmony_ci 709e41f4b71Sopenharmony_ci// 检查编译状态 710e41f4b71Sopenharmony_ciglGetShaderiv(shader, GL_COMPILE_STATUS, &compiled); 711e41f4b71Sopenharmony_ci 712e41f4b71Sopenharmony_ciif (!compiled) 713e41f4b71Sopenharmony_ci{ 714e41f4b71Sopenharmony_ci GLint infoLen = 0; 715e41f4b71Sopenharmony_ci 716e41f4b71Sopenharmony_ci // 获取着色器信息日志的长度 717e41f4b71Sopenharmony_ci glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen); 718e41f4b71Sopenharmony_ci 719e41f4b71Sopenharmony_ci if ( infoLen > 1 ) 720e41f4b71Sopenharmony_ci { 721e41f4b71Sopenharmony_ci // 分配存储信息日志的内存 722e41f4b71Sopenharmony_ci char *infoLog = malloc(sizeof(char) * infoLen); 723e41f4b71Sopenharmony_ci 724e41f4b71Sopenharmony_ci // 获取并打印着色器信息日志 725e41f4b71Sopenharmony_ci glGetShaderInfoLog(shader, infoLen, NULL, infoLog); 726e41f4b71Sopenharmony_ci esLogMessage("Error compiling shader:\n%s\n", infoLog); 727e41f4b71Sopenharmony_ci 728e41f4b71Sopenharmony_ci // 释放分配的内存 729e41f4b71Sopenharmony_ci free(infoLog); 730e41f4b71Sopenharmony_ci } 731e41f4b71Sopenharmony_ci 732e41f4b71Sopenharmony_ci // 删除编译失败的着色器 733e41f4b71Sopenharmony_ci glDeleteShader(shader); 734e41f4b71Sopenharmony_ci return 0; 735e41f4b71Sopenharmony_ci} 736e41f4b71Sopenharmony_ci``` 737e41f4b71Sopenharmony_ci 738e41f4b71Sopenharmony_ci在使用glLinkProgram可使用如下代码检查是否正常。 739e41f4b71Sopenharmony_ci 740e41f4b71Sopenharmony_ci```cpp 741e41f4b71Sopenharmony_ci// 链接程序对象 742e41f4b71Sopenharmony_ciglLinkProgram(programObject); 743e41f4b71Sopenharmony_ci 744e41f4b71Sopenharmony_ci// 检查链接状态 745e41f4b71Sopenharmony_ciglGetProgramiv(programObject, GL_LINK_STATUS, &linked); 746e41f4b71Sopenharmony_ci 747e41f4b71Sopenharmony_ciif (!linked) 748e41f4b71Sopenharmony_ci{ 749e41f4b71Sopenharmony_ci GLint infoLen = 0; 750e41f4b71Sopenharmony_ci 751e41f4b71Sopenharmony_ci // 获取程序对象信息日志的长度 752e41f4b71Sopenharmony_ci glGetProgramiv(programObject, GL_INFO_LOG_LENGTH, &infoLen); 753e41f4b71Sopenharmony_ci 754e41f4b71Sopenharmony_ci if (infoLen > 1) 755e41f4b71Sopenharmony_ci { 756e41f4b71Sopenharmony_ci // 分配存储信息日志的内存 757e41f4b71Sopenharmony_ci char *infoLog = malloc(sizeof(char) * infoLen); 758e41f4b71Sopenharmony_ci 759e41f4b71Sopenharmony_ci // 获取并打印程序对象的信息日志 760e41f4b71Sopenharmony_ci glGetProgramInfoLog(programObject, infoLen, NULL, infoLog); 761e41f4b71Sopenharmony_ci esLogMessage("Error linking program:\n%s\n", infoLog); 762e41f4b71Sopenharmony_ci 763e41f4b71Sopenharmony_ci // 释放分配的内存 764e41f4b71Sopenharmony_ci free(infoLog); 765e41f4b71Sopenharmony_ci } 766e41f4b71Sopenharmony_ci 767e41f4b71Sopenharmony_ci // 删除链接失败的程序对象 768e41f4b71Sopenharmony_ci glDeleteProgram(programObject); 769e41f4b71Sopenharmony_ci return FALSE; 770e41f4b71Sopenharmony_ci} 771e41f4b71Sopenharmony_ci``` 772e41f4b71Sopenharmony_ci 773e41f4b71Sopenharmony_ci### 确定顶点属性数组的配置 774e41f4b71Sopenharmony_ci 775e41f4b71Sopenharmony_ci包括顶点属性在缓冲区中的布局和格式。 776e41f4b71Sopenharmony_ci 777e41f4b71Sopenharmony_ci```cpp 778e41f4b71Sopenharmony_civoid glVertexAttribPointer(GLuint index, // 指定要修改的顶点数组的起始索引,索引它与顶点着色器中的属性变量绑定。(layout (location = 0) in vec3 aPos;) 779e41f4b71Sopenharmony_ci GLint size, // 指定每个顶点属性的分量个数 780e41f4b71Sopenharmony_ci GLenum type, // 指定每个顶点属性分量的类型 781e41f4b71Sopenharmony_ci GLboolean normalized, // 指定在访问顶点数据时是否将其映射到[0, 1]或[-1, 1]范围内 782e41f4b71Sopenharmony_ci GLsizei stride, // 指定顶点属性之间的偏移量,如果是精密性排列可以设置为0 783e41f4b71Sopenharmony_ci const void *offset);//属性在缓冲区中的偏移量,允许在缓冲区中指定一个位置开始读取数据。 784e41f4b71Sopenharmony_ci``` 785e41f4b71Sopenharmony_ci 786e41f4b71Sopenharmony_ci```cpp 787e41f4b71Sopenharmony_civoid glEnableVertexAttribArray(GLuint index); 788e41f4b71Sopenharmony_ci``` 789e41f4b71Sopenharmony_ci 790e41f4b71Sopenharmony_ciglEnableVertexAttribArray 函数用于启用指定索引的顶点属性数组。例如,调用glEnableVertexAttribArray(0)可以启用位置索引为 0 的顶点属性数组,这与顶点着色器程序中的 layout (location = 0) in vec3 aPos 相关联。 791e41f4b71Sopenharmony_ci 792e41f4b71Sopenharmony_ci在示例代码中,glVertexAttribPointer 的第一个参数index对应顶点着色器中aPos的location,即位置 0。其他参数设置了顶点属性的格式,告诉 OpenGL 该属性包含 3 个组件(x、y、z),数据类型为 GL_FLOAT,并且每个顶点数据的第一个属性从偏移量 0 开始。 793e41f4b71Sopenharmony_ci 794e41f4b71Sopenharmony_ciglBindBuffer函数绑定当前的顶点缓冲对象(VBO),glBufferData将顶点数据传输到 GPU 中,而glVertexAttribPointer 说明了如何解释这些数据。在使用顶点缓冲对象(VBO)时,顶点数据通常存储在缓冲区中,但这些数据不会自动传递给顶点着色器。相反,我们需要通过顶点属性指针告诉 OpenGL ES如何解释这些数据。glEnableVertexAttribArray用于启用指定位置的顶点属性数组。例如,启用位置0的顶点属性数组,可以调用 glEnableVertexAttribArray(0)。 795e41f4b71Sopenharmony_ci 796e41f4b71Sopenharmony_ci 797e41f4b71Sopenharmony_ci### 绘制图元并显示 798e41f4b71Sopenharmony_ci 799e41f4b71Sopenharmony_ci```cpp 800e41f4b71Sopenharmony_civoid glDrawArrays(GLenum mode, // 参数指定要绘制的图元的类型,比如GL_TRIANGLES表示绘制三角形。 801e41f4b71Sopenharmony_ci GLint first,// 参数指定要绘制的顶点数组的起始索引。 802e41f4b71Sopenharmony_ci GLsizei count // 参数指定要绘制的顶点数量 803e41f4b71Sopenharmony_ci ); 804e41f4b71Sopenharmony_ci``` 805e41f4b71Sopenharmony_ci 806e41f4b71Sopenharmony_ciglDrawArrays函数用于根据当前绑定的顶点数组和顶点属性以及其他设置来绘制图元。 807e41f4b71Sopenharmony_ci 808e41f4b71Sopenharmony_ci```cpp 809e41f4b71Sopenharmony_ciEGLBoolean eglSwapBuffers(EGLDisplay dpy, // EGL显示连接 810e41f4b71Sopenharmony_ci EGLSurface surface); // 要交换其缓冲区的EGL表面 811e41f4b71Sopenharmony_ci``` 812e41f4b71Sopenharmony_ci 813e41f4b71Sopenharmony_cieglSwapBuffers函数用于交换前后缓冲区的内容,并将渲染结果显示在屏幕上。 814e41f4b71Sopenharmony_ci 815e41f4b71Sopenharmony_ci## 相关实例 816e41f4b71Sopenharmony_ci 817e41f4b71Sopenharmony_ci针对OpenGL ES的使用和相关开发,有以下相关实例可供参考: 818e41f4b71Sopenharmony_ci 819e41f4b71Sopenharmony_ci- [简易Native C++示例](https://gitee.com/openharmony/codelabs/tree/master/NativeAPI/NativeTemplateDemo) 820e41f4b71Sopenharmony_ci- [Native XComponent组件的使用](https://gitee.com/openharmony/codelabs/tree/master/NativeAPI/XComponent)