3. OpenGL ES基础
在 Wayland 客户端中,OpenGL ES 是实际完成图形绘制的核心 API。Wayland 只负责窗口、输入与缓冲区交换,而像素如何生成、如何合成,最终由 OpenGL ES 的渲染管线决定。理解 OpenGL ES 的版本差异、管线阶段、着色器、缓冲区与纹理,是后续把 EGL 上下文与 Wayland 表面正确对接的前提。
一、OpenGL ES 版本与能力边界
OpenGL ES 是面向嵌入式与移动设备的裁剪版 OpenGL。它去掉了大量传统固定管线接口,强调可编程着色器与低功耗。实际开发中最常见的是 ES 2.0、ES 3.0、ES 3.1 和 ES 3.2。ES 2.0 使用 GLSL ES 1.00,只支持顶点与片元两个可编程阶段;ES 3.0 引入 GLSL ES 3.00、整数纹理、实例化、多重渲染目标(MRT)和统一缓冲对象(UBO);ES 3.1 增加计算着色器与间接绘制;ES 3.2 进一步补齐几何着色器、细分着色器与同步对象。
在 Wayland 环境下,版本选择不是孤立的:它受 EGL 配置、驱动能力与合成器支持共同约束。例如使用 eglBindAPI(EGL_OPENGL_ES_API) 后,通过 EGL_CONTEXT_CLIENT_VERSION 请求 ES 3.0 上下文,若驱动只暴露 ES 2.0,则上下文创建会失败。因此工程中通常先查询 GL_VERSION 与 GL_EXTENSIONS,再决定渲染路径。
二、渲染管线:从顶点到像素
OpenGL ES 的渲染管线可以理解为一条从顶点数据到帧缓冲像素的流水线。以 ES 3.0 为例,主要阶段包括:顶点着色器、图元装配、裁剪与透视除法、视口变换、光栅化、片元着色器、逐片元操作(深度测试、模板测试、混合),最终写入帧缓冲。Wayland 客户端通常把帧缓冲绑定到 EGLSurface,再通过 eglSwapBuffers 把结果提交给合成器。
管线中每个阶段都有明确的输入输出。顶点着色器处理每个顶点的位置与属性;光栅化把图元离散为片元;片元着色器计算颜色;逐片元操作决定该片元是否写入以及如何与已有像素混合。理解这条链路,才能在画面异常时快速定位是顶点变换错误、纹理采样错误,还是混合状态错误。
三、着色器:可编程阶段的核心
着色器是运行在 GPU 上的小程序,使用 GLSL ES 编写。顶点着色器必须输出裁剪空间坐标 gl_Position;片元着色器输出颜色,通常写入 out 变量。ES 3.0 中推荐显式声明版本与精度:
#version 300 es
precision mediump float;
layout(location = 0) in vec3 aPos;
layout(location = 1) in vec2 aTexCoord;
uniform mat4 uMVP;
out vec2 vTexCoord;
void main() {
vTexCoord = aTexCoord;
gl_Position = uMVP * vec4(aPos, 1.0);
}
片元着色器通过 sampler2D 采样纹理,并可与光照、混合因子结合。着色器编译与链接需要检查日志:glGetShaderiv 配合 GL_COMPILE_STATUS,glGetProgramiv 配合 GL_LINK_STATUS。在 Wayland 客户端中,着色器错误往往表现为黑屏或花屏,而不是进程崩溃,因此日志检查是必备步骤。
四、缓冲区与纹理:数据如何进入 GPU
缓冲区对象(VBO、EBO、UBO)用于存放顶点属性、索引和统一变量。顶点数据通过 glGenBuffers、glBindBuffer、glBufferData 上传,再用 glVertexAttribPointer 描述布局。纹理则通过 glGenTextures、glBindTexture、glTexImage2D 创建,并设置过滤与环绕方式。纹理坐标原点、Y 轴方向与 Wayland 合成器的缓冲区方向需要特别留意,否则会出现上下颠倒。
| 对象 | 典型 API | 作用 | 常见误区 |
|---|---|---|---|
| VBO | glBufferData / glVertexAttribPointer | 存放顶点位置、法线、UV | 属性步长与偏移写错导致顶点错位 |
| EBO | glDrawElements | 索引复用顶点,减少数据量 | 索引类型与 GL_UNSIGNED_INT 不匹配 |
| UBO | glBindBufferBase | 批量传递矩阵、光照参数 | std140 对齐规则未遵守 |
| Texture | glTexImage2D / glGenerateMipmap | 提供表面颜色细节 | 未设置过滤导致远处闪烁 |
五、典型场景与排障复盘
假设一个 Wayland 客户端要绘制带纹理的旋转矩形。流程是:创建 EGL 上下文并绑定到 wl_egl_window;编译顶点与片元着色器;上传顶点与 UV 到 VBO;加载纹理并设置 GL_LINEAR;每帧更新 MVP 矩阵,执行 glDrawElements,最后 eglSwapBuffers。若画面全黑,排查顺序建议为:着色器编译日志 → 属性 location 是否匹配 → 纹理是否绑定到正确纹理单元 → 深度测试与混合状态 → EGL 配置是否支持当前颜色格式。
eglSwapBuffers 会触发缓冲区提交;若合成器未收到新缓冲区,画面不会更新。因此渲染循环必须与 Wayland 的事件循环配合,而不是只写一个死循环。#version 300 es 中继续使用 attribute、varying 会直接编译失败;应改用 in、out。同时注意 ES 3.0 要求片元着色器声明精度,否则部分驱动会报错。掌握版本、管线、着色器、缓冲区与纹理这五个基础点后,就能理解 Wayland 客户端中“谁在画、画到哪里、怎么交给合成器”的完整链条。后续接入 EGL 与 wl_surface 时,这些概念会直接决定上下文创建、表面绑定与帧提交的正确性。