2.1.1 顶点:3D图形的基础
OpenGL中的基本单元是顶点。简单来说,顶点就是空间中的一个点。在这些点上可以附加其他信息,如它如何映射到纹理,是否具有一定的重量或颜色等,但是最重要的信息是它的位置。
游戏会把大量的时间用于发送OpenGL顶点,或者告诉OpenGL以特定的方式移动顶点等。游戏可能首先告诉OpenGL它将要发送的全部顶点会构成三角形。此时,OpenGL将使用线把它收到的每三个顶点连接起来以创建一个多边形,然后可能还会使用纹理或颜色填充多边形的表面。
现代图形硬件非常擅长处理大量的顶点,把它们构成多边形,然后渲染到屏幕上。这个从顶点到屏幕的过程叫做流水线(pipeline)。流水线负责定位顶点和提供光照,以及进行投影变换。这个过程将把收到的3D数据变换为2D数据,以便将其显示到屏幕上。投影变换听起来有点复杂,但是几个世纪以来,画家和艺术家们一直在利用这种变换,在平展的画布上绘制出他们周围的世界。
2.1.2 流水线
流水线的流程如图2-3所示。这个流水线叫做固定功能流水线(fixed function pipeline)。当程序员为这种流水线提供输入后,所有的功能已被固定,无法修改。例如,在像素处理阶段很容易向屏幕上添加蓝色调,但是在固定功能流水线中,程序员无法直接控制这个阶段。
图2-3 基本的固定功能流水线 |
固定功能流水线包含6个主要的阶段。这些阶段几乎都可以并行应用。如果硬件支持的话,每个顶点可以同时经过同一个阶段。由于这个原因,图形卡的处理速度比CPU的处理速度要快得多。
输入阶段。输入以顶点的形式给出,顶点的属性包括位置、颜色和纹理数据。
变换和光照。根据当前视图变换顶点。这包括将顶点的位置由3D空间(也叫做世界空间)改为2D空间(也叫做屏幕空间)。如果有些顶点对最终的显示没有影响,可以在这个阶段移除它们。在这个阶段也可以对每个顶点执行光照计算。
创建图元。这是使用顶点信息创建多边形的过程,需要根据OpenGL的状态把顶点连接到一起。游戏经常使用三角形或者三角带作为图元。
光栅化。这是将多边形转换为像素(也叫做片段)的过程。
像素处理。像素处理也叫做片段处理。这个阶段将测试像素,以确定是否把它们绘制到帧缓冲区中。
帧缓冲区。帧缓冲区是一块内存,代表特定的帧将显示在屏幕上的部分。混合设置将决定像素如何与已经绘制的部分进行混合。
使用程序控制流水线了。将程序上传到图形卡以后,它们将替换固定功能流水线的默认阶段。重写流水线的特定部分的小程序叫做着色器(shader)。
图2-4显示了可编程流水线的阶段。这里删除了变换和光照阶段,添加了顶点和几何着色器。像素处理阶段也可以像像素着色器那样进行编程。通过流水线的每个像素都会被应用像素着色器。像素着色器不能添加顶点,但是可以修改位置、颜色和纹理位置等属性。
图2-4 |
与像素着色器和顶点着色器相比,几何着色器加入的时间较晚。几何着色器以一个完整的图元(如线、点或三角形组成的带)作为输入,而顶点着色器针对每个图元都会运行。几何着色器可以创建新的顶点信息、点、线,甚至图元。几何着色器还可用于创建点精灵和动态镶嵌,以及实现其他一些效果。点精灵可以快速渲染大量精灵,这种技术经常用于粒子系统,可创建类似于火焰或者烟雾效果。动态镶嵌可以在几何形状中添加更多的多边形。这种技术可以用来增加包含多边形数较少的游戏模型的平滑度,并在摄像机放大模型时展现更多的细节。
像素着色器会应用到被发送到帧缓冲区的每个像素。像素着色器用于创建凹凸贴图效果、镜面高光和逐像素光照。凹凸贴图效果可以给表面增加额外的高度信息。凹凸贴图通常包含一个图片,其中每个像素代表一个标准的法向量,该法向量描述了如何扭曲表面。像素着色器可以使用凹凸贴图来为模型提供一个更有趣的纹理,使其看上去有一定的深度感。逐像素光照用于替换OpenGL的默认的光照公式。OpenGL的光照公式可计算出每个顶点的光照,然后为该顶点提供一种合适的颜色。逐像素光照使用更精确的光照模型,为每个像素单独应用光照。镜面高光是模型中非常亮、反射大量光的区域。
2.2 变化中的OpenGL
现在是学习OpenGL的大好时机。OpenGL的当前版本非常适合学习。这个版本中包含了大量易于使用的函数,可以完成各种各样与图形处理有关的工作。可以把OpenGL的当前版本想象成一个带有辅助轮的自行车,刚开始时借助它们学习怎么骑车,熟练之后就把它们取下来。OpenGL的下一个版本更像是一个性能强大的摩托车,没有必要的东西都被移除了,剩下的就是不受限制的原始力量。对于有经验的OpenGL程序员,其好处自不必言,但是初学者却容易对其望而生畏。
可以使用的OpenGL版本要取决于系统中安装的图形卡驱动程序。几乎每台计算机都支持OpenGL 2.1,而近期生产的图形卡会支持OpenGL 3.x。
2.2.1 OpenGL ES
OpenGL ES是用于嵌入式系统的OpenGL的新版本。它与OpenGL的近期版本相似,但是功能集更加受限。它用于高端手机,例如Android、BlackBerry和iPhone。OpenGL ES也用于军用硬件,例如战机上的平视显示器。
OpenGL ES支持可编程流水线,也支持着色器。
2.2.2 WebGL
WebGL目前仍处于开发中,这个版本的OpenGL是专为在Web上使用而设计的。使用WebGL的浏览器必须支持HTML 5 canvas标签。就现在而言,还无法知道它会取得多大的成功。以前人们曾有过在Web上使用3D的尝试,但是都失败了。例如,VRML(Virtual Reality Modeling Language,虚拟现实建模语言)与HTML类似,但是允许用户创建3D世界,它在学术界受到了不少关注,却从没有吸引过普通的用户。
WebGL有一些强有力的支持者,许多大公司都加入了WebGL工作组,如Google、Apple和Mozilla,而且WebGL还有一些让人印象非常深刻的演示。就目前来看,WebGL的性能还是不错的,在成熟以后很可能可以与Flash一较高低。
2.3 OpenGL和图形卡
OpenGL是一个允许程序员发送指令到图形卡的库。图形卡是一种专用于显示3D数据的硬件,由很多标准组件构成,包括帧缓冲区、纹理内存和GPU。GPU是图形处理单元(Graphics Processing Unit)的缩写,它控制着如何处理顶点并把它们显示到屏幕上。CPU向GPU发送指令和数据,描述每一帧应该怎样显示到屏幕上。纹理内存通常是一块较大的内存,用于存储游戏所需的大量纹理。帧缓冲区是内存中的一块区域,存储下一帧中将显示到屏幕上的图像。现代的图形卡通常有多个GPU,每个GPU上都有许多着色器处理单元来执行大规模的并行着色器操作。分布式应用程序(如模拟蛋白质折叠的Folding@home)和世界各地的数十万台计算机都利用了这一特点。
第一个获得流行的3D图形卡是3dfx Voodoo 1。这是早期的一个图形卡,有2MB的纹理内存和2MB的帧缓冲区,并且使用PCI总线,时钟速度为135MHz。早期的一些游戏使用它来加速执行,例如《古墓丽影(Tomb Raider)》、Descent II、《雷神之锤(Quake)》以及《雷神之锤2(Quake 2)》的演示,从而运行得更加流畅,并且可显示更多的细节。Voodoo 1使用一个标准的PCI总线,允许CPU以最高533MB/s的速度向图形卡发送数据。现代的图形卡已经从PCI转向使用AGP(Accelerated Graphics Port,加速图形端口),其最高数据发送速度为2GB/s,后来又转向使用PCI Express。当前的这一代PCI Express卡的最高数据发送速度为8GB/s。
好像每个月都会有新的图形卡问世,每个新图形卡都比之前的图形卡更快。在编写本书时,最快的图形卡可能是ATI Radeon HD 5970。它有两个GPU,每个GPU都有1600个着色器处理器。它的时钟速度为725MHz,每秒可以处理4.64万亿次浮点运算。
大多数现代图形卡都有专门执行新操作的特殊硬件。这种硬件通过使用扩展提供给OpenGL。当收到一个标识新扩展的字符串时,OpenGL能够展示驱动程序和图形卡中的功能。例如,ATI Radeon HD 5970有两个GPU,这种情况很少见。为了能够充分利用两个GPU,需要使用一些新的扩展方法,如AMD_gpu_association。这个扩展允许用户在两个GPU之间分配任务。如果多家供应商实现了相同的扩展,那么扩展字符串的某个位置会有字母EXT。有时候,控制OpenGL规范的架构评审委员会可能会把某个扩展的状态提升为官方扩展,此时,扩展字符串中将包含字母ARB,所有的供应商都必须支持该扩展。
着色器--图形卡上的程序
着色器(shader)这个词带有一定的误导性。最初的着色器程序主要用于处理组成每个多边形表面的像素,以便为模型着色。但是随着时间推移,着色器程序的功能已经被扩展,现在还可以修改顶点属性、创建新顶点,甚至完成一般的操作。
着色器与运行在CPU上的普通程序具有不同的工作方式。着色器程序在大量的元件上同时执行,这意味着着色器程序是大规模并行程序,而运行在CPU上的程序一般则是串行运行的,一次只有一个实例运行。着色器程序非常适合对构成3D世界的像素和顶点的集合执行操作。
目前共有3类着色器,分别是顶点着色器、几何着色器和像素着色器,每种着色器都只能执行特定的操作。顶点着色器处理顶点,像素着色器处理像素,几何着色器处理图元。为了降低复杂性,并使硬件制造商可以更高效地进行优化,所有这些着色器都被一种叫统一着色器(unified shader)的着色器替代了。
着色器通过运行在图形卡上的特殊语言进行编程。目前,这些语言比C++低级得多(更别提C#了)。OpenGL有一门叫做GLSL(OpenGL Shading Language,OpenGL着色语言)的着色语言,它与C语言有些类似,但是有大量用于处理向量和矩阵的特殊操作。DirectX也有自己的着色语言,叫做HLSL(High Level Shading Language,高级着色语言)。两种语言十分类似。让人更加困惑的是,除了这两种语言以外,还有一种叫做Cg的语言,它由Microsoft和Nvidia开发,与HLSL有些类似。
游戏中的着色器非常适合创建需要进行大量计算的特殊效果,如视差贴图的光照。当前的技术使得着色器程序几乎不能用于其他用途。本书主要介绍游戏编程,由于可编程流水线是一个很大的主题,所以不会讨论该技术。如果对此主题感兴趣,可以参阅附录A部分,那里介绍了几本非常好的书籍。
着色器的一种趋势是用于一般性的并行编程任务,而不只是处理图形。例如,Nvidia PhysX库允许在GPU而非CPU上完成物理计算,从而得到更佳的性能。PhysX是用另外一种叫做CUDA的着色器语言编写的,但是CUDA与其他着色器语言有一些不同,这种语言不怎么关注像素和顶点,而是更关注一般目的的并行编程。假设在游戏中要模拟一个城市,并且有一个非常新奇的并行算法可以更新城市中的全部居民,那么这种计算在GPU上可以更快地执行,而CPU就可以被解放出来,执行其他任务。CUDA通常用于科学研究项目,因为这是利用强大的计算能力的一种廉价的方式。使用CUDA的应用程序包括量子化学计算、心肌模拟和黑洞建模。
2.4 Tao框架
Tao框架是C#使用OpenGL库的一种方式。Tao包装了许多C库(见表2-1),并使得在C#中使用这些函数变得很简单。Tao中还绑定了Mono,所以也可以用在Linux和Mac中。
通过Tao,C#不只可以使用OpenGL,还可以使用其他一些有用的库。
表2-1 Tao框架中包含的库
库 | 用途 |
Tao.OpenAl | OpenAL是一个强大的音频库 |
Tao.OpenGl | OpenGL是我们将要使用的图形库 |
Tao.Sdl | SDL(Simple DirectMedia Layer),一个 构建在OpenGL之上的2D库 |
Tao.Platform.Windows | 支持通过Windows.Forms使用OpenGL |
Tao.PhysFs | 一个I/O的包装器,支持游戏资源的存档文件,如.zip文件 |
Tao.FreeGlut | OpenGL实用程序工具包(OpenGL Utility Toolkit)是一组包装器,用于设置OpenGL 程序和一些绘图例程 |
Tao.Ode | Open Dynamics Engine是在游戏中使用的 一个实时物理引擎 |
Tao.Glfw | OpenGL Framework是一个可以在多个平台 上使用的轻量级的包装器类 |
(续表)
库 | 用途 |
Tao.DevIL | DevIL是将各种图片类型(bmp、tif、gif、 png等)加载到OpenGL的出色的工具 |
Tao.Cg | Cg是一种高级着色语言 |
Tao.Lua | Lua是游戏业最常用的脚本语言之一 |
Tao.FreeType | 字体包 |
Tao.FFmpeg | 主要用于播放视频 |
OpenAL代表开放音频库(Open Audio Library),是一个强大的开源库。《生化奇兵(BioShock)》、《雷神之锤4(Quake 4)》、《毁灭战士3(Doom III)》和《虚幻(Unreal)》等游戏都使用了这个音频库。它采用OpenGL作为模型,具有相同的状态机风格的设计和扩展方法。
SDL(Simple DirectMedia Layer)是一个跨平台的库,支持输入、声音和图形。SDL在游戏开发商中非常流行,在独立或者开源游戏中使用得尤其多。使用SDL开发的最著名的开源游戏之一是FreeCiv,它是《文明(Civilization)》的一个联机版本。多数Linux游戏端口中也使用了SDL。
PhysFs初看起来可能是一个物理库,但是实际上却是一个小型的IO库。它可以将全部游戏资源打包为一个较大的二进制文件,或者几个小的二进制文件。许多商业游戏都有类似的系统,例如《毁灭战士(Doom)》的wad系统或《雷神之锤(Quake)》的pak系统。它可以使游戏在发布后的修改和更新变得更加简单。
FreeGLUT是OpenGL实用程序工具包的免费版本。这个库中的函数可以让用户马上就能够使用OpenGL。它还有从键盘和鼠标接受输入的方法,以及绘制各种基本形状的方法,例如球形、立方形,甚至茶壶形(这个茶壶在计算机图形学中非常有名,它是由Martin Newell在犹他大学求学期间进行建模的。茶壶是一个非常复杂的表面,所以在测试新的图形技术时非常有用。动画电影《玩具总动员》中就有一个典型的茶壶模型,DirectX甚至有自己的茶壶创建方法D3DXCreateTeapot( )。在讲授OpenGL时经常用到FreeGLUT,但是它的功能很有限,很少用于真正的项目。
ODE(Open Dynamics Engine)是一个可以用在多个平台上的物理引擎,可以完成碰撞检测和刚体模拟。PC上的第一人称射击游戏《潜行者(S.T.A.L.K.E.R)》中就使用了ODE。Glfw是可以通过Tao使用的第三个可移植的OpenGL包装器。Glfw代表OpenGL框架(OpenGL framework),它的目的是扩展GLUT提供的功能。如果不想使用SDL,但又确实想使用框架来访问OpenGL,就可以考虑使用Glfw。
DevIL(Developer's Image Library)是一个从磁盘加载纹理到OpenGL中的库。DevIL与OpenGL有些类似,因为它也是一个状态机,并且有类似的方法名称。DevIL是跨平台的,支持多种(43种)不同的图片格式。Cg是本章前面提到的一种着色器语言。通过使用Tao.Cg,可以从文本文件或字符串中加载着色器程序,进行处理,然后在OpenGL中使用。
Lua可能是游戏开发中最流行的脚本语言。它是一种小型的、易于嵌入的语言,表达力非常强。使用Tao.Lua可以在脚本和C#程序之间传递函数和数据。Tao.FreeType是一个基本的字体包,可以将FreeType类型的字体转换成一幅位图。它的接口简单易用。
Tao提供的最后一个库是FFmpeg,这个名称由MPEG(一个视频标准)和FF(Fast Forward,快进)组成。它提供了一种播放视频的方式。如果想要在游戏中使用过场动画,FFmpeg是一个不错的选择。
Tao提供的所有库都是完全开源的。其中的多数库都可以免费用在商业项目中,但还是有必要阅读许可证中列出的具体说明。Tao是一个出色的程序包,刚开始涉足游戏的开发商可以把它作为一个起点。对每个库的介绍不在本书的讨论范围之内,我们将只关注其中最重要的那些库。从第5章开始,我们将使用OpenGL和Tao.Platform.Windows库。第6章将讨论DevIL。第9章将讨论使用OpenAL播放声音,以及使用SDL处理手柄输入。每个库都很有用,所以很有必要花些时间研究每个感兴趣的库。