内容简介:这是这几个小模块来一一介绍,阅读完本篇内容你将收获:友情提示:该篇文字较多,比较适合对 OpenGL 知之甚少的同学阅读,已经有相关经验的大佬可以溜了:stuck_out_tongue_closed_eyes:,闲言少叙,直入正题。
这是 视觉专题 的第一篇,将分为:
- 什么是 OpenGL
- OpenGL 语法说明
- OpenGL 渲染管线
- OpenGL 程序&渲染流程分析
这几个小模块来一一介绍,阅读完本篇内容你将收获:
- OpenGL 是什么
- OpenGL 渲染管线的工作流程
友情提示:该篇文字较多,比较适合对 OpenGL 知之甚少的同学阅读,已经有相关经验的大佬可以溜了:stuck_out_tongue_closed_eyes:,闲言少叙,直入正题。
注: 该专题默认使用 核心模式 来进行,需要 OpenGL 的版本在3.3以上。
一:什么是 OpenGL
一般它被认为是一个 API (Application Programming Interface, 应用程序编程接口),包含了一系列可以操作图形、图像的函数(通过直接访问图形硬件设备的特性来实现)。事实上,OpenGL 本身并不是一个API,它仅仅是一个由 Khronos 组织制定并维护的规范(Specification)。
OpenGL 规范严格规定了每个函数该如何执行,以及它们的输出值。至于内部具体每个函数是如何实现(Implement)的,将由 OpenGL 库的开发者自行决定(这里开发者通常是显卡的生产商)。
因为 OpenGL 规范并没有规定实现的细节,具体的 OpenGL 库允许使用不同的实现,只要其功能和结果与规范相匹配即可。所以,当你使用 Apple 系统的时候,OpenGL 库是由 Apple 自身维护的。在 Linux 下,有显卡生产商提供的 OpenGL 库,也有一些爱好者改编的版本。这也意味着任何时候 OpenGL 库表现的行为与规范规定的不一致时,基本都是库的开发者留下的bug。
OpenGL 是使用客户端 - 服务端的形式实现的,我们编写的应用程序可以看做客户端,而计算机图形硬件厂商所提供的 OpenGL 实现可以看做服务端。我们编写的 OpenGL 命令,最终会被转换为相关的协议提交给服务端,然后被执行并产生图像内容。
二:OpenGL 语法
OpenGL 库中所有的函数都会以字符“gl”作为前缀,然后是个或多个大写字母开头的词组,以此来命名一个完成的函数(如 glBinVertexArray() )。除此之外你还会看到“glfw”开头的函数,它们来自第三方库GLFW,这是一个抽象化窗口管理和其他系统任务的开发库。类似的,还有“gl3w”开头的函数,它们来自三方库 GL3W 。后续会进一步展开讲这两个库的内容。
与函数命名约定类似,OpenGL 库中定义的常量采用“GL_”开头,通过 #define
来完成常量的定义。为了方便在不同的操作系统之间移植 OpenGL 程序,OpenGL 还为函数定义了不同的数据类型,如 GLfloat
,所以最好统一使用 OpenGL 定义的数据类型,这样就不需要关心系统兼容性问题了。
由于 OpenGL 是一个 C 语言形式的库,因此它不能使用函数的重载来处理不同类型的数据,它通过函数名称的细微变化来实现同一类功能函数集的管理。举个例子: glUniform2f()
与 glUniform3fv()
,前者的后缀 2f
表示这个函数需要两个 GLfloat
类型的参数(以此类推,目前一共定义了24种不同的 glUniform*
()函数),后者的后缀多出的一个 v
是 vector 的缩写,即表示它需要传入一个包含三个 GLfloat
类型元素的一维数组作为参数。
所有可以作为后缀的字母,以及它们所对应的数据类型:
三:OpenGL 渲染管线
早期的(3.3版本以前) OpenGL 使用 立即渲染模式 (Immediate mode,即固定渲染管线):OpenGL的大多数功能都被库隐藏起来,开发者很少能控制 OpenGL 计算的过程。固定渲染管线较容易使用和理解,但是效率太低且不够灵活。
当使用OpenGL的核心模式时,OpenGL 迫使我们使用现代的函数,现代函数具有更高的灵活性和效率性,也能让人更容易清楚 OpenGL 是如何运作的,更好的理解图形编程。但入门门槛也稍有增加。
我们通常所说的 渲染管线 (rendering pipeline),它包含了两个部分:第一部分把你的3D坐标转换为2D坐标,第二部分是把2D坐标转变为实际的有颜色的像素。下图是 OpenGL4.5 版本的 管线 示意图:
OpenGL 首先接收用户提供的几何数据(顶点和几何图元),并且将它输入到一系列着色器阶段中进行处理,然后将处理后的数据送入光栅化单元(rasterizer)。光栅化单元负责对所有剪切区域内的图元生成片元数据,我们可以将一个片元视为一个“候选的像素”,然后对每个生成的片元都执行一个片元着色器,这一步会计算出这个片元的最终颜色。注意,即使在片元着色器中计算出来了一个像素输出的颜色,在渲染多个三角形的时候最后的像素颜色也可能完全不同,还会受 深度 和 混合的影响,这个后面会详细讲。
我们可以通过控制我们需要的着色器来实现自己所需的功能,事实上,只有顶点着色器和片元着色器是必需的,细分和几何是可选的步骤。为了更好理解顶点着色器和片元着色器的分工和区别,可以总结为: 顶点着色(包括细分和几何着色)决定了一个图元应该位于屏幕的什么位置,而片元着色使用这些信息来决定某个片元的颜色应该是什么 。
五:OpenGL 程序&渲染流程分析
无论 OpenGL 的程序写的有多么庞大与复杂,它的基本结构通常都是类似的:
- 初始化物体渲染所对应的状态。
- 设置需要渲染的物体。
在上代码之前,咱们需要对必要的图形学名词有基本的理解。
- 渲染 :计算机从模型到最终的图像创建的过程。OpenGL 只是计算机渲染系统的其中一种,基于光栅化的系统。 Object,IBO. 决定了绘制顶点的顺序,避免顶点数据重复造成的资源浪费。
- OpenGL 状态机 :可以看做一个上下文(context),在调用任何 OpenGL 的指令之前,都需要先创建并进入这样的上下文中,它可以记录自己当前的状态,并能接收新的输入(调用 OpenGL 函数),当关闭了上下文,就不再接收输入。
- 着色器 (Shader) :为 图形渲染管线 中的某个特定部分,将输入转化为输出的程序。在 OpenGL 中,会涉及到六种不同的着色阶段,其中最常用的包括 顶点着色器 以及 片元着色器 ,前者用于处理顶点数据,后者用于处理光栅化后的片元数据。着色器运行在 GPU 上,在 OpenGL 使用它之前,必须经过编译并链接为一个 着色器程序对象 (Shader Program Object)。
一个简单顶点着色器的源代码长这样:
void main(){ gl_Position = ftransform(); } 复制代码
一个简单片元着色器的源代码:
void main() { gl_FragColor = vec4(1.0,0.5,0.2,1.0); } 复制代码
它们都需要用GLSL (OpenGL Shading Language,着色器语言) 。
-
标准化设备坐标(Normalized Device Coordinates, NDC):一旦你的顶点坐标已经在顶点着色器中处理过,它们就应该是标准化设备坐标了,标准化设备坐标是一个x、y和z值在-1.0到1.0的一小段空间。任何落在范围外的坐标都会被丢弃/裁剪,不会显示在你的屏幕上。
-
像素:显示器上最小的可见单元。
-
帧缓存:保存着所有计算机生成的图像的像素点,它是由图形硬件设备管理的一块独立内存区域,可以直接映射到最终的显示设备上。
-
顶点缓冲对象:Vertex Buffer Object, VBO . 管理着在GPU内存(通常被称为显存)中,一块储存着大量顶点数据的内存。因为从CPU把数据发送到显卡相对较慢,所以只要可能我们都要尝试尽量一次性发送尽可能多的数据。
-
顶点数组对象:Vertex Array Object, VAO . 可以像顶点缓冲对象那样被绑定,任何随后的顶点属性调用都会储存在这个 VAO 中。这样的好处就是,当配置顶点属性指针时,你只需要将那些调用执行一次,之后再绘制物体的时候只需要绑定相应的 VAO 就行了。这使在不同顶点数据和属性配置之间切换变得非常简单。
当你打算绘制多个物体时,你首先要生成/配置所有的VAO(和必须的VBO及属性指针),然后储存它们供后面使用。当我们打算绘制物体的时候就拿出相应的VAO,绑定它,绘制完物体后,再解绑VAO。 -
索引缓冲对象:Element Buffer Object,EBO或Index Buffer. 专门储存顶点绘制的索引,OpenGL调用这些顶点的索引来决定该绘制哪个顶点。由于三角形是绘制的基本图形,对于一些复杂图形会存在很多三角形(共用边)的顶点重合问题,该索引就是为了解决此问题,从而降低开销。
我写了一份简单的 OpenGL 示例程序 给你作参考,附有比较详细的渲染流程分析,需要的同学自取,这里就不占篇幅粘代码了。用 Xcode 打开程序直接 Run, 就可以看到该效果:
这份 Demo 使用了可编程渲染管线,自定义了简单的着色器,初学者不需要搞清楚每一步的原理,先对 OpenGL 的语法和基本结构有个初步的了解就行。
总结
枯燥的概念和晦涩的专业术语很容易让初学者望而生畏,所以此篇我只选了一些必要的概念,做了简要的介绍。建议结合文中提供的源码,把上面涉及到的步骤一一拆解,进而加深对相关概念以及 OpenGL 工作流程的理解。
近来因工作和身体原因,文章更新滞后了两周有余,现已恢复正常。
下一篇文章将使用 固定管线 来完成一些动画效果,顺带介绍涉及到的 OpenGL 知识,这样更好理解。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。