OpenGL ES pipeline简介

作者:abamon/XTBlock

我的个人博客站:www.zy2zy.com

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


前言

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

OpenGLES 简介

OpenGL ES 是一套用于手持嵌入式设备的API,如手机、PDA等上面都可以使用到。OpenGLES是一个跨平台的,功能完善的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。

OpenGLES 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》:

// 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》:

 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》

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


什么是设计模式一套被反复使用、多数人知晓的、经过分类编目的、代码 设计经验 的总结;使用设计模式是为了 可重用 代码、让代码 更容易 被他人理解、保证代码 可靠性;设计模式使代码编制  真正工程化;设计模式使软件工程的 基石脉络, 如同大厦的结构一样;并不直接用来完成代码的编写,而是 描述 在各种不同情况下,要怎么解决问题的一种方案;能使不稳定依赖于相对稳定、具体依赖于相对抽象,避免引
单一职责原则定义(Single Responsibility Principle,SRP)一个对象应该只包含 单一的职责,并且该职责被完整地封装在一个类中。Every  Object should have  a single responsibility, and that responsibility should be entirely encapsulated by t
动态代理和CGLib代理分不清吗,看看这篇文章,写的非常好,强烈推荐。原文截图*************************************************************************************************************************原文文本************
适配器模式将一个类的接口转换成客户期望的另一个接口,使得原本接口不兼容的类可以相互合作。
策略模式定义了一系列算法族,并封装在类中,它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。
设计模式讲的是如何编写可扩展、可维护、可读的高质量代码,它是针对软件开发中经常遇到的一些设计问题,总结出来的一套通用的解决方案。
模板方法模式在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中,使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。
迭代器模式提供了一种方法,用于遍历集合对象中的元素,而又不暴露其内部的细节。
外观模式又叫门面模式,它提供了一个统一的(高层)接口,用来访问子系统中的一群接口,使得子系统更容易使用。
单例模式(Singleton Design Pattern)保证一个类只能有一个实例,并提供一个全局访问点。
组合模式可以将对象组合成树形结构来表示“整体-部分”的层次结构,使得客户可以用一致的方式处理个别对象和对象组合。
装饰者模式能够更灵活的,动态的给对象添加其它功能,而不需要修改任何现有的底层代码。
观察者模式(Observer Design Pattern)定义了对象之间的一对多依赖,当对象状态改变的时候,所有依赖者都会自动收到通知。
代理模式为对象提供一个代理,来控制对该对象的访问。代理模式在不改变原始类代码的情况下,通过引入代理类来给原始类附加功能。
工厂模式(Factory Design Pattern)可细分为三种,分别是简单工厂,工厂方法和抽象工厂,它们都是为了更好的创建对象。
状态模式允许对象在内部状态改变时,改变它的行为,对象看起来好像改变了它的类。
命令模式将请求封装为对象,能够支持请求的排队执行、记录日志、撤销等功能。
备忘录模式(Memento Pattern)保存一个对象的某个状态,以便在适当的时候恢复对象。备忘录模式属于行为型模式。 基本介绍 **意图:**在不破坏封装性的前提下,捕获一个对象的内部状态,并在该
顾名思义,责任链模式(Chain of Responsibility Pattern)为请求创建了一个接收者对象的链。这种模式给予请求的类型,对请求的发送者和接收者进行解耦。这种类型的设计模式属于行为
享元模式(Flyweight Pattern)(轻量级)(共享元素)主要用于减少创建对象的数量,以减少内存占用和提高性能。这种类型的设计模式属于结构型模式,它提供了减少对象数量从而改善应用所需的对象结