1.OpenGL上下文
在应用程序调用任何OpenGL
的指令前,都需要先创建一个OpenGL
的上下文。这个上下文是一个非常庞大的状态机,保存了OpenGL
中的各种状态,这也是OpenGL
指令执行的基础。
OpenGL
的函数是类似C
语言一样的面向过程的函数,本质上都是对OpenGL
上下文这个庞大状态机中某个状态或对象进行操作。也可以通过对OpenGL
的封装,将OpenGL
相关指令封装成一个面向对象的图形API
。
OpenGL
上下文是一个非常庞大的状态机,切换上下文往往会产生较大的开销。当遇到不同的绘制模块,需要使用完全独立的状态管理时,可以在应用程序中创建多个上下文,在各线程中分别使用不同的上下文,上下文之间共享纹理、缓存区等资源。这样会比反复切换上下文,或者大量修改渲染状态,更加合理和高效。
2.OpenGL状态机
状态机理论上是一种机器,它记录了一个对象在其生命周期中所经历的各种状态,并能响应状态转变事件来进行对应的输出。具有如下特点:
- 有记忆功能,能记录当前状态。
- 可以接受输入,根据输入的内容修改当前状态,并可作对应的输出。
- 当进入特殊状态(停机状态)时,不再接收输入,停止工作。
前面有提到过OpenGL
上下文是一个庞大的状态机,它对OpenGL
指令的响应可以这么理解:
- 上下文会记录
OpenGL
对象自己的各种状态(如当前所使用的的颜色、是否开启了混合功能等)。 - 当调用
OpenGL
函数时,上下文接收了输入,然后作对应的响应输出。比如,调用glColor3f
函数,则会改变OpenGL
当前颜色状态。 - 退出上下文后,
OpenGL
则进入停止状态,不再接收输入。此时再调用相关函数,OpenGL
都不会作出响应。
(这里的理解有待验证)
3.渲染(Rendering)
将图形/图像数据转换成2D空间图像的操作叫做渲染。
4.顶点数组( VertexArray )和顶点缓存区( VertexBuffer )
画图时一般先画好图像的骨架,然后往骨架里填充颜色。OpenGL
也是一样,先通过顶点数据确定图像的轮廓(比如三角形的三个顶点),但需要注意的是,OpenGL
中的图像都是由图元组成。在OpenGL ES
中,有三种类型的图元:点,线,三角形。
-
在调用绘制方法时,顶点数据可以由内存传入,保存这些顶点数据的内存,称为顶点数组。
-
为了更高性能的绘制,还会提前分配一块
GPU
显存来缓存这些顶点数据,这部分显存称为顶点缓存区。绘制图像时,可以直接从显存中读取顶点数据。
5.管线
在工厂中,将不同的零件按照指定顺序组装成最后的产品,这个过程称之为流水线作业。
在OpenGL
下渲染图形,也会按顺序经历一个一个节点,这样的操作过程称为管线。管线是一个抽象概念,之所以这么称呼,是因显卡按照一个固定的顺序来处理数据,就像水从水管的一端流向另一端,这个顺序是不能被打破的,所以就形象地称为管线。
6.固定管线
在早期的OpenGL
版本,封装了很多种着色器程序块,提供了一段包含光照、坐标转换、裁剪等诸多功能的固定shader程序块,开发者只要传入相应的参数,即可快速完成图形的渲染。这种程序块,称为固定管线。类似于iOS
开发会封装很多API
,⽽我们只需要调⽤,就可以实现功能,不需要关注底层实现原理。
但是由于OpenGL
的使用场景非常丰富,固定管线或存储着色器无法完成每一个业务,所以需要将相关部分开放成可编程。
7.着色器程序Shader
Shader
,中文名着色器,是专门用来渲染图形的一种技术,它的本质是一段程序代码,这段代码的作用是告诉GPU
具体怎么绘制图形的每个顶点及最终的每个像素的颜色。
通过Shader
,我们可以自定义显卡渲染画面的算法,将固定渲染管线架构变为了可编程渲染管线,使画面达到我们想要的效果。
常见的Shader
主要有顶点着⾊器(VertexShader),⽚段(元)着⾊器 (FragmentShader)/像素着⾊器(PixelShader),⼏何着⾊器 (GeometryShader),曲⾯细分着⾊器(TessellationShader)。
直到OpenGL ES 3.0
,依然只支持顶点着色器和片段着色器。
7.1顶点着色器(VertexShader)
- ⼀般⽤来处理图形每个顶点的变换(旋转/平移/投影等)。
- 顶点着色器是
OpenGL
中用于计算顶点属性的程序,是逐顶点运算的。每个顶点数据都会执行一次顶点着色器,这是并行运算的,并且顶点着色器在运算过程中是不能访问其他顶点数据的。 - ⼀般来说典型的需要计算的顶点属性主要包括顶点坐标变换、逐顶点光照运算等。顶点坐标由⾃身坐标系转换到归⼀化坐标系的运算,就是在这⾥发⽣的。
7.2⽚段着⾊器(FragmentShader)
- ⼀般⽤来处理图形中每个像素点的颜⾊计算和填充。
- ⽚段着⾊器是
OpenGL
中⽤于计算⽚段(像素)颜⾊的程序。⽚段着⾊器是逐像素运算的程序,也就是说每个像素都会执⾏⼀次⽚段着⾊器,当然也是并⾏的。 -
逐像素运算的着色器程序,在
DirectX
中,称为像素着色器(PixelShader),在OpenGL
中,称为片段(元)着色器(Fragment Shader)。
在OpenGL
进⾏绘制的时候,⾸先由顶点着⾊器对传⼊的顶点数据进⾏运算,再通过图元装配,得出构成这个图形所需的所有图元。然后进⾏光栅化,将图元这种⽮量图形,转换为栅格化数据。最后,将栅格化数据传⼊⽚段着⾊器中进⾏运算。⽚段着⾊器会对栅格化数据中的每⼀个像素进⾏运算,并决定像素的颜⾊。
8.GLSL (OpenGL Shading Language)
OpenGL
着⾊语⾔,是⽤在OpenGL
中着⾊编程的语⾔,也即开发⼈员写的短⼩的⾃定义程序,他们是在图形卡的GPU
上执⾏的,代替了固定的渲染管线的⼀部分,使渲染管线中不同层次具有可编程性。⽐如:视图转换、投影转换等。GLSL
的着⾊器代码分成2个部分: VertexShader
(顶点着⾊器)和FragmentShader
(⽚元着⾊器)。
9.光栅化(Rasterization)
光栅化是一个将几何图元转变成二维图像的过程。二维图像上每个点都包含颜色、深度、纹理等数据,这个点和相关数据被称为片段/元(Fragment)。
下面以OpenGL绘制一个正方形来介绍光栅化的工作:
-
假设当前屏幕分辨率为
1920×1080
,即屏幕共有1920x1080
个像素点,每个像素点都以一个方形小栅格的形式存在。 -
OpenGL
绘制一个正方形时,传入的顶点数据,经由顶点着色器运算以及图元装配后,会得出构成这个正方形的所有图元。然后再将图元进行光栅化,转换成栅格数据。这个过程中,光栅化主要做下面两部分的工作:
1.确定这两个三角形图元所覆盖的栅格区域。
2.给这些栅格区域分配颜色值和深度值。 -
在后续渲染流程中,片元着色器会对传入的栅格化数据再进行运算,然后给像素点着色。
10.纹理
纹理是用来表现物体表面细节的一幅或几幅二维图形,也称纹理贴图。在渲染图形时,为了使场景更加逼真,有时候需要在图形上映射纹理贴图。瓷砖上凹凸的花纹,以及瓷器上的图案,都可以理解成纹理。
11.混合(Blending)
混合是将一种颜色和另一种颜色组合以获得第三种颜色的行为。在OpenGL
中,混合发生在渲染过程的后期:片段着色器计算出片段的最终输出颜色并写入帧缓冲区时,通常情况下,这片段会覆盖之前所有内容,但如果启用了混合,那么该片段将与之前的片段颜色进行混合。
混合的算法可以通过OpenGL
的函数进⾏指定,但是OpenGL提供的混合算法是有限的,如果需要更加复杂的混合算法,⼀般可以通过片段着⾊器进⾏实现,当然性能会⽐原⽣的混合算法差⼀些。
12.变换矩阵(Transformation)
例如图形想发⽣平移,缩放,旋转变换,就需要使⽤变换矩阵。
13.投影矩阵(Projection)
⽤于将3D坐标转换为⼆维屏幕坐标,实际线条也将在⼆维坐标下进⾏绘制。投影方式有两种:
- 正投影(平行投影):不管远近顺序,按照1:1进行绘制2D图形。
- 透视投影: 按照远小近大的真实比例,绘制3D图形。
14.渲染上屏/交换缓冲区(SwapBuffer)
-
渲染缓冲区⼀般映射的是系统的资源⽐如窗⼝。如果将图像直接渲染到窗⼝对应的渲染缓冲区,则可以将图像显示到屏幕上。
-
但是,值得注意的是,如果每个窗⼝只有⼀个缓冲区,那么在绘制过程中屏幕进⾏了刷新,窗⼝可能显示出不完整的图像,造成画面撕裂。
-
为了解决这个问题,常规的
OpenGL
程序⾄少都会有两个缓冲区。显示在屏幕上的称为屏幕缓冲区,没有显示的称为离屏缓冲区。在⼀个缓冲区渲染完成之后,通过将屏幕缓冲区和离屏缓冲区交换,实现图像 在屏幕上的显示。 -
由于显示器的刷新⼀般是逐⾏进⾏的,为了防⽌交换缓冲区的时候屏幕上下区域的图像分属于两个不同的帧,因此⼀般会等待显示器发出信号才交换,这个信号就被称为垂直同步信号,这个技术被称为垂直同步。
-
使⽤了双缓冲区和垂直同步技术之后,由于总是要等待缓冲区交换之后再进⾏下⼀帧的渲染,使得帧率⽆法完全达到硬件允许的最⾼⽔平。为了解决这个问题,引⼊了三缓冲区技术,在等待垂直同步时,来回交替渲染两个离屏的缓冲区,⽽垂直同步发⽣时,屏幕缓冲区和最近渲染完成的离屏缓冲区交换,充分利⽤硬件性能。
网友评论