OpenGL ES 初探

578 阅读6分钟

OpenGL ES 概述

OpenGL ES(OpenGL for Embedded Systems)是以手持和嵌入式为目的的高级 3D 图形应用程序编程接口(API),OpenGL ES 是目前智能手机中占据统治地位的图形 API,支持的平台有:iOS,Android,BlackBerry,bada,Linux,Windows。

Apple 官方对 OpenGL ES 的解释:OpenGL ES开放式图形库用于可视化的二维和三维数据。它是一个多功能开放标准图形库,支持 2D 和 3D 数字内容创建,机械和建筑设计,虚拟原型设计,飞行模拟,视频游戏等应用程序。您可以使用 OpenGL 配置 3D 图形管道并向其提交数据,顶点变换、光照、组合图元、光栅化后创建 2D 图像。OpenGL 旨在将函数调用转为可以发送到底层图形硬件的图形命令。由于此底层硬件专用于处理图形命令,因此 OpenGL 绘图非常的快。OpenGL ES 是OpenGL 的简化版本,它消除了冗余功能,提供了一个既易于学习又更易于在移动图形硬件中实现的库。

1. OpenGL ES 图形管道

OpenGL ES 图形管道

接下来我们来解释一下这个图形管道:

  • 从 Application 获取数据,包括图元信息(点、线、三角形)和图片信息
  • 在顶点着色器里处理顶点位置的变化(平移、旋转、缩放)以及光照信息(法向量)
  • 几何图形处理,比如我们的图元装配、裁剪、透视分割和 viewport 变换等
  • 在片元着色器里面获取纹素(纹理像素点的颜色值)是否需要雾化处理
  • 最后在帧缓冲区做一些其他处理比如 alpha 计算,深度测试等。

2. 顶点着色器

顶点着色器是描述顶点上执行操作的源代码或可执行文件。

顶点着色器工作流程

2.1 顶点着色器输入

  • Attribute:用顶点数组提供每个顶点的数据(顶点位置、法向量、纹理坐标以及顶点颜色)
  • Uniforms:顶点、片元着色器使用的不变数据(变换矩阵、光照参数、颜色)
  • Samplers:用于呈现纹理,可用于顶点、片元着色器

2.2 顶点着色器输出

  • 可变变量(Varying):varying 变量用于存储顶点着色器的输出数据,也存储片元着色器的输入数据。varying 变量会在光栅化处理阶段被线性插值。顶点着色器如果声明了 varying 变量,它必须被传递到片元着色器中才能进一步传递到下一阶段,因此顶点着色器中声明的 varying 变量都应在片元着色器中重新声明为同名同类型的 varying 变量。
  • gl_Position:在顶点着色器阶段至少应输出位置信息-即内建变量。
  • gl_FrontFacing:为back-face culling stage阶段生成的变量,无论精选是否被禁用,该变量都会生成。
  • gl_PointSize:点大小。

2.3 顶点着色器业务

  • 位置矩阵变换
  • 计算光照公式生成逐顶点颜色
  • 生成/变化纹理坐标

它可以用于执行自定义计算,实施新的变换,照明或者传统的固定功能所不允许的基于顶点的效果。

2.4 代码示例

// 位置属性
attribute vec4 position;
// 坐标属性
attribute vec2 textCoordinate; 
// 旋转角度
uniform mat4 rotateMatrix; 
// 输出变量
varying lowp vec2 varyTextCoord; 

// 着色器程序(Shader Program)
void main()
{
    // 赋值坐标属性到输出变量
    varyTextCoord = textCoordinate;
    // 位置乘以旋转矩阵
    vec4 vPos = position;
    vPos = vPos * rotateMatrix; 
    // 赋值位置到内建变量gl_Position上,作为输出信息(必须)
    gl_Position = vPos;
}

3. 片元着色器

片元着色器是描述在片元上执行操作的源代码或可执行文件。

片元着色器工作流程

3.1 片元着色器输入

  • 可变变量(Varyings): 顶点着色器阶段输出的 varying 变量在光栅化阶段被线性插值计算之后输出到片元着色器中作为它的输入,即上图中的 gl_FragCoord,gl_FrontFacing 和 gl_PointCoord。
  • 统一值(Uniforms): 用于片元着色器的常量,如雾化参数,纹理参数等。
  • 采样器(Samples): 一种特殊的 uniform,用于呈现纹理。

3.2 片元着色器输出

  • gl_FragColor: 在顶点着色器阶段只有唯一的 varying 输出变量-即内建变量gl_FragColor。

3.3 片元着色器业务

  • 计算颜色
  • 获取纹理值
  • 往像素点中填充颜色值/纹理值

它可以用于图片/视频/图形中每个像素的颜色填充(比如给视频添加滤镜,实际上就是将视频中每个图片的像素点颜色填充进行修改)

3.4 片元着色器示例代码

// 纹理坐标
varying lowp vec2 varyTextCoord;
// 采样器
uniform sampler2D colorMap;

// 着色器程序(Shader program)
void main()
{
    // 读取纹素(纹理的颜色)并赋值给内建变量gl_FragColor
    gl_FragColor = texture2D(colorMap,varyTextCoord);
}

4. 逐片段操作

  • 像素归属测试:确定帧缓冲区中的位置(x,y)的像素目前是不是归属于 OpenGL ES 所有。例如如果一个显示 OpenGL ES 帧缓冲区的 view 被另外一个 view 所遮蔽,则窗口系统可以确定被遮蔽的像素不属于 OpenGL ES 上下文,从而不全显示这些像素。而像素归属测试是 OpenGL ES的一部分,它不由开发人员人为控制,而是由 OpenGL ES 内部进行。
  • 裁剪测试:裁剪测试确定(x,y)是否位于作为 OpenGL ES 状态的一部分裁剪矩形范围内,如果在裁剪区域之外则被抛弃。
  • 深度测试:输入线段的深度值进行比较,确定片段是否拒绝测试
  • 混合:混合将新生成的片元颜色与保存在帧缓冲区的位置的颜色值组合起来。
  • 抖动:抖动可用于最小化因为使用有限精度在帧缓冲区中保存颜色值而产生伪像。

5. OpenGL ES 错误处理

如果不正确使用 OpenGL ES 命令,应用程序就会产生一个错误编码。这个错误编码将被记录,可以用 glGetError() 函数查询,在应用程序使用 glGetError() 查询第一个错误码之前,不会记录其他错误代码。一旦查询到错误代码,当前错误代码便复位为 GL_NO_ERROR

GLenum glGetError(void)
错误代码 描述
GL_NO_ERROR 从上一次调用 glGetError 依赖没有生成任何错误
GL_INVALID_ENUM GLenum 参数超出范围,忽略生成错误命令
GL_INVALID_VALUE 数值型参数超出范围,忽略生成错误命令
GL_INVALID_OPERATION 特定命令在当前 OpenGL ES 状态无法执行
GL_OUT_OF_MEMORY 内存不足时执行改名了,如果遇到这个错误,除非有当前错误代码,否则 OpenGL ES 管线的状态被认为未定义