Open GL 初探

OpenGL(Open Graphics Library):图形硬件的软件接口,将计算机的资源抽象成一个OpenGL对象。对OpenGL对象的操作就是对这些资源的整合和操作。它具有强大的跨编程语言和跨平台的兼容性。

跨编程语言:OpenGL官方网页中列出了用于Java、Fortran 90、Perl、Pike、Python、Ada和Visual Basic的多个绑定。

跨平台:Windows、Mac OS、Linux、Unix。

OpenGL操作的是GPU芯片,关于窗口系统的view、window并不是OpenGL来操作,而是相对的iOS或Android系统来完成的。

OpenGL是一种图形应用程序编程接口,简单理解就是开发图形库。

    可以用来做:    

    视频、图形、图片处理;    

    2D、3D游戏引擎开发  

    科学可视化  

    医学软件开发  

    CAD(计算机辅助技术)  

    虚拟实境(AR、VR)  

    AI人工智能

OpenGL ES(OpenGL for Embedded Systems)是OpenGL的子集。针对移动终端或游戏主机等嵌入式设备而设计的,去除了很多不必要和性能较低的API接口。

现在主要 有两个版本,OpenGL ES 1.x 针对固定管线硬件的,OpenGL ES 2.x 针对可编程管线硬件。OpenGL ES 1.0 是以 OpenGL 1.3 规范为基础的,OpenGL ES 1.1 是以 OpenGL 1.5 规范为基础的,它们分别又支持 common 和 common lite 两种profile。lite profile只支持定点定点实数,而common profile既支持定点数又支持浮点数。 OpenGL ES 2.0 则是参照 OpenGL 2.0 规范定义的,common profile发布于2005-8,引入了对可编程管线的支持。

支持平台、机型: 

- 支持iPad, iPhone3GS 和后续版本,以及iPodTouch3代和后续版本。

- 支持Android平台从Android 2.2版本开始。

- 支持Android NDK从Android 2.0版本开始。

- 支持BlackBerryPlayBook黑莓。

- 支持Pandora潘多拉控制台的3D库。

- 被WebGL支持:浏览器支持OpenGL 

- 支持少数新款Nokia诺基亚手机,比如N900上的Maemo和N8上的Symbian3塞班3系统。  

- 支持多款三星手机,包括Galaxy S和Wave。  

- 使用开发插件可以支持Palm webOS。   

- 支持Archos 爱可视上网本:70 IT, 101 IT。

Metal:在WWDC 2014 上,Apple为游戏开发者推出了新的平台技术 Metal,该技术能够为 3D 图像提高 10 倍的渲染性能,并支持大家熟悉的游戏引擎及公司

相对于OpenGL而言,Metal在iOS端的图形渲染能力上做到了极致。是一种低层次的渲染应用程序编程接口,提供了软件所需的最低层,保证软件可以运行在不同的图形芯片上。Metal 提升了 A7 与 A8 处理器效能,让其性能完全发挥。 可以这样理解,Metal相对于OpenGL ES来说就是swift相对于objective-C。尽管苹果大力推崇swift,但是还是有大部分开发者喜欢使用objective-C,甚至在2018年编程语言排名中,objective-C强势反弹回前十。Metal虽然在极致渲染方面强于OpenGL,但是兼容性能不足,市场占有率和对于开发者的学习吸引力可能弱于OpenGL。只能说未来可期吧~

在应用程序调用任何OpenGL指令之前,需要安排首先创建一个OpenGL的上下文。这个上下文是一个非常庞大的状态机,保存了OpenGL中的各种状态,这也是OpenGL指令执行的基础。每一个硬件GPU是个服务器,每一个绘制上下文对应于申请的一个客户端,一个客户端维护着一套状态机,如果两个窗口分别对应两个不同的绘制上下文,则两个窗口彼此状态独立。

OpenGL的函数不管在哪个语言中,都是类似C语言一样的面向过程的函数,本质上面都是对OpenGL上下文这个庞大的状态机中的某个状态或者对象进行操作。通过对OpenGL指令的封装,是可以将OpenGL的相关调用封装成为一个面向对象的图形API的。

OpenGL上下文(Context)

