最近在学习一下OpenGL
方面的知识,写博客记录一下。
概念
OpenGL (Open Graphics Library)
是⼀个跨编程语⾔、跨平台的编程图形程序接⼝,它将计算机的资源抽象称为⼀个个OpenGL
的对象,对这些资源的操作抽象为⼀个个的OpenGL
指令.
OpenGL 上下⽂ ( context )
- 在应⽤程序调⽤任何
OpenGL
的指令之前,需要安排⾸先创建⼀个OpenGL
的上下⽂。这个上下⽂是⼀个⾮常庞⼤的状态机,保存了OpenGL
中的各种状态,这也是OpenGL
指令执⾏的基础 -
OpenGL
的函数不管在哪个语⾔中,都是类似C语⾔⼀样的⾯向过程
的函数,本质上都是对OpenGL
上下⽂这个庞⼤的状态机中的某个状态或者对象进⾏操作,当然你得⾸先把这个对象设置为当前对象。因此,通过对OpenGL
指令的封装,是可以将OpenGL
的相关调⽤封装成为⼀个⾯向对象的图形API的 - 由于
OpenGL
上下⽂是⼀个巨⼤的状态机
,切换上下⽂往往会产⽣较⼤的开销,但是不同的绘制模块,可能需要使⽤完全独⽴的状态管理。因此,可以在应⽤程序中分别创建多个不同的上下⽂,在不同线程中使⽤不同的上下⽂,上下⽂之间共享纹理、缓冲区等资源。这样的⽅案,会⽐反复切换上下⽂,或者⼤量修改渲染状态,更加合理⾼效
OpenGL状态机
状态机
是理论上的⼀种机器.这个⾮常难以理解,简单理解为:状态机描述了⼀个对象在其⽣命周期内所经历的各种状态,状态间的转变,发⽣转变的动因,条件及转变中所执⾏的活动。或者说,状态机是⼀种⾏为,说明对象在其⽣命周期中响应事件所经历的状态序列以及对那些状态事件的响应。因此具有以下特点
:
- 有记忆功能,能记住其当前的状态
- 可以接收输⼊,根据输⼊的内容和⾃⼰的原先状态,修改⾃⼰当前状
态,并且可以有对应输出 - 当进⼊特殊状态(停机状态)的时候,不再接收输⼊,停⽌⼯作
渲染
渲染:将图形/图像数据转换成2D空间图像操作叫做渲染(Rendering)
.
顶点数组( VertexArray ) 和 顶点缓冲区( VertexBuffer )
屏幕中的所有图形/图像,都是由这三种图元组合而成的
点
线
三角形
顶点数据存储方式
顶点数组与顶点缓冲区的区别在于顶点数据的存储方式不同
- 存储在内存中,即
【顶点数组】
- 存储在GPU提前分配的显存中,即
【顶点缓冲区】
管线
可以理解为流水线,该流水线有一个固定顺序的操作,需要按着这个顺序一个个执行
固定管线/存储着色器
- 一个已经封装好的
Shader
程序,开发者使用时,只需要传入相应参数,即可快速完成渲染,类似苹果系统中封装好的API - 当固定管线无法完成每个业务时,需要将与业务相关的部分变成可编程,用户根据需要自定义管线来完成业务
- 目前
OpenGL
中可编程的仅有两个程序:顶点着色器、片元着色器
顶点着⾊器 VertexShader
- ⼀般⽤来处理图形每个顶点
变换
(旋转/平移/投影等)- 顶点着⾊器是OpenGL中⽤于计算
顶点属性
的程序。顶点着⾊器是逐顶点运算的程序,也就是说每个顶点数据都会执⾏⼀次顶点着⾊器,当然这是并⾏
的,并且顶点着⾊器运算过程中⽆法访问其他顶点的数据- ⼀般来说典型的需要计算的顶点属性主要包括顶点坐标变换、逐顶点光照运算等等。顶点坐标由⾃身坐标系转换到归⼀化坐标系的运算,就是在这⾥发⽣的。
⽚元着⾊器程序 FragmentShader
- ⼀般⽤来处理图形中每个像素点颜⾊计算和填充
- ⽚段着⾊器是OpenGL中⽤于
计算⽚段(像素)颜⾊
的程序。⽚段着⾊器是逐像素运算的程序,也就是说每个像素都会执⾏⼀次⽚段着⾊器,当然也是并⾏的
GLSL (OpenGL Shading Language)
OpenGL着⾊语⾔(OpenGL Shading Language)
是⽤来在OpenGL中着⾊编程的语⾔,也即开发⼈员写的短⼩的⾃定义程序,他们是在图形卡的GPU (Graphic Processor Unit图形处理单元)
上执⾏的,代替了固定的渲染管线的⼀部分,使渲染管线中不同层次具有可编程性。⽐如:视图转换、投影转换等。GLSL(GL Shading Language)的着⾊器代码分成2个部分:Vertex Shader(顶点着⾊器)
和Fragment(⽚元着⾊器)
光栅化Rasterization
- 是把
顶点数据转换为⽚元
的过程,具有将图转化为⼀个个栅格组成的图象的作⽤,特点是每个元素对应帧缓冲区中的⼀像素。 - 光栅化就是把顶点数据转换为⽚元的过程。⽚元中的每⼀个元素对应于帧缓冲区中的⼀个像素。
- 光栅化其实是⼀种将⼏何图元变为⼆维图像的过程。该过程包含了两部分的⼯作。第⼀部分⼯作:决定窗⼝坐标中的哪些整型栅格区域被基本图元占⽤;第⼆部分⼯作:分配⼀个颜⾊值和⼀个深度值到各个区域。光栅化过程产⽣的是⽚元
- 把物体的数学描述以及与物体相关的颜⾊信息转换为屏幕上⽤于对应位置的像素及⽤于填充像素的颜⾊,这个过程称为
光栅化
,这是⼀个将模拟信号转化为离散信号的过程
纹理
纹理可以理解为图⽚.
⼤家在渲染图形时需要在其编码填充图⽚,为了使得场景更加逼真.⽽这⾥使⽤的图⽚,就是常说的纹理.但是在OpenGL我们更加习惯叫纹理,⽽不是图⽚.
混合(Blending)
如果像素依然没有被剔除,那么像素的颜⾊将会和帧缓冲区中颜⾊附着上的颜⾊进⾏混合,混合的算法可以通过OpenGL的函数进⾏指定。但是OpenGL提供的混合算法是有限的,如果需要更加复杂的混合算法,⼀般可以通过像素着⾊器进⾏实现(性能会⽐原⽣的混合算法差⼀些).
变换矩阵 Transformation
用于图形的平移、旋转、缩放时使用
投影矩阵 Projection
将3D坐标转换为2D屏幕坐标时使用
渲染上屏/交换缓冲区 SwapBuffer
- 渲染缓冲区:可以理解为是系统的资源,例如窗口
- 渲染上屏:将图像直接渲染到窗口对应的渲染缓冲区
总结:GPU 图形渲染流水线的具体实现可分为六个阶段,如下图所示。
- 顶点着色器(Vertex Shader)
- 形状装配(Shape Assembly),又称 图元装配
- 几何着色器(Geometry Shader)
- 光栅化(Rasterization)
- 片段着色器(Fragment Shader)
- 测试与混合(Tests and Blending)
第一阶段,顶点着色器。该阶段的输入是 顶点数据(Vertex Data) 数据,比如以数组的形式传递 3 个 3D 坐标用来表示一个三角形。顶点数据是一系列顶点的集合。顶点着色器主要的目的是把 3D 坐标转为另一种 3D 坐标,同时顶点着色器可以对顶点属性进行一些基本处理。
第二阶段,形状(图元)装配。该阶段将顶点着色器输出的所有顶点作为输入,并将所有的点装配成指定图元的形状。图中则是一个三角形。图元(Primitive) 用于表示如何渲染顶点数据,如:点、线、三角形。
第三阶段,几何着色器。该阶段把图元形式的一系列顶点的集合作为输入,它可以通过产生新顶点构造出新的(或是其它的)图元来生成其他形状。例子中,它生成了另一个三角形。
第四阶段,光栅化。该阶段会把图元映射为最终屏幕上相应的像素,生成片段。片段(Fragment) 是渲染一个像素所需要的所有数据。
第五阶段,片段着色器。该阶段首先会对输入的片段进行 裁切(Clipping)。裁切会丢弃超出视图以外的所有像素,用来提升执行效率。
第六阶段,测试与混合。该阶段会检测片段的对应的深度值(z
坐标),判断这个像素位于其它物体的前面还是后面,决定是否应该丢弃。此外,该阶段还会检查 alpha
值( alpha
值定义了一个物体的透明度),从而对物体进行混合。因此,即使在片段着色器中计算出来了一个像素输出的颜色,在渲染多个三角形的时候最后的像素颜色也可能完全不同。