干货:OpenGL ES pipeline 简介

2021-12-20 14:38:17 浏览数 (2)

前言

在移动应用开发过程中用到了 OpenGL ES 的相关知识,虽然 app 已经完成了相应的功能,但是始终觉得自己的认知与真实的 OpenGL ES 隔了一层薄雾,因此趁着周末有时间,彻底学习一下OpenGL ES。

OpenGL ES 简介

OpenGL ES 是一套用于手持嵌入式设备的API,如手机、PDA等上面都可以使用到。

OpenGL ES是一个跨平台的,功能完善的2D和3D图形应用程序接口API,而且它还是免授权费的。

它其实是源自于桌面系统使用的OPENGL,但是因为目标设备不一样,OpenGL ES不可避免的对OpenGL做了一遍精简,比如说去除了OpenGL中的立即模式(immediate mode)、显示列表(display list)等许多非必要的复杂特性。

OpenGL ES 其实是一个状态机,它保存一种状态直至它改变,然后进入下一个状态。每个状态都有本身默认的缺省值,可以通过相关的函数进行查询和设置。

因为OpenGL ES在流程上采用的是同一套处理顺序,因此我们平常称这一个标准的处理流程为OpenGL ES的渲染管线(pipeline)。

OpenGL ES 2.0的标准流程图如下:

OpenGL ES 3.0的标准流程图如下:

从OpenGL ES Programming Guide来看,OpenGL ES 2.0 与 OpenGL ES3.0 大体上是一致的,对API来说,其可使用的接口模块也是一样的(Graphics Pipeline图中有阴影的模块为可编程API)。

因此本文主要以 OpenGL ES 2.0 的流程来学习OpenGL ES。

OpenGL ES 2.0 pipeline 介绍

1、Vertex Buffer/ArrayObjects

顶点数据来源,即渲染管线的顶点输入,通常使用Buffer object效率更好。

2、Vertex Shader

顶点着色器,该模块为可编程模块,可以通过API来对顶点进行操作。

顶点着色器是以顶点为目标来进行处理的,如通过矩阵变换位置,根据光源生成每个顶点的颜色数据,以及计算生成或移动纹理的坐标。

该模块的输入通常为:

1) Attributes:来自顶点数组中每个顶点的数据

2) Uniforms:顶点着色器的常量数据,不能被着色器修改,一般用于对同一组顶点组成的3D物体中所有顶点都相同的变量,如光源的位置。

3) Samplers:一种特殊的Uniforms,顶点着色器使用的纹理,这个输入是可选的。

4) Shader program:这个是顶点着色器上要执行的处理的代码。

顶点着色器的输出称为Varying变量(varying variables),在图元光栅化阶段,varying变量的值为每个生成的原片进行计算(这个计算过程称为插值),然后作为输入数据输入到片元着色器(fragment shader)。

顶点着色器的结构图如下:

vertex shader编程示例,详细编程规则参看《OpenGL ES2.0 Programming Guide》:

代码语言:javascript复制
// uniforms used by the vertex shader
 uniform mat4 u_mvpMatrix; // matrix to convertP from model
 //space to normalized device space.
 
// attributes input to the vertex shader
 attribute vec4 a_position; // position value
 attribute vec4 a_color; // input vertex color
 
 //varying variables – input to the fragment shader
 varying vec4 v_color; // output vertex color
 
 void
 main()
 {
 v_color = a_color;
 gl_Position = u_mvpMatrix * a_position;
 }

3、Primitive Assembly

图元(Primitive)是一个可以使用合适OpenGL ES指令进行渲染的几何结构。图元装配(Primitive Assembly),顾名思义就是组装图元的意思,也就是说它会把顶点组装成图元,同时它也会对它组装的图元进行一个简单的处理以使得在后续流程中只处理可以在屏幕中显示的图元。

图元装配首先会将顶点着色器处理过的顶点组装成一个一个独特的可以被渲染的几何图元,如三角形、线、点块纹理。