由于OpenGL上下文是一个巨大的状态机,切换上下文往往会产生较大的开销。但是不同的绘制模块,可能需要使用完全独立的状态管理。因此,可以在应用程序中分别创建多个不同的上下文,在不同的线程中使用不同的上下文,上下文之间共享纹理、缓冲区等资源。这样的方案,会比反复切换上下文,或者大量修改渲染状态,更加合理高效的。

OpenGL状态机

状态机理论上是一种机器,其实我们可以这样去理解,状态机描述了一个对象在其生命周期中所经历的各种状态,状态之间的转变,发生转变的动因,条件以及转变中所执行的活动。

   为什么说OpenGL是状态机?

1. OpenGL可以记录自己的状态(比如:当前所使用的颜色、是否开启了混合功能,等等,这些都是要记录的)

2. OpenGL可以接收输入(当我们调用OpenGL函数的时候,实际上可以看成OpenGL在接收我们的输入),根据输入的内容和自己的状态,修改自己的状态,并且可以得到输出(比如我们调用glColor3f,则OpenGL接收到这个输入后会修改自己的“当前颜色”这个状态;我们调用glRectf,则OpenGL会输出一个矩形)

3. OpenGL可以进入停止状态,不再接收输入。这个可能在我们的程序中表现得不太明显,不过在程序退出前,OpenGL总会先停止工作的。 

渲染(Rendering)

将图形/图像数据转换成3D空间图像操作叫做渲染。例如,在图片或者视频进行解码之后,形成了一大堆的二进制文件,然后我们将这一堆的二进制文件显示到屏幕上面的过程就可以理解为渲染

顶点数据(Vertex Data)

一个顶点是一个3D坐标(也就是x、y、z数据)。三个3D坐标组成一个三角形。而顶点数据是用顶点属性(Vertex Attributes)表示的,它可以包含任何我们希望用的数据。

顶点数组(VertexArray)

顶点是我们在绘制一个图形的时候,顶点的位置数据,这个数据是可以直接存储在数组中或者将其缓存在GPU内存(栈区)中的。顶点数组就是我们在画画的时候,最开始画的一个大致的骨架。在OpenGL中的图像都是由图元组成。在OpenGL ES中,有三种图元:点、线、三角形。我们通过设定函数的指针,将顶点数据存储在内存中,然后需要绘制的时候,直接从内存中取出来使用。这一部分的数据其实就是顶点数组

顶点缓冲区(VertexBuffer)

我们上面说了,我们在调用绘制方法的时候,直接就由内存传入顶点数据。还有一种更加高性能的方法,就是提前分配一快内存,将顶点数据预先传入到显存当中,这部分的显存,就叫做顶点缓冲区。值得注意的是,这一块空间不再内存中,而是在显存的一块空间中。

管线

因为我们的GPU在处理数据的时候,是通过一个固定的顺序来的,这个顺序不能被打破。类似一个流水线的形式,所以被称之为管线。

固定管线/存储着色器

在早期的OpenGL版本,它封装了很多着色器程序块内置的一段包含了光照、坐标变换、裁剪等等诸多功能的固定shader程序来完成,来帮助开发者来完成图形的渲染。而开发者只需要传入相应的参数,就能快速完成图形的渲染,类似于iOS开发会封装很多的API。而我们只需要调用,就可以实现功能,不需要关注底层实现原理。OpenGL的使用场景非常丰富,固定管线或存储着色器无法完成所有业务,所以将相关部分开放成可编程状态。

着色器程序(Shader)

将固定渲染管线架构变成可编程渲染管线。OpenGL在实际调⽤绘制函数之前,还需要指定一个由shader编译成的着色器程序。

常见的着色器主要有:    

    顶点着⾊器(VertexShader)    

    ⽚段着⾊器 (FragmentShader)/像素着⾊器(PixelShader)/片元着色器/图元着色器 

    ⼏何着⾊器 (GeometryShader) 

     曲⾯细分着⾊器(TessellationShader)⽚段着⾊器和像素着⾊器只是在OpenGL和DX中的不同叫法⽽而已。可惜的是,直到 OpenGLES 3.0,依然只支持了顶点着⾊器和片段着⾊器这两个最基础的着⾊器。

