详解 OpenGL ES 2.x 渲染流程
khronos官方对OpenGL ES的描述如下:
OpenGL ES is a royalty-free, cross-platform API for rendering advanced 2D and 3D graphics on embedded and mobile systems - including consoles, phones, appliances and vehicles. It consists of a well-defined subset of desktop OpenGL suitable for low-power devices, and provides a flexible and powerful interface between software and graphics acceleration hardware.
OpenGL ES 是一种免费的跨平台 API,用于在嵌入式设备和移动系统(包括 consoles、手机、电器 和 车载 )上渲染高效的 2D 和 3D 图形。 OpenGL ES 由OpenGL裁剪而来,适用于低功耗设备,并为软件和图形硬件加速之间提供灵活而强大的接口。
这篇文章主要介绍OpenGL ES的渲染流程
,通过该文章了解一帧图像经过OpenGL ES处理,是如何最终渲染到手机屏幕上的。
二、着色语言
OpenGL ES 2.x采用的是可编程渲染管线
(OpenGL ES 1.x为固定渲染管线),将顶点着色器
、片元着色器
的编码权限开放给开发者。
开发者需要使用OpenGL ES Shading Language(着色语言)
编码实现顶点着色器
、片元着色器
处理逻辑,从而渲染出自己想的展示效果。
正文开始之前,有必要先了解一下OpenGL ES 与 OpenGL ES Shading Language 的对应关系。了解对应的API版本,才能编写对应版本要求的Shading Language脚本。
OpenGL ES Version | GLSL Version |
---|---|
1.0 | -- |
1.1 | -- |
2.0 | 100 |
3.0 | 300 |
3.1 | 310 |
3.2 | 320 |
- OpenGL ES 1.x为固定渲染管线,无Shading Language对应版本;
- OpenGL ES 2.x开始才有可编程渲染管线,
OpenGL ES 2.0
对应的OpenGL ES Shading Language
版本为1.00
Shading Language 1.00 文档:
https://www.khronos.org/registry/OpenGL/specs/es/2.0/GLSL_ES_Specification_1.00.pdf
各OpenGL ES版本API文档:
https://www.khronos.org/registry/OpenGL/specs/es/
二、渲染管线
OpenGL ES中的渲染管线
指的是一系列的绘制过程
,输入
是需要渲染的3D物体的相关描述信息数据
(例:顶点坐标、顶点颜色、顶点纹理等),经过渲染管线
,输出一帧想要的图像
。
渲染管线
也称之为渲染流水线
,由显示芯片(GPU)内部处理图形信号的并行处理单元
组成,这些并行处理单元两两之间相互独立,根据显示芯片性能的不同处理单元的数量也存在很大的差异。将渲染工作通过显示芯片中多个相互独立的单元进行并行处理后,渲染的效率可以大大提高。
OpenGL ES 2.x的渲染管线如下图所示:
2.1 基本处理
该阶段设定三维空间中物体的顶点坐标
、顶点对应的颜色
、顶点的纹理坐标
等数据,并且指定三维物体的绘制方式
,如:点绘制、线段绘制或者三角形绘制等。
2.2 顶点缓冲区
顶点缓冲区在应用程序中是可选的,对于某些在整个场景中顶点数据基本不变的情况,可以在初始化阶段将顶点数据经基本处理后送入顶点缓冲区,在绘制每一帧想要的图像时就省去了顶点数据IO的步骤,直接从顶点缓冲区中获取顶点数据即可。相比于每次绘制时单独将顶点数据送入GPU的方式,可以在一定程度上节省GPU的 IO 带宽,提高渲染效率。
2.3 顶点着色器
顶点着色器是一个可编程的处理单元
,功能为执行顶点的变换
、光照
、材质的应用与计算
等顶点的相关操作
,每个顶点执行一次
。
其工作过程为将原始的顶点几何信息(顶点坐标、颜色、纹理)
及其他属性传送到顶点着色器中,经过自定义的顶点着色程序处理产生变化后的顶点位置信息,将变化后的顶点位置信息传递给后续图元装配阶段,对应的顶点纹理、颜色等信息则经光栅化后传递到片元着色器。
顶点着色器
替代了原有固定管线
(OpenGL 1.x采用固定渲染管线)的顶点变换、光照计算
,采用 着色语言进行开发(OpenGL ES Shading Language) ,开发人员可以根据自己的需求采用着色语言自行开发顶点变换、光照等功能,大大增加了程序的灵活性。
顶点着色器的输入主要为待处理顶点相应的attribute
、uniform
、采样器以及临时变量
,输出主要为经过顶点着色器后生成的varying
及一些内建输出变量
。
uniform(统一变量)
:
指的是对于同一组顶点组成的三维物体其所有顶点属性都相同的属性量
,一般为场景中当前的光源位置
、当前的摄像机位置
、投影系列矩阵
等,,可以使用 uniform (统一变量) 传入顶点着色器。attribute(属性变量)
指的是三维空间中物体每个顶点各自不同的信息所属的变量,一般为顶点的位置
、颜色
、法向量
等,每个顶点各自不同的信息都是以attribute变量的方式传入顶点着色器的。varying(易变变量)
对于从顶点着色器传递到片元着色器的量
,如 用于传递到片元着色器中的顶点颜色,可以使用varying (易变变量)。- gl_Position (内建变量):
内建变量为输出到OpenGL ES渲染管线的数据变量
。
顶点着色器从应用程序中获得原始的顶点位置数据,这些原始的顶点数据在顶点着色器中经过平移、旋转、缩放等数学变换后,生成新的顶点位置。新的顶点位置通过在顶点着色器中写入gl_Position传递到渲染管线的后继阶段继续处理。 - gl_PointSize (内建变量):
内建变量为输出到OpenGL ES渲染管线的数据变量
。
gl_PointSize的值一般只有在采用了点绘制方式之后才有意义。顶点着色器中可以计算一个点的大小(单位为像素),并将其赋值给gl_PointSize(float类型)以传递给渲染管线,如果没有明确赋值的话,默认值为1。
顶点着色器代码举例
着色器采用OpenGL ES Shading Language编写,为一种类C的编程语言,顶点着色器代码实现举例如下:
详细了解着色语言语法,可查看khronos
官方:
OpenGL ES Shading Language 1.00:
https://www.khronos.org/registry/OpenGL/specs/es/2.0/GLSL_ES_Specification_1.00.pdf
// 顶点着色器程序举例如下:
uniform mat4 uMVPMatrix; //总变换矩阵
attribute vec3 aPosition; //顶点位置
attribute vec4 aColor; //顶点颜色
varying vec4 vColor; //用于传递给片元着色器的变量
void main()
{
//根据总变换矩阵计算此次绘制此顶点位置
gl_Position = uMVPMatrix * vec4(aPosition,1);
//将接收的颜色传递给片元着色器
vColor = aColor;
}
uniform(统一变量)
如:顶点着色器举例程序中的uniform mat4 uMVPMatrix; 总变换矩阵
。attribute(属性变量)
如:顶点着色器举例程序中的顶点位置aPosition、顶点颜色aColor。varying(易变变量)
如:顶点着色器举例程序中的varying vec4 vColor; 顶点颜色
。内建输出变量
如:如输出到渲染管线的 gl_Position(顶点的最终位置)。
注:
易变变量(顶点颜色、纹理)在顶点着色器赋值后并不直接赋值到片元着色器中,而是经由光栅化阶段处理。将三维世界由顶点数据构成的物体离散为二维世界一个个像素点,片元着色器处理的数据是已经离散到二维显示平面上的一个个离散的像素点(每个像素点为一个片元)
。而这个离散过程中位于两个顶点之间的像素顶数据(颜色值、纹理数据),大多在光栅化阶段差值计算产生
。
上图中,展示的是一个自上而下
从黑到白的一个渐变三角形
。
在OpenGL ES的输入中,仅仅输入了三个顶点颜色值,顶点1 (0,0,0) 黑色、,顶点2 (1,1,1) 白色、顶点3 (1,1,1) 白色。而在三角形在光栅化阶段,投影到屏幕上之前,其他部分的颜色是由这三个输入顶点颜色值差值计算产生的
,例如:三角形高二分之一处的点颜色差值计算的结果为 (0.5,0.5,0.5) 灰色。
2.4 图元装配
在这个阶段,主要有两个任务,一个是图元组装
,另一个是图元处理
。
图元组装
是顶点数据被结合成完整的图元。
例如:一个单独的点就是一个图元,它只需要一个点;一条直线也是一个图元,为两个点构成的图元;三角形则需要三个顶点构成一个图元。
图元组装时会有效的收集足够的顶点以组成一个单独的图元,方便进行后续处理。图元处理
最重要的是剪裁
,其任务是消除位于显示区域之外的部分
几何图元。
图元处理阶段:
如果图元完全位于视椎体
内,则传递图元进行后面的处理;
如果有一部分位于视椎体
内,则剪裁
该图元(剪裁图元可能会额外增加顶点
数据)。
如果其完全位于视椎体
外,则丢弃该图元,以节省GPU性能。
2.5 光栅化
光栅化就是将三维空间中连续的数学图形
,栅格化为二维显示平面上一个个像素点
的过程。
将由三维
顶点信息组成的几何图元
,栅格化
为帧缓冲区中由无数像素点
构成的二维图像
。这里,最终生成的帧缓冲区中每一个像素点
都被看做一个片元
。
例如,一条直线可能在屏幕上包含了5个像素,而光栅化就是将这条直线转化为5个片元,每个片元由顶点坐标、顶点颜色、顶点纹理坐标以及顶点的深度等信息组成。
光栅化实际是由以下两个过程组成:
- 将三维空间中的几何图元,投影到二维平面上:
- 将投影到二维平面上的几何图元,栅格化为帧缓冲区中一个个像素:
2.6 片元着色器
片元着色器
是用于处理片元相关数据的可编程单元
,可以执行纹理的采样
、颜色的汇总
、计算雾颜色
等操作,每片元执行一次(每个像素执行一次)
。
片元着色器
可编程单元替换了OpenGL ES 1.x固定渲染管线阶段中纹理颜色求和
、雾以及Alpha测试
等阶段,被其替代的功能将需要由开发人员用着色器语言编码完成。
顶点着色器每顶点执行一次
,片元着色器每片元[像素]执行一次
。
一般情况下片元的数量远远大于构成三维物体顶点的数量(例如:一个三角形由三个顶点数据构成,光栅化到屏幕上时,却有成百上千个像素点构成),因此片元着色器的执行次数明显大于顶点着色器的执行次数,开发中为提高运行效率,应尽量减小片元着色器的运算量
,将一些复杂运算放在顶点着色器中执行
。
varying(易变变量)
从顶点着色器传递到片元着色器的数据变量
,如顶点着色器所介绍,由系统在顶点着色器后的光栅化阶段自动插值产生。varying变量个数随需求而定,并没有硬性的变量数量上限限制。- gl_FragColor (内建变量):
片元着色器用 gl_FragColor 向OpenGL ES渲染管线写入计算完成的片元颜色值,此颜色值将进入渲染管线的后继阶段继续处理。
片元着色器代码举例
着色器采用OpenGL ES Shading Language编写,为一种类C的编程语言,片元着色器代码实现举例如下:
详细了解着色语言语法,可查看khronos
官方:
OpenGL ES Shading Language 1.00:
https://www.khronos.org/registry/OpenGL/specs/es/2.0/GLSL_ES_Specification_1.00.pdf
// 片元着色器举例(每个片元[像素]执行一次)
precision mediump float;
//接收从顶点着色器过来的参数(具体数值由光栅化阶段差值产生)
varying vec4 vColor;
void main()
{
//片元的最终赋值
gl_FragColor = vColor;
}
varying(易变变量)
例,接收从顶点着色器过来的颜色参数varying vec4 vColor
gl_FragColor
例,片元着色器具体程序中的gl_FragColor = vColor
。
2.7 剪裁测试
如果程序中启用了剪裁测试,OpenGL ES 会检查每个片元在帧缓冲中对应的位置,若对应位置在剪裁窗口中则将此片元送入下一阶段,否则丢弃此片元。。
2.8 深度测试和模板测试
- 深度测试
- 模板测试
a、深度测试
深度测试是在片元处理过程中通过测试一个片元在视椎体
的深度坐标,来判断它是否被更小深度坐标的片断遮挡而决定是否真去绘制它(开启深度测试有助于节省GPU性能);没有深度测试,物体展现与否就会取决于绘制顺序而不是摆放的坐标。
// 在Android中打开深度测试
GLES20.glEnable(GLES20.GL_DEPTH_TEST);
b、模板测试
模板测试的主要功能为将绘制区域限定在一定的范围内,一般用在湖面倒影、镜像等场合。
模板测试涉及到一个词模板缓冲区
(Stencil Buffer)。在模板缓冲中,通常每个模板值(Stencil Value)是8位的,所以每个片元一共能有256种不同的模板值。
使用时,我们可以先将模板缓冲区刷成个某个固定的值,然后进行图像绘制,完成绘制后模板缓冲区中的值可能会被污染[污染],此时开发者就可以选择丢弃或是保留这些被污染的片段,从而构造湖面倒影或者镜像。
2.9 颜色缓冲混合
经过前面的几个阶段,已经产生了候选片元,而候选片元最终要进入帧缓冲成为像素。
在候选片元进入帧缓冲区成为像素的过程中:
- 若未开启了Alpha混合,则当前片元成为屏幕的像素,覆盖掉屏幕上的原有像素;
- 若开启了混合,则根据Alpha值将当前像素与候选片元进行颜色混合得出最终的颜色。
2.10 抖动
抖动是一种简单的操作,是一种在色彩空间较小的设备上展示较大色彩空间的图像的一种方法。
例如:在一个RGB_565
的设备上展示RGB_888
的图像,展示时如果简单进行数据截断位,会造成色彩的失真和生硬。抖动使用一个矩阵,来调整一个像素周围的像素的值,来使人眼产生错觉,而模拟
出原来的色彩。
这种技巧,对于只支持8位或者16位颜色信息的显示系统中非常有用。
// 开启抖动
GLES20.glEnable(GLES20.GL_DITHER);
2.11 帧缓冲
OpenGL ES的物体绘制并不是直接回执在屏幕上的,而是预先在帧缓冲区中进行绘制,每一绘制完成一帧再将绘制的结果交换到屏幕上。因此,每次绘制新的一帧数据时都需要清理缓冲区中的相关数据,否则可能产生错误的显示效果。
三、参考
OpenGL ES Shading Language 1.00:
https://www.khronos.org/registry/OpenGL/specs/es/2.0/GLSL_ES_Specification_1.00.pdf
OpenGL ES 2.0 API:
https://www.khronos.org/registry/OpenGL-Refpages/es2.0/
= THE END =
详解 OpenGL ES 2.x 渲染流程的更多相关文章
- 一文详解 OpenGL ES 3.x 渲染管线
OpenGL ES 构建的三维空间,其中的三维实体由许多的三角形拼接构成.如下图左侧所示的三维实体圆锥,其由许多三角形按照一定规律拼接构成.而组成圆锥的每一个三角形,其任意一个顶点由三维空间中 x.y ...
- 一文详解 OpenGL ES 纹理颜色混合
在OpenGL中绘制的时候,有时候想使新画的颜色和已经有的颜色按照一定的方式进行混合.例如:想使物体拥有半透明的效果,或者绘制叠加光亮的效果,这时候就需要用到OpenGLES混合. 如上图所示,为石头 ...
- 第三节:带你详解Java的操作符,控制流程以及数组
前言 大家好,给大家带来带你详解Java的操作符,控制流程以及数组的概述,希望你们喜欢 操作符 算数操作符 一般的 +,-,*,/,还有两个自增 自减 ,以及一个取模 % 操作符. 这里的操作算法,一 ...
- JDBC详解系列(一)之流程
---[来自我的CSDN博客](http://blog.csdn.net/weixin_37139197/article/details/78838091)--- JDBC概述 使用JDBC也挺长 ...
- 详解PHP的执行原理和流程
简介 先看看下面这个过程: • 我们从未手动开启过PHP的相关进程,它是随着Apache的启动而运行的: • PHP通过mod_php5.so模块和Apache相连(具体说来是SAPI,即服务器应用程 ...
- Android OpenGL ES 离屏渲染(offscreen render)
通常在Android上使用OpenGL ES,都是希望把渲染后的结果显示在屏幕上,例如图片处理.模型显示等.这种情况下,只需要使用Android API中提供的GLSurfaceView类和Rende ...
- Android中measure过程、WRAP_CONTENT详解以及 xml布局文件解析流程浅析
转自:http://www.uml.org.cn/mobiledev/201211221.asp 今天,我着重讲解下如下三个内容: measure过程 WRAP_CONTENT.MATCH_PAREN ...
- 详解OpenGL中的各种变换(投影变换,模型变换,视图变换)(完)——法线变换
前面两节内容已经说完了所有的三种变换.也就是说我们现在程序里面既不需要glLookAt(),也不需要gluPerspective(),这些矩阵我们都可以自己写.然后,再用glMultMatrix()来 ...
- 【详解JavaScript系列】JavaScript之流程语句
一 开篇概述 本讲主要讲解JavaScript流程语句,其大致内容包括如下: 其中,常用的if,while,do..while,for在本片文章就不论述,重点论述for..in..,label,bre ...
随机推荐
- OpenMVG 系列 (2):Image 和 Numeric
OpenMVG 的功能模块由若干核心库组成,本文主要介绍 Image 和 Numeric 两个库 1 Image Image 库包含图像容器 Image<T>.图像IO读写函数 Read ...
- 高校表白App-团队冲刺第十天
今天要做什么 做一个类似于淘宝的小云播报 做了什么 没有完全实现,轮转实现,功能没有 遇到的问题 遇到的问题好多啊,感觉写一天都写不完,我还是好好学习一下再重新写吧
- Day1 Markdown学习!
Markdown学习 标题 一级标题:# (空格)+内容 二级标题:##(空格)+内容 同理可支持到六级标题 字体 Hello,World! 两边两个** 加粗 Hello,World! 两边一个* ...
- Scala学习——函数
一.函数的定义(def) object FunctionApp { //定义函数:方法体内最后一行为返回值,不需要使用return def add(a:Int,b:Int):Int={ a + b } ...
- Leetcode13. 罗马数字转整数Leetcode14. 最长公共前缀Leetcode15. 三数之和Leetcode16. 最接近的三数之和Leetcode17. 电话号码的字母组合
> 简洁易懂讲清原理,讲不清你来打我~ 输入字符串,输出对应整数 ![在这里插入图片描述](https://img-blog.csdnimg.cn/63802fda72be45eba98d9e4 ...
- odoo检查规则
@api.multidef button_cancel(self): for move in self: if not move.journal_id.update_posted: raise Use ...
- 第二十五篇 -- C++宝典中的图书管理系统
此篇文章是基于C++宝典写的图书管理系统,本人对其中的部分做了相应修改,并且以现有格式替代原有格式,使程序更加清晰明了.此程序运行在VS2017上. 系统设计 图书管理系统分为四个模块:图书管理模块. ...
- 大数据开发-Go-数组,切片
new()和make的区别 二者看起来没什么区别,但是他们的行为不同,分别适用于不同的类型 new (T) 为每个新的类型 T 分配一片内存,初始化为 0 并且返回类型为 * T 的内存地址:这种方法 ...
- 一文彻底搞清 Gradle 依赖【转】
来源:曾是放牛娃 www.jianshu.com/p/59fd653a54d2 转自:https://mp.weixin.qq.com/s?__biz=MzA3MDMyMjkzNg==&mid ...
- CTF_论剑场_Web25
点击xiazai后面发现404,没办法打开,抓包也没发现啥,用御剑扫描了下发现还有新的页面 点击会跳转到flag.php这个文件,这里应该才是真正的提交页面 另外前面提示了一个ziidan.txt在s ...