现代OpenGL介绍(一)

2013-12-28

第一章:绘制管线

OpenGL的诞生有很长时间了,读过互联网上成百上千的文档后,很难明确哪一部分是老的内容,哪一部分在现代图形硬件上仍然可以继续使用。是时候对如今仍在使用的OpenGL做一次新的介绍了。

什么是OpenGL

Wikipedia 对OpenGL的用途和历史做了一个全面的回顾,但是这里我还是简要的总结一下。在现代的体系中,OpenGL是一个和可编程GPU进行交互的跨平台程序库,用于绘制实时3D图形。在游戏、CAD和数据可视化程序中被广泛使用。OpenGL最早出现在90年代,是作为一个跨平台SGI专用图形库的标准,SGI专用图形库用于在其高端工作站上驱动图形硬件。

几年以后,GLQuake和3dfx的Voodoo图形加速卡渐成主流,OpenGL也成为除了Microsoft专用Direct3d程序库以外,用于操作普通PC机上图形加速卡的又一个标准。近几年来,Khronos组织管理OpenGL标准,不断更新使之可以支持现代可编程GPU,并通过OpenGL ES和WebGL将它扩展至移动和网络领域。OpenGL 3抛弃了那些老版本中变得混乱的过时特性,让OpenGL越发精简流畅。

最近的另一个发展是通用GPU(GPGPU)库,包括NVIDIA的CUDA技术和Khronos的OpenCL。这些库通过一种类C语言,增加了数据并行特性,让GPU能用于普通计算,而不必在OpenGL的图形框架之下。然而,这些GPGPU的框架并没有取代OpenGL;因为他们的主要目的不是图形编程,它们只是提供了访问GPU的运算单元,而忽视专用的图形硬件。虽然这样做是可行的,但作为OpenGL的配件,支持CUDA和OpenCL既可以分享GPU与OpenGL的内存缓冲区,又可以在GPGPU程序和图形管线之间传递数据。 GPGPU不在本次介绍之列,我的重点是使用OpenGL完成图形(编程)。

对于这些教程,假设你已经是一个程序员,知道C语言,但之前不一定见过OpenGL或进行图形编程。了解一些基本的代数和几何知识将有很大的帮助。我会覆盖OpenGL 2.0避免讨论在OpenGL 3或者OpenGL ES中不推荐或者抛弃的任何API。在讲完基础知识以后,我会谈谈OpenGL 3和4的一些新功能。除了OpenGL,我将使用两个辅助库:GLUT(GL实用工具包),它提供了一个跨平台的OpenGL和窗口系统接口,GLEW(GL扩展),它简化了不同版本的OpenGL及其扩展。

从哪里获得OpenGL, Glut 和Glew

OpenGL的标准以某种形式存在于MacOS X,Windows和大多数Linux发行版中。如果你想继续学习这些教程,你需要确保你的OpenGL实现支持至少2.0。 MacOS X系统即使图形卡驱动程序不提供,也能通过软件实现OpenGL 2.0。在Windows上,取决于你的图形卡驱动程序提供OpenGL 2或更高版本。你可以使用RealTech免费的OpenGL扩展查看器,看看你的驱动程序所支持的OpenGL版本。近4年来NVIDIA和AMD发行的图形卡所提供的驱动至少支持OpenGL 2.0。Intel板载图形卡以及老的显卡用户没有那么幸运。对于后者,Mesa提供了一个开源、跨平台的OpenGL 2.1软件实现,在Windows和几乎所有的Unix平台上都可以运行。

Mesa是最常见的Linux中OpenGL实现,它和X服务使用“直接渲染接口”(DRI)通过图形硬件与OpenGL交互。你可以通过运行一个xterm的glxinfo命令来查看您的DRI驱动程序是否支持OpenGL 2.0。如果不支持,你可以禁用该驱动程序,使用Mesa的软件实现。 nVidia对于他们自己的GPU也提供专有的针对Linux的OpenGL实现,对于目前的NVidia图形卡,都能支持OpenGL 2.0或以后更高版本。

要安装GLUT和GLEW,在它们各自的网站上找到二进制包。MacOS X预装了GLUT。大多数Linux发行版都可以在其打包系统资源得到GLUT和GLEW。对于GLUT你可能还需要开启你的发行版中 “非自由”套件库选项,因为它的许可并不是技术开源。如果你对它要求很高,有一个开源的GLUT克隆:OpenGLUT可以使用。(译者注:我没有linux的编程经验,有不准确的请见谅)

如果你是一位经验丰富的C语言程序员,你应该能够安装这些库,并让他们在你的开发环境中顺畅工作。在我们开始编写代码之前,我要讲一些笼统的概念。在这第一章,我要解释图形管线和数据流的绘制工作。在下一章,我们将编写一个简单的“Hello World”程序,将图像文件绘制到屏幕上,显示该管线是如何工作的。

图形管线