OpenGL在处理shader时,和其他编译器一样。通过编译、链接等步骤,⽣成了着⾊器程序(glProgram),着⾊器程序同时包含了顶点着⾊器和⽚段着⾊器的运算逻辑。

在OpenGL进行绘制的时候,⾸先由顶点着⾊器对传⼊的顶点数据进行运算。再通过图元装配,将顶点转换为图元。然后进行光栅化,将图元这种矢量图形,转换为栅格化数据。最后,将栅格化数据传入⽚段着⾊器中进行运算。⽚段着⾊器会对栅格化数据中的每一个像素进行运算,并决定像素的颜⾊。

顶点着色器(VertexShader)

一般用来处理图形每个顶点变换,即:旋转/平移/投影等。

顶点着色器是OpenGL中用于计算顶点属性的程序。顶点着色器是逐个顶点运算的程序,也就是说每个顶点数据都会执行一次顶点着色器,当然这是并行的,并且顶点着色器运算过程中无法访问其他顶点的数据

一般来说典型的需要计算的顶点属性包括顶点坐标变换、逐个顶点光照运算等等。顶点坐标由自身坐标系转换到归一化做标记的运算,就是在这里发生的。

属性 : 用顶点数组提供的逐顶点数据;

统一变量和统一变量缓冲区 : 顶点着色器使用的不变数据;

采样器 : 代表顶点着色器使用的纹理的特殊统一变量类型;

着色器程序 : 顶点着色器程序源代码或者描述在操作顶点的可执行文件。

片元着色器 (FragmentShader)

一般用来处理图形中每个像素点颜色计算和填充

片段着色器是OpenGL中用于计算片段(像素)颜色的程序。片段着色器是逐个像素运算的程序,也就是说每个像素都会执行一次片段着色器,当然也是并行的。

片元着色器是一个处理片元值及其相关联数据的可编程单元,片元着色器可执行纹理的访问、颜色的汇总、雾化等操作,每片元执行一次。片元着色器替代了纹理、颜色求和、雾以及Alpha测试,这一部分是需要开发者自己开发的。GLSL(OpenGL Shading language)

光栅化(Rasterization)

官方翻译成栅格化或者像素化。光栅化就是把顶点数据转换为片元的过程,实际绘制或填充每个定点之间的像素行程过程。片元中的每一个元素对应于帧缓冲区中的一个像素。该过程包含了两部分的工作。第一部分工作:决定了窗口坐标中哪些整型格栅区域被基本图元占用。第二部分工作:分配一个颜色值和一个深度值到各个区域。光栅化过程产生的是片元。

把物体的数学描述以及与物体相关的颜色信息转换为屏幕上用于对应位置的像素及⽤于填充像素的颜色,这个过程称为光栅化,这是一个将模拟信号转化为离散信号的过程。

纹理

纹理可以理解为一个图片,也就是位图。⼤家在渲染图形时需要在其编码填充图⽚,为了使得场景更加逼真.⽽这里使⽤的图片,就是常说的纹理.但是在OpenGL,我们更加习惯叫纹理,⽽不是图片。

OpenGL要求纹理的高度和宽度都必须是2的n次方大小,只有满足这个条件,这个纹理图片才是有效的。一般使用uv来表示纹理坐标,uv是一个二维向量(u,v),u和v的取值从0到1。我在代码中为每个顶点数据增加了2个GLFloat来表示uv的值。

使用OpenGL函数生成纹理,除了使用GLKit生成纹理之外,还可以直接使用OpenGL生成纹理。

1、将图片的数据以RGBA的形式导出;

2、使用glGenTextures生成纹理,这里生成的纹理就相当于上面说到的self.diffuseTexture.name;

3、使用glBindTexture绑定纹理到GL_TEXTURE_2D;

4、使用glTexImage2D写图片数据,我们的图片数据已经统一导出成RGBA格式了,所以颜色格式参数使用GL_RGBA。每个颜色组件参数使用GL_UNSIGNED_BYTE,就是说R,G,B,A每个数据各占一个字节的大小;

5、使用glTexParameteri设置采样方式和重复方式,每个方式具体的效果大家可以自行修改例子观察一下。重复方式主要用于uv超出0到1的场景;

6、glBindTexture(GL_TEXTURE_2D, 0);是为了清空GL_TEXTURE_2D绑定的数据,可以把GL_TEXTURE_2D理解为一个工作台,你处理完了你的事情需要把工作台清理干净。

混合(gl_blend)

在测试阶段之后,如果像素依然没有被剔除,那么像素的颜色将会和帧缓冲区中颜色附着上的颜色进行混合,混合的算法可以通过OpenGL的函数进行指定。但是OpenGL提供的混合算法是有限的,如果需要更加复杂的混合算法,⼀般可以通过像素着⾊器进行实现,当然性能会比原生的混合算法差一些。

混合常用来绘制透明或半透明的物体。在混合中起关键作用的α值实际上是将源色和目标色按给定比率进行混合,以达到不同程度的透明。α值为0则完全透明,α值为1则完全不透明。混合操作只能在RGBA模式下进行,颜色索引模式下无法指定α值。物体的绘制顺序会影响到OpenGL的混合处理。

glEnable( GL_BLEND );   // 启用混合

glDisable( GL_BLEND );  // 禁用关闭混合

矩阵变换(Transformation)

图形想发生平移,缩放,旋转变换。就需要使用变换矩阵。

Peakin 来源:CSDN 原文:https://blog.csdn.net/u014587123/article/details/80337627

模型变换:

    模型变换解决的是,把物体在世界坐标系下的位置拆分成平移、缩放、旋转的表达方式。 

视图变换:

    视图变换的作用可以理解为,指定一个照相机的位置和角度。

投影变换:

    投影变换是把前面在三维空间中的坐标投影到二维屏幕坐标,但是计算结果也是一个三维坐标(严格来说是四维的,还有个齐次的 1),除了屏幕的横纵坐标之外,另一个维度就是垂直屏幕方向上的深度坐标,就是之后可以写入深度缓冲器的值。

视口变换:

    视口变换,这里只是一个非常简单的 X-Y 平面上的缩放,它决定了最终渲染到屏幕上的哪一块区域。

变换结果:

    变换后的坐标 = 视口矩阵 x 投影矩阵 x 视图矩阵 x 模型矩阵 x 模型点坐标 其中模型点坐标和变换后的坐标是 1x4 的向量,其他矩阵都是 4x4 的。 规定:OpenGL的向量为列向量,矩阵和向量采用的方式又是“矩阵X列向量”的方式,当多个矩阵乘以向量时,应该从右向左计算。故上式的计算顺序为模型点坐标先与模型矩阵相乘,再与视图矩阵相乘,投影矩阵和视口矩阵,最后得到变换后的坐标。

投影矩阵(Projection)

负责给场景增加透视;⽤于将3D坐标转换为二维屏幕坐标,实际线条也将在二维坐标下进行绘制。

投影矩阵P:满足P^2=P

正交矩阵矩阵P:P'=P=P^2

超定线性方程组Ax=b通常化成解PAx=Pb,其中P是全空间到A的值域Im(A)的投影,经等价变换可得A'Ax=A'b

在线性代数和泛函分析中,投影是从向量空间映射到自身的一种线性变换,是日常生活中“平行投影”概念的形式化和一般化。同现实中阳光将事物投影到地面上一样,投影变换将整个向量空间映射到它的其中一个子空间,并且在这个子空间中是恒等变换。

渲染上屏/交换缓冲区(SwapBuffer)

常规的OpenGL程序⾄至少都会有两个缓冲区。显示在屏幕上的称为屏幕缓冲区,没有显示的称为离屏缓冲区。在一个缓冲区渲染完成之后,通过将屏幕缓冲区和离屏缓冲区交换,实现图像在屏幕上的显示。

当我们想把一个图像渲染到窗口的时候,GPU会开辟一个渲染缓冲区。但是每一个窗口又只有一个缓冲区,那么如果在绘制的过程中屏幕进行了刷新,窗口显示的画面就有可能不完整。为了解决这个问题,常规的OpenGL程序至少都会有两个缓冲区。显示在屏幕上的称为屏幕缓冲区,没有显示的称为离屏缓冲区,在一个缓冲区渲染完成之后,通过将屏幕缓冲区和离屏缓冲区交换,实现图像在屏幕上的显示。在iOS中经常遇到的离屏渲染,其实就是双缓冲区的机制引起的。

你可能感兴趣的:(Open GL 初探)