在组装好图元之后,它会判断该图元是否处于屏幕的可显示的范围内,如果图元完全不在屏幕的可显示范围内,那么它就会丢弃该图元,如果图元有一部分在可显示的范围内,则裁切图元,丢弃不在屏幕显示范围内的部分。

除此之外,图元装配也会去判断图元的朝向是面向正面还是背面,如果图元是面向背面的,那么该图元也会被丢弃。

4、Rasterization

在图元装配之后就是光栅化(Rasterization)图元了,它是将上一步装配好的图元(点块、线、三角形)转换成可以画到屏幕上的二维片(two-dimensional fragments),以便家下来的片元着色器(fragmentsshader)对它进行处理。其流程图如下:

5、Fragment Shader

片元着色器(Fragment Shader)也是一个可编程模块,我们可以通过OpenGL ES的对应api来对片元进行处理。

片元着色器可以丢弃片元(fragment)也可以为片元生成一个颜色值储存在内置变量gl_FragColor中。

光栅化阶段生成的颜色、深度、模板和屏幕坐标将成为下一个阶段——逐个片源处理(Per-Fragment operations)的输入。

1) Varying vriables:顶点着色器计算出来的Varyingvriables经过光栅化模块对每个片进行插值计算之后的值

2) Uniforms:片着色器模块使用的常量数据

3) Samplers:一种特殊的uniforms类型,供片着色器使用的纹理

4) Shader program:实现片着色器里相关处理/操作的代码

其结构如图:

Shader program编程示例,详细编程规则参看《OpenGL ES 2.0 Programming Guide》:

代码语言:javascript复制
 precision mediump float;
 varying vec4 v_color; // input vertex colorfrom vertex shader
 
void
 main(void){
    gl_FragColor = v_color;
}

6、Per-FragmentOperations

在逐个片元处理阶段,一个经由光栅化阶段生成的片元,假如它对应的屏幕坐标为(x,y),那么在这个阶段,在该片元处理过程中,只能改动framebuff中坐标为(x,y)的像素。

其处理过程由如下流程组成:

1) Pixel ownership test:像素所有权测试,该测试是为了确定片元坐标(x,y)对应在framebuffer中的像素是否是属于当前OpenGL ES的context,而所有权的决定方在窗口系统(Windows System),比如说:如果一个OpenGL ES帧缓冲窗口被其它窗口遮住了,那么窗口系统会决定这个像素不属于当前OpenGL ES的context,也就是说这个像素不应该在当前窗口中显示。

2) Scissor test:裁剪测试,该测试用于判断片元的坐标(x,y)是否处于当前OpenGL ES确定的裁剪矩形内,如果片元坐标不在这个矩形里面,那么该片元会被丢弃。

3) Stencil and depthtests:模板和深度测试,根据传入片元的模板和深度来决定是否丢弃片元。

4) Blending:混合,将新产生的片元的颜色值与framebuffer中对应坐标(x,y)的像素的颜色值进行回合。

5)Dithering:抖动,用于最大限度地优化用有限精度储存framebuffer中的颜色的过程。

在经过以上过程之后,要么丢弃片元,要么将片元的颜色、深度、模板值写入framebuffer的(x,y)像素。

最后是丢弃片元还是写入像素是由write mask决定的,write mask能够完美地使颜色、深度和模板值写入合适的像素。比如说,write mask可以设置成没有红色的像素被写入framebuffer。

最后

除此之外,OpenGL ES 2.0还提供了从framebuffer中读取像素的接口。不过需要注意,只有像素能够读取,而深度与模板值是无法获取的。

参考文档:

[1]. 《OpenGL ES 2.0 ProgrammingGuide》

[2]. 《OpenGL ES 3.0 ProgrammingGuide》

转载请注明出处:http://www.zy2zy.com/articles/64.html

0 人点赞