【OpenGL ES 太好玩了】图形渲染管线

·  阅读 447
【OpenGL ES 太好玩了】图形渲染管线

前言

OpenGL 是一个在现实中大量使用和广为接受的跨平台标准的图形应用程序编程接口(API),规模庞大且复杂,而 OpenGL ES 是 OpenGL 删除一切冗余后得出,可以说是 OpenGL 的一个子集,以手持和嵌入式设备为标准,在当今智能手机中占据统治地位。

需要注意的是在 WWDC2018 上,苹果宣布在新系统上弃用OpenGL、ES、CL,而采用苹果私有的 Metal。

image.png

图形渲染管线

OpenGL ES 实现了具有可编程着色功能的图形管线,由 OpenGL ES APIOpenGL ES Shader Language(可以称为 GLSL,主要用来编写着色器代码) 两个规范组成。

对于不同版本的区别,相比于 OpenGL ES 1.x 系列的固定功能管线,OpenGL ES 2.0 和 OpenGL ES 3.0 都是可编程图形管线。开发者可以自己编写图形管线中的 顶点着色器 和 片段着色器 两个阶段的代码。并且 OpenGL ES 3.0 是向后兼容 OpenGL ES 2.0 的。也就是说使用 2.0 编写的应用程序是可以在 3.0 中继续使用的。

但是在实际场景中,任然大量的项目使用的是 OpenGL ES 2.0,因此该系列的文章会尽量囊括 2.0 和 3.0。

图形渲染管线翻译自 Graphics Pipeline,看起来翻译没问题,但是其实「管线」这个词容易给人造成歧义或迷惑。可以理解成图形渲染管线就是图形渲染的「流程」,这里我们看看比较权威的 LearnOpenGL 网站上对它的解释:

图形渲染管线(Graphics Pipeline,大多译为管线,实际上指的是一堆原始图形数据途经一个输送管道,期间经过各种变化处理最终出现在屏幕的过程)管理的。

下图展示了 OpelGL ES 图形管线:

image.png

图中带阴影的方框是管线中的可编程阶段,也是我们实现各种特效、滤镜最重要的部分。

顶点着色器

顶点着色器(Vertex Shader)实现了顶点操作的通用可编程方法。

image.png 顶点着色器的输入和输出如上图所示。

顶点着色器的输入包括:

  • 顶点着色器程序/vertex shader:描述顶点上执行操作的顶点着色器程序
  • 顶点着色器输入(或者属性)/input(attribute):用顶点数组提供的每个顶点的数据
  • 统一变量/uniforms:顶点着色器或者片段着色器使用的不变数据
  • 采样器/samplers:代表顶点着色器使用纹理的特殊统一变量类型

顶点着色器的输出称为顶点着色器输出变量。在图元光栅化阶段计算顶点着色器输出值,并作为输入传递给片段着色器用于分配给每个图元顶点的顶点着色器输出生成每个片段值的机制称作插值(Interpolation

OpenGL ES 3.0 新增了一个新功能 - 变换反馈(Transform Feedback),使顶点着色器输出可以选择性地写入一个输出缓冲区(除了传递给片段着色器之外,也可能代替这种传递)。

我们来看一个顶点着色器代码:

//顶点着色器示例
//指定 OpenGL ES 版本
#version 300 es
//统一变量
uniform mat4 u_mvpMatrix;
//输入
in vec4 a_position;
in vec4 a_color;
//输出,传递给片段着色器
out vec4 v_color;

void main(){
    v_color = a_color;
    gl_Position = u_mvpMatrix * a_position;
}
复制代码

这段程序跟 c 语言写法非常类似,目前看不懂没关系,有个概念即可,后续的文章会详细讲解。

可以看到输入和输出变量上分别用 in 和 out 关键字修饰,需要注意的是 3.0 中新增了 in、out、inout 关键字用来取代 attribute 和 varying 关键字,同时 gl_FragColor 和 gl_FragData 也删除了。

这段程序中,a_position 是输入顶点位置属性,a_color 是输入顶点颜色属性,v_color 用于存储描述每个顶点颜色的顶点着色器输出。内简变量 gl_Position 是自动声明的,着色器必须将变换后的位置写入这个变量。

图元装配

顶点着色器之后,OpenGL ES 渲染图形管线的下一阶段是图元装配(Primitive Assembly)。图元是点、直线、三角形等几何对象。图元的每个顶点被发送到顶点着色器的不同拷贝,在图元装配期间,这些顶点被组合成图元。

对于每个图元,必须确定图元是否位于屏幕上可见的空间区域内,如果没有则可能需要进行裁剪。裁剪之后,定点位置被转换为屏幕坐标。如果图元完全处于可见的空间区域外,则会被抛弃。也可以执行一次淘汰操作,根据图元面向前方或者后方抛弃它们。裁剪和淘汰之后,图元便准备传递给管线的下一阶段 - 光栅化。

光栅化

光栅化(Rasterization)是将图元转化为一组二维片段(这些二维片段代表着可在屏幕上绘制的像素)的过程,在此阶段会绘制对应的图元(点、直线、三角形),然后这些片段传递给片段着色器处理。每个片段的输出包含屏幕坐标、颜色、纹理坐标等。 image.png

片段着色器

片段着色器(Fragment Shader)为片段上的操作实现了通用的可编程方法。对光栅化阶段生成的每个片段执行片段着色器。

image.png

片段着色器的输入包括:

  • 片段着色器程序/fragment shader:描述片段上所执行操作的片段着色器程序
  • 输入变量/input(varying):光栅化单元用插值为每个片段生成的顶点着色器输出
  • 统一变量/uniforms:片段着色器或者顶点着色器使用的不变数据
  • 采样器/samplers:代表片段着色器所用纹理的特殊统一变量类型

片段着色器可以抛弃片段,也可以生成一个或者多个颜色值作为输出。光栅化阶段生成的颜色、深度、模板和屏幕坐标变成 OpenGL ES 图形渲染管线逐片段操作阶段的输入。

我们来看一个片段着色器的代码:

//顶点着色器示例
//指定 OpenGL ES 版本
#version 300 es
//指定精度限定符
precision mediump float;
//顶点颜色输入,从顶点着色器输出数据传入
in vec4 v_color;
//输出
out vec4 fragColor;
void main(){
    fragColor = v_color;
}
复制代码

片段着色器的输入在图元之间进行线性插值,然后传递给片段着色器。可以看到,顶点着色器的输出必须和片段着色器的输入是同一组变量。fragColor 将是传递到下一阶段的颜色,它的值设置为输入颜色 v_color。

逐片段操作

片段着色器的下一阶段是逐片段操作(Per-Fragment Operations)。光栅化生成的屏幕坐标为(x,y)的片段只能修改帧缓冲区中位置为(x,y)的像素。在逐片段操作阶段,会在每个片段上执行一些功能和测试,这个我们暂时不做了解。

总结

本篇我们介绍了 OpenGL ES 的图形渲染管线,可以为我们后续的学习做铺垫。另外如果看完仍然很懵也没关系,等该系列整个讲完再回来看这篇,会对整体流程有更清晰的认识。

分类:
Android
标签:
分类:
Android
标签: