推荐
OpenGL 经典两本书红宝书和蓝宝书,其中蓝宝书更适合初学者
《OpenGL超级宝典 第5版》
《OpenGL编程指南(英文第八版)》
前置概念解释
1.OpenGL与OpenGL ES 的区别
OpenGL针对PC端,是Sun公司开发的一套三维图形应用程序接口库,软件开发者借助OpenGL可以实现复杂的三维图形变换。
OpenGL ES (OpenGL for Embedded Systems)是OpenGL三维图形API的子集,针对手机,PAD和游戏主机等嵌入式设备而设计。
2.DirectX windows 多媒体处理框架
3.metal 2014年推出, 苹果内核逐渐使用的三维图形框架
4.图形API目的是解决什么问题?
1)音视频开发中,对于视频解码后的数据渲(ijkplayer,kxmovie等等)
2)地图数据渲染
3)核心动画绘制
4)加滤镜效果
5)游戏人物场景的渲染
6)离屏渲染
解决问题的本质是操作GPU芯片。
前言
在开始这段旅程之前我们先了解一下OpenGL到底是什么。一般它被认为是一个API (应用程序编程接口),包含了一系列可以操作图形、图像的函数。然而,OpenGL本身并不是一个API,它仅仅是一个由Khronos组织制定并维护的规范(Specification)。
实际的OpenGL库的开发者通常是显卡的生产商。你购买的显卡所支持的OpenGL版本都为这个系列的显卡专门开发的。当你使用Apple系统的时候,OpenGL库是由Apple自身维护的。在Linux下,有显卡生产商提供的OpenGL库,也有一些爱好者改编的版本。这也意味着任何时候OpenGL库表现的行为与规范规定的不一致时,基本都是库的开发者留下的bug。
概念解析
一、状态机
OpenGL自身是一个巨大的状态机(State Machine):一系列的变量描述OpenGL此刻应当如何运行。OpenGL的状态通常被称为OpenGL上下文(Context)。我们通常使用如下途径去更改OpenGL状态:设置选项,操作缓冲。最后,我们使用当前OpenGL上下文来渲染。
假设当我们想告诉OpenGL去画线段而不是三角形的时候,我们通过改变一些上下文变量来改变OpenGL状态,从而告诉OpenGL如何去绘图。一旦我们改变了OpenGL的状态为绘制线段,下一个绘制命令就会画出线段而不是三角形。
当使用OpenGL的时候,我们会遇到一些状态设置函数(State-changing Function),这类函数将会改变上下文。以及状态使用函数(State-using Function),这类函数会根据当前OpenGL的状态执行一些操作。只要你记住OpenGL本质上是个大状态机,就能更容易理解它的大部分特性。
二、上下文
在应⽤程序调⽤任何OpenGL的指令之前,首先需要创建⼀个OpenGL的上下文。这个上下⽂是⼀个⾮常庞⼤的状态机,保存了了OpenGL中的各种状态,这也是OpenGL指令执⾏的基础。
OpenGL的函数不管在哪个语⾔中,都是类似C语⾔一样的面向过程的函数。本质上都是对OpenGL上下⽂这个庞⼤的状态机中的某个状态或者对象进行操作。通过对 OpenGL指令的封装,可以将OpenGL的相关调⽤封装成为⼀个⾯向对象的图形API。
由于OpenGL上下⽂是⼀个巨大的状态机,切换上下文往往会产生较⼤的开销,但是不同的绘制模块,可能需要使⽤完全独立的状态管理。因此,可以在应⽤程序中分别创建多个不同的上下文,在不同线程中使⽤不同的上下文,上下⽂之间共享纹理、缓冲区等资源。这样的方案,会⽐反复切换上下⽂,或者⼤量修改渲染状态,更加合理高效。
重点:
OpenGL指令执⾏的基础,是⼀个⾮常庞⼤的状态机。
OpenGL上下文切换开销大,虽然可能使用多个上下文,但上下文之间会共享纹理、缓冲区等资源。
三、渲染
将图形/图像数据转换成3D空间图像操作叫做渲染(Rendering)。
四、顶点数组 (VertexArray)
这顶点指的是我们在绘制一个图形时,它的顶点位置数据,而这个数据可以直接存储在数组中或者将其缓存到GPU内存中
顶点数组指定每个顶点的属性,是保存应用程序地址空间的缓冲区。他们作为顶点缓冲对象的基础,提供指定顶点属性数据的一个高效、灵活的手段
顶点数据就是图像的轮廓。OpenGL中的图像都是由图元组成。在OpenGL ES中,有3种类型的图元:点、线、三⻆形。
在调⽤绘制⽅法的时候,直接由内存传入顶点数据,也就是说这部分数据之前是存储在内存当中的,被称为顶点数组(VertexArray)。
五、顶点缓存区(VertexBuffer)
缓冲区保存在GPU内存中, 可以在CPU不介入的情况下,完成数据操作。
性能更高的做法是,提前分配⼀块显存,将顶点数据预先传⼊到显存当中。这部分的显存,就被称为顶点缓冲区(VertexBuffer)。
顶点数组指定的顶点数据保存在客户内存(CPU)中。在进行glDrawArrays或者glDrawElements等绘图调用时,这些数据必须同客户内存复制到图形内存。
没必要每次绘图时都复制顶点数据,而是在图形内存中缓存这些数据,这样可以显著改善渲染性能,也可以降低内存带宽和电力消耗需求。这就是顶点缓冲区对象发挥作用的地方
六、位图(OpenGL 也称为纹理)
位图图像(bitmap),亦称为点阵图像或栅格图像,是由称作像素(图片元素)的单个点组成的。这些点可以进行不同的排列和染色以构成图样。当放大位图时,可以看见赖以构成整个图像的无数单个方块。扩大位图尺寸的效果是增大单个像素,从而使线条和形状显得参差不齐。然而,如果从稍远的位置观看它,位图图像的颜色和形状又显得是连续的。用数码相机拍摄的照片、扫描仪扫描的图片以及计算机截屏图等都属于位图。位图的特点是可以表现色彩的变化和颜色的细微过渡,产生逼真的效果,缺点是在保存时需要记录每一个像素的位置和颜色值,占用较大的存储空间。
七、管线
在OpenGL下渲染图形,就会经历⼀个⼀个的节点。而这样的操作可以理理解管线。就像一个流⽔线,任务按照先后顺序依次执行。管线是⼀个抽象的概念,之所以称之为管线是因为显卡在处理数据的时候是按照一个固定的顺序来的,而且严格按照这个顺序。
八、固定管线 和 可编程管线
在早期的OpenGL版本,封装了多种着色器程序块,内置了一段包含了光照、坐标变换、裁剪等诸多功能的固定shader程序来完成。来帮助开发者来完成图形的渲染。开发者只需要传入相应的参数,就能快速完成图形的渲染。类似于iOS开发会封装很多API,而我们只需要调⽤,就可以实现功能,不需要关注底层实现原理。
但是由于OpenGL的使⽤场景⾮常丰富,固定管线或存储着⾊器⽆法完成每一 个业务,这时将相关部分开放成可编程。
九、着色器(Shader)
着色器(Shader)是运行在GPU上的小程序。这些小程序为图形渲染管线的某个特定部分而运行。从基本意义上来说,着色器只是一种把输入转化为输出的程序。着色器也是一种非常独立的程序,因为它们之间不能相互通信;它们之间唯一的沟通只有通过输入和输出。
OpenGL在实际调⽤绘制函数之前,还需要指定⼀个由shader编译成的着色器程序。常见的着⾊器主要有顶点着⾊器(VertexShader),⽚段着⾊器(FragmentShader)/像素着⾊器(PixelShader),几何着⾊(GeometryShader),曲⾯细分着⾊器(TessellationShader)。片段着⾊器和像素着⾊器只是在OpenGL和DX中的不同叫法而已。可惜的是,直到OpenGL ES 3.0,OpenGL ES依然只⽀持顶点着⾊器和片段着⾊器这两个最基础的着⾊器。
OpenGL在处理理shader时,和其他编译器一样。通过编译、链接等步骤,⽣成了着⾊器程序(glProgram),着⾊器程序同时包含了顶点着⾊器和⽚段着⾊器的运算逻辑。在OpenGL进行绘制的时候,⾸先由顶点着⾊器对传⼊的顶点数据进⾏运算。再通过图元装配,将顶点转换为图元。然后进⾏光栅化,将图元这种⽮量图形,转换为栅格化数据。最后,将栅格化数据传入⽚段着⾊器中进⾏运算。⽚段着⾊器会对栅格化数据中的每一个像素进行运算,并决定像素的颜⾊。
十、顶点着色器(VertexShader)
顶点着色器对顶点实现了一种通用的可编程方法,它一般用来处理图形每个顶点变换(旋转/平移/投影等)
顶点着色器是OpenGL中用于计算顶点属性的程序。每个顶点都会执行依次顶点着色器,执行顺序是按照存储在顶点数组的顺序依次处理(一般是逆时针)。
十一、片元着色器(FragmentShader)/ 像素着⾊器(PixelShader)
片元着色器主要是对光栅化处理后生成的片元逐个进行处理(并行)。接收顶点着色器输出的值,需要传入的数据,以及它经过变换矩阵后输出值存储位置。
十二、GLSL
GLSL着色语⾔是⽤来在OpenGL中着⾊编程的语⾔,是在图形卡的GPU上执⾏的。代替了固定的渲染管线的⼀部分,使渲染管线中不同层次具有可编程性。⽐如:视图转换、投影转换等。GLSL(GL Shading Language)的着⾊器代码分成2个部分: Vertex Shader(顶点着⾊器)和Fragment(⽚断着⾊器)。
十三、光栅话(Rasterization)
光栅化(Rasterization)是把顶点数据转换为片元的过程,具有将图转化为一个个栅格组成的图象的作用,特点是每个元素对应帧缓冲区中的一像素。
光栅化其实是一种将几何图元变为二维图像的过程。该过程包含了两部分的工作。
第一部分工作:
决定窗口坐标中的哪些整型栅格区域被基本图元占用
第二部分工作:
分配一个颜色值和一个深度值到各个区域。光栅化过程产生的是片元
把物体的数学描述以及与物体相关的颜色信息转换为屏幕上用于对应位置的像素及用于填充像素的颜色,这个过程称为光栅化,这是一个将模拟信号转化为离散信号的过程
简而言之,光栅化阶段绘制对应的图元(点、线、三角形),将图元
转化为一组二维数组
的过程,然后传递给片元着色器
处理。这些二维数组代表屏幕上绘制的像素
十三、纹理
纹理可以理解为图⽚。 在渲染图形时需要在顶点围成的区域中填充图⽚,使得场景更加逼真。⽽这⾥使⽤的图⽚,就是常说的纹理。只是在OpenGL,我们更加习惯叫纹理,⽽不是图⽚。
十四、混合(Blending)
Blend 混合是将源色和目标色以某种方式混合生成特效的技术。混合常用来绘制透明或半透明的物体。
在混合中起关键作用的α值实际上是将源色和目标色按给定比率进行混合,以达到不同程度的透明。α值为0则完全透明,α值为1则完全不透明。混合操作只能在RGBA模式下进行,颜色索引模式下无法指定α值。物体的绘制顺序会影响到OpenGL的混合处理。
十五、矩阵
变换矩阵(Transformation)
例如图形想发⽣平移、缩放、旋转等变换,就需要使用变换矩阵。
投影矩阵(Projection)
⽤于将3D坐标转换为⼆维屏幕坐标,实际线条也将在二维坐标下进行绘制。