早期的实时三维,三角形是场景绘制的一切。虽然现代图形处理器可以执行各种花哨的效果来掩盖这个秘密,但在所有的效果之下,三角形仍然是GPU工作的介质。 OpenGL的图形管线反映了这一点:主程序填充OpenGL顶点数组管理的内存缓冲区,这些顶点投影到屏幕空间并组装成三角形,通过光栅化生成像素大小的片元,然后片元被分配颜色值并绘制到帧缓冲区。现代图形处理器可以使用着色器来实现“投影到屏幕空间”和“分配颜色值”获取灵活性。让我们来看看每个阶段的细节:

顶点和图元数组

渲染工作通过管线中一个或者多个填充着顶点属性值的顶点缓冲区开始,这些属性用于顶点着色器的输入。常见的顶点属性包括在三维空间的顶点位置,一个或多个纹理坐标集合,纹理坐标用于将顶点映射到一个或者多个纹理的采样点。这一系列提供数据给绘制工作的顶点缓冲区即称之为顶点数组。当绘制任务提交时,我们提供一个额外的图元数组到顶点数组中,即一个索引数组,用来选择哪些顶点进入绘制管线。索引值的顺序还可以控制以后顶点如何装配成三角形。

统一状态和纹理

绘制任务也有统一的渲染工作状态,它在管线的每一个可编程阶段都提供了一系列共享的只读值。这使得着色器程序在顶点和片元之间能保持参数不改变。统一的状态包括纹理,可以是能被着色器采样的一维,二维,或三维数组。顾名思义,纹理通常用来将图片映射到表面上,它们也可以用来作为预先计算函数的查找表,或作为各种效果的数据集。

顶点着色器

GPU从顶点数组中读取每一个选择的顶点,通过顶点着色器来执行它,顶点着色器是将一系列顶点属性作为输入并输出一系列新属性的程序,这些属性称之为变量,用于提交给光栅化处理。在顶点着色器中至少要计算屏幕空间顶点的投影位置。顶点着色器也可以产生其他的输出变量,比如颜色、纹理坐标,用于光栅化为混合连接顶点的三角形表面。

三角形装配

GPU连接投影后顶点来组建三角形。它通过图元数组所指示的顺序讲顶点组织成“三个顶点”的结合。顶点可以被组织为几种不同的方式:

  • 以每三个元素作为一个独立的三角形。
  • 设为一个三角形条带,重用三角形的最后两个顶点,作为下一个三角形的前两个顶点。
  • 设为三角形扇面,连接的第一个元素到后面的每一对元素。

该图显示了三种不同模式的行为。条带和扇面在第一个三角形之后的每一个三角形都只需要一个新的索引就能构建,平衡了图元数组中独立三角形的灵活性和额外的存储效率。

光栅化

光栅化处理每一个三角形,裁切或者丢弃屏幕之外三角形,将可见的三角形分割成像素大小的片元。如上所述,顶点着色器的输出也被插值,并光栅化到每一个三角形的表面,同时将每一个片元之间光滑过度。例如,如果顶点着色器分配每个顶点一个颜色值,光栅化将融合像素化表面的颜色,如图所示。

片元着色器

生成的片元提交给另一个称为片元着色器的程序。片元着色器接收到顶点着色器输出的变量,通过光栅化作为输入进行插值。它输出颜色和深度值,然后进入帧缓存进行绘制。普通片元着色器操作包括纹理映射和光照处理。由于片元着色器对每一个像素的绘制独立运行,它可以执行最复杂的特殊效果,同时它也是图形管线中对性能最敏感的部分。

帧缓存,测试和混合

帧缓存的是渲染工作输出的最终目的地。除了OpenGL给你的用于绘制到屏幕上的默认帧缓存以外,现代的OpenGL实现可以让你绘制到离屏的帧缓存对象或者纹理上。这些纹理可以被用来作为其他渲染工作的输入。帧缓存不仅仅是一个单一的二维图像,除了一个或多个颜色缓冲区,帧缓存可以拥有一个深度缓冲区或者模板缓冲区,这两种缓冲区在片元绘制到帧缓冲区之前对其进行过滤。深度测试将放置在已绘制片元之后的片元丢弃,模板测试使用形状将缓冲区对象的可绘制部分绘制到模板缓冲区中。片元将它们的颜色值和“覆盖”的颜色值进行混合,最终的颜色、深度和模板值绘制到相应的缓冲区中。

结论

这就是你在OpenGL中调用“绘制”方法时,数据从顶点缓存到帧缓存的过程。渲染一个场景通常涉及多个绘制工作,变换纹理、其他的统一状态或者阶段之间的着色器,以及使用帧缓存的深度和模板缓冲区来结合每个阶段的绘制结果。现在我们已经覆盖了三维渲染一般的数据流过程,我们可以写一个简单的程序,看看OpenGL中这一切是如何发生的。在整个教程的过程中,我很想知道您的反馈意见,如果有帮助请告知我,或者这些对你没有任何意义。

目录 | 下一章 >>


本文英文来源为An intro to modern OpenGL。看到这篇文章觉得不错,于是想翻译,结果发现已经有人翻译了第一章,于是决定将其补完。 本章为直接转载的前人劳动成果,感谢译者所做的工作。接下来的章节将是由本人翻译的。

OpenGL转载