OpenGL 编程/OpenGL ES 概述
嵌入式系统 OpenGL (OpenGL ES) 是 OpenGL 3D 图形 API 的一个子集。它专为嵌入式设备而设计,例如移动电话、PDA 和视频游戏机。支持 OpenGL ES 2.0 的著名平台包括iPhone 3GS 及更高版本、Android 2.2 及更高版本和WebGL。桌面显卡驱动程序通常不支持 OpenGL ES API。然而,截至 2010 年,一些显卡制造商在其桌面驱动程序中引入了 ES 支持[1]。
对于熟悉其他 OpenGL 版本的人来说,以及浏览网络查找 OpenGL ES 2.0 信息时需要注意的一点是,OpenGL ES 2.0 API 与 OpenGL <= 3.0 和 OpenGL ES 1.x 相去甚远。
- 不支持固定管道
- 这意味着没有内置的灯光、雾、多纹理或顶点变换(平移/旋转等)支持。这些功能必须作为自定义着色器实现。对于标准用途,这些着色器非常简单,大多数情况下只需复制粘贴即可。
- 仅使用顶点数组/顶点缓冲区处理顶点
- 不支持立即模式 (glBegin/glEnd) 和显示列表。
- 辅助函数较少
- 例如,glFrustum()、glTranslate() 和 glRotate() 不存在。
这些设计决策导致 API 规模更小,但也需要更深入地了解渲染过程,并且需要付出更多努力(可能需要编写更多样板代码)才能设置渲染管道。从某种意义上说,OpenGL ES 2.0(发布于 2007 年)领先于它的时代:对于桌面 OpenGL 来说,直到 OpenGL 3.1(发布于 2009 年)才将传统功能从核心功能中删除。
以下是 OpenGL ES 2.0 管道的粗略概述。我们将在后面更详细地讨论各个阶段。有关非常详细的说明,请参阅OpenGL ES 2.0 规范。
- 顶点着色器
- 输入:属性(顶点位置和其他每个顶点的属性,例如通过顶点数组/顶点缓冲区的纹理位置)、采样器(纹理)、统一变量(常量)
- 输出:gl_Position(在裁剪坐标中)、gl_FrontFacing(自动生成)、gl_PointSize(用于点精灵)、用户定义的变元(到片段着色器)
- 图元装配
- 三角形/线条/点精灵
- 裁剪
- 透视除法(产生设备坐标)
- 视口变换(产生窗口坐标)
- 光栅化
- 剔除
- 深度偏移
- 变元插值
- 片段着色器
- 输入:gl_FragCoord、gl_FrontFacing、gl_PointCoord、采样器(纹理)、统一变量(常量)、插值的变元(来自顶点着色器)
- 输出:gl_FragColor
- 片段操作
- 剪切测试
- 模板测试
- 深度测试
- 混合
- 抖动
- 渲染目标
- 窗口系统提供的可绘制对象(直接渲染到屏幕)
- 帧缓冲区和附加的渲染缓冲区(充当颜色、深度或模板缓冲区)或附加的纹理缓冲区
着色器是在 GPU 上运行的小程序。语法类似于 C,但有许多限制。着色器的输入称为属性(用于每个顶点/每个片段的输入)和统一变量(用于所有顶点/片段的常量)。用户定义的输出称为变元。
设置顶点和片段着色器包括将着色器(作为包含GLSL 的字符串)传递给 OpenGL ES API,编译两个着色器并将它们链接(在链接期间,检查输入/输出变元的对应关系),并将缓冲区绑定到统一变量和变元。
对于每个输入顶点,顶点着色器都会调用一次。顶点着色器的主要任务是为管道后面的阶段提供顶点位置。此外,它还可以计算其他属性,这些属性可以作为片段着色器后面的输入。最基本的着色器只是将顶点位置作为输入,并将输入数据直接分配给 gl_Position 变元。通常,着色器会执行与模型视图投影矩阵(作为统一变量常量传递)的乘法,以允许对输入几何体进行平移和旋转以及透视投影,可能传递纹理坐标并计算光照参数。
其他用户输出通常包括
- 纹理坐标。这些坐标可以只是从输入属性中传递到简单的纹理贴图,但也可以生成或处理以实现反射表面和环境映射,或其他效果,例如动态纹理贴图。
- 雾因子。对于雾效果,可以在顶点着色器中计算图元到眼睛的距离。片段着色器稍后可以根据此值淡出片段。
- 光照参数。根据光源位置(作为统一变量常量传递)和顶点法线(需要作为额外的每个顶点的输入),可以为片段着色器生成光照参数。
请注意,在 OpenGL ES 2.0 中,通过采样器在顶点着色器中进行纹理访问是可选的,并且可能在某些设备上不受支持。
在图元装配阶段,将执行几个坐标变换
- 裁剪。位于视体之外的图元将被丢弃,部分位于视体之外的图元将被裁剪。顶点着色器的变元输出也会被裁剪。
- 透视除法。gl_Position 的三个主要元素(x、y、z)通过除以第四个顶点元素 w 归一化为 [-1.0...1.0]。结果是归一化的设备坐标。
- 视口变换。通过使用 glViewport() 和 glDepthRangef() 设置的参数进行线性变换,将坐标转换为窗口坐标。
光栅化是指从图元创建二维光栅化图像的过程,即为每个图元计算片段(像素)集。对于多边形光栅化,这包括以下步骤。
- 剔除。如果使用 FrontFace() 和 CullFace() 启用,则可以丢弃从背面看到的多边形。
- 深度偏移。可以使用 PolygonOffset() 对多边形坐标应用深度偏移。这可以防止位于同一平面的多边形的 Z 抖动。
- 变元插值。在作为片段着色器输入准备时,顶点着色器的变元输出和深度将被插值。
片段着色器对每个图元片段(像素)调用一次。片段着色器的主要任务是为每个输出片段提供颜色值。最基本的片段着色器只是将一个常数值分配给它的 gl_FragColor 输出。通常,片段着色器会执行纹理查找,并根据顶点着色器之前计算的照明参数来实现照明。
- 剪切测试。如果使用 glEnable(GL_SCISSOR_TEST) 启用,则仅绘制指定矩形区域内的像素。使用 glScissor() 配置。
- 模板缓冲区测试。如果使用 glEnable(GL_STENCIL_TEST) 启用,则只有在通过与模板缓冲区的测试后,才能更新像素。使用 glStencil*() 配置。
- 深度缓冲区测试。如果使用 glEnable(GL_DEPTH_TEST) 启用,则只有在通过深度缓冲区测试后,才会绘制像素,从而实现隐藏面剔除。使用 glDepthFunc() 配置。需要提供深度缓冲区。
- 混合。如果使用 glEnable(GL_BLEND) 启用,则片段着色器输出的像素可以与输出缓冲区中已经存在的像素值混合。混合的配置使用 glBlend*() 完成。
- 抖动。如果使用 glEnable(GL_DITHER) 启用,则可以使用抖动来增加感知到的颜色深度。无法进一步控制抖动过程。
- 抗锯齿。使用 glEnable(GL_SAMPLE_COVERAGE)、glEnable(GL_SAMPLE_ALPHA_TO_COVERAGE) 和 glSampleCoverage(),可以配置简单的抗锯齿。
生成的像素有几个可能的目标。
- 窗口系统提供的可绘制对象(直接渲染到屏幕)
- 帧缓冲区和附加的渲染缓冲区
帧缓冲区对象具有三个附加的对象:深度或模板渲染缓冲区,以及颜色渲染缓冲区 *或* 纹理缓冲区。颜色渲染缓冲区不能用作纹理源。
- glGenRenderbuffers()、glGenFramebuffers()、glBindRenderbuffer()、glRenderbufferStorage()、glBindFramebuffer()、glFramebufferRenderbuffer()、glFramebufferTexture*()、glCheckFramebufferStatus()、glDeleteRenderbuffers()、glDeleteFramebuffers()
待办事项 - 请参阅 [2] 等以获取代码示例
应用模型视图变换以及世界对象的平移/旋转必须在顶点着色器中完成。你应该使用一个实用程序库来执行这些计算。例如,在桌面 OpenGL 帮助方法的文档中描述了数学运算,这些方法在 OpenGL ES 2.0 中被省略。
- Khronos OpenGL ES API 注册表,包括 OpenGL ES 2.0 规范和 API 参考。
- 在 iPhone 3GS 上开始使用 OpenGL ES 2.0 博客文章
- 示例代码 来自 OpenGL ES 2.0 编程指南 一书