首页 > 代码库 > 和馅饼一起学opengl 第一篇——总概

和馅饼一起学opengl 第一篇——总概

写在前面

    这个系列是关于OpenGL(以下称OGL)的一些入门学习,虽然旨在入门,但是我却不想把它写得过于简单(当然是相对的,会者不难),首先这系列要肯定的一点是一定会有编码,有实例来帮助大家一起学习,但是套用一句侯捷先生说过的话,勿在浮沙筑高台,所以我会说一些晦涩的关于OGL的理论知识,如其RC的概念,状态机等等,旨在深入浅出OGL,同时,我也在学习中,所以错误在所难免,请大家不要给我面子一定要狠狠的指出来,那么有疑问的地方我一定会寻找更多的证据来解释,所以,本系列希望达到的是一个双赢的效果,还有作为业界良心,我不希望自己凑合的写文章,当然会做很多资料收集,考证,所以,更新频率不会太快,请大家不要介怀,好了,开坑~~

 
OGL的前世今生

    既然要说清楚OGL这个东西(姑且称为东西),那么不得不提及其从何而来,又将去往何处。OGL被定义为一种“图形硬件的软件接口”,其1.0版本发布于1992年7月,姑且当其诞生日(就比我小两个月左右,真是缘分天注定),其前身为SGI公司(开发图形工作站的,已经被收购)的IRIS GL,这时候它还只是一个2D图形函数库,逐渐被SGI公司演化为为其高端IRIS图形工作站所使用的3D API,此时,它还只是一套比较专能的API,但是由于历史原因和一些技术原因,不得不对其进行改进,于是OGL诞生了,一套具有高可移植性(切记,只是高可移植性)的3D API,并且为了其可以更好的发展,从而推动SGI的工作站能大卖,SGI“开放”了OGL的标准,让更多的厂商可以参与进来,你至今也可以看到OGL的扩展库中有SGI_打头的扩展。

    正所谓独乐乐不如众乐乐(真虚伪),SGI开放了标准,但是他控制着标准从本质上来说它就不是开放的(看看巨硬的DX),所以众多厂商汇聚在一起,大腿一拍,于是ARB(OpenGL Architecture Review Board,OGL体系结构审核委员会)诞生啦,那么其中包括有巨硬,十八摸,当当当当等等大公司(题外话,谁知道就培养了巨硬这么个二五仔,自己搞了一套DX,节操掉光光),就此,促使了一个一个OGL的版本的诞生,一切是那么和谐,可惜,好景不长,就因为巨硬这么个二五仔,暗地里发展实力,并且在Windows系统逐渐推广为全世界应用最广泛的操作系统(我是异教徒,你们不要扒我裤子,不要放火烧我),随着ARB组织的人员流失,SGI公司的债务危机,扩展的混乱,低效,逐渐逐渐让许多许多人开始对OGL失去了信心,然后,DX8顺势而出,其在2D游戏上的突出表现,简直让人惊艳,大批大批的人开始加入DX阵营,面对当时如日中天的NV对DX的全面支持(NV和巨硬就是好丽友),Windows的广泛使用,巨硬强大的商业支持,OGL不得不让出其的位置,光芒日趋黯淡。

    对于OGL的发展,有一个人居功至伟——卡马克(当然不是我,你们想到那里去了),约翰卡马克其人只能用鬼才形容,神童,自由软件支持者,一个人对抗一个公司的战斗力,3D游戏引擎概念提出者,独立游戏引擎开发者,造火箭狂人,妻管严,并且他还是个OGL死衷粉。如果不是他,那么现在的Win上可能都没有OGL 1.1 的软件版本,在Win上开发OGL的难度可想而知。随着这些年移动游戏平台的广泛发展,OPENGL ES进入了人们视线,又掀起了大量学习OGL的狂潮,OGL也得以重新进入了大众的视线,这和2006年一次变动不无关系,khronos行业协会,一个由大部分原ARB小组成员组成的行业协会,它一直负责维护OGL ES,在06年以后,他们也开始负责维护OGL,从而,OGL开始了快速的发展,目前的OGL4.5和DX11.1理论上是伯仲之间,所以有这样的发展,实在是让人心悦。

状态机,扩展和跨平台

    在这一系列中,我将好好说说关于OGL的底层机制,虽然这并不是编码者需要关注的部分,举个不恰当的例子,一个好的汽车工程师,想要造出一辆性能卓越的跑车,那么就得充分了解你所拥有发动机,而我们就是工程师,OGL就是我们手中的发动机,那么想要玩的好,一定的了解是必要的。

    首先要说说的是OGL的跨平台机制,那么所谓OGL跨平台这个说法由来已久,其实其中有许多的误区和以讹传讹。首先,OGL确实是跨平台,但是这个跨平台并不是其与生俱来的特性,我之前说了,其与生俱来的是强大的可移植性,那么众所周知,巨硬的DX是不可跨平台的,那么究竟他们干了什么,听我细细道来,首先我们来看点东西,如图1-1:

 

图1-1 OGL与D3D架构比较

(图片引用:《跨越opengl和d3d的鸿沟(一):开篇》——龚敏敏)

    从图1-1上可以看出,OGL的Runtime在硬件抽象层,而DX的Runtime层在系统驱动层,就是这个区别,从架构上来说,OGL和DX的性能并没有太大的区别,区别在于,DX的Runtime在系统层,由巨硬自己实现,他们在此上进行了平台绑定,并且对资料进行了严格保护,所以使得跨平台的难度大大增加(实际上在linux平台上有一套非官方的DX版本,名叫Direct9 for linux,由别人反编译得到),而因为OGL与生俱来的可移植性,并且其RT和Driver都是由厂商自己实现得到,所以只要平台根据标准进行一定的支持,并且提供一些扩展,那么OGL可以轻易的移植到该平台上来,这就是OGL的跨平台思想,依靠不同平台的支持和自身强大的可移植性。但是,水能载舟亦能覆舟,从目前的情况上来看,OGL的稳定性并不如DX,因为由于不同厂商的BUG,在不同的硬件上,会出现明明没有任何代码变动,却会挂掉的情况,这是一些不可预料的坑。目前来说,在对OGL的支持性来说,N卡最好,A卡和Intel核卡都有不同的问题(也是醉了)。而众所纷纭的OGL跨平台其实还是有些不同程度的坑,在linux和Windows上你可以获得最完善的OGL支持,在mac平台上的OGL版本总是较低(我确认了一下,好像是不到4.x版本),而在移动平台,是OGL ES(一个子集版本,有些开发标准不同)我的IOS是7.1.2版本,支持到2.0,安卓我不知道。在其他console(游戏主机)平台,wii平台用的是完全固定管线,而骚尼自己根据标准实现了一套,所以虽说是跨平台,还是老老实实的吧:(

    OGL有一个核心概念,就是基于扩展,那么什么叫扩展?扩展就是厂商在实现过程中加入一些自己的想法来充分利用硬件去完成一些功能,可以看做是功能增加,在2.x之前,是一个扩展混乱时代,不同的厂商实现了重复的功能,良莠不齐,导致开发复杂度提高,软件性能下降。于是之后ARB组织,提出了注册机制,所有厂商在实现扩展之前需要去OGL官网上进行注册登记,防止出现重复,并且在3.x之后ARB对OGL进行了一次大规模的裁剪,并且推出了一个gl3.h的头文件,但是我发现还有好多人在用之前的东西进行开发,orz。那么对于扩展来说,又分为核心扩展和非核心扩展两种,组织一方面开放标准,让厂商开放思维去增加扩展,这些扩展就是非核心扩展,当这个扩展被越来越多人接受使用,这个扩展会经过投票被加入到核心扩展中,加入核心扩展就保证了,只要你的显卡支持OGL相应版本,就可以使用相应的核心扩展。那么非核心扩展的支持性也有好坏之分,ARB扩展(组织和厂商们共同商讨的扩展)> EXT扩展(厂商们自己商讨的扩展)> other,在使用顺序上也应该基于此。

    最后要讲讲状态机,有一定经验或者有所了解的都知道,OGL底层是基于一个抽象状态机的,我知道状态机这个东西还是在学习编译原理的时候,哎,一塌糊涂,具体状态机其本质为何,我不做深述,OGL的状态机可以从几个方面来看,首先,可以开启与关闭状态,比如用glEnable和glDisable函数,开启/关闭深度检测,MSAA等等;其次,记录状态,比如记录当前target下bind的buffer,跟踪数据流动,根据状态不同对数据进行不同的处理,基于状态机的特性帮助我们管理流水线,大概就是这么个东西。

Objects和Context

    OGL的Context(这个词为什么一直被翻译成上下文呢)是一个重要的概念,非常非常核心,它掌管着OGL的生命周期,一个OGL程序需要从初始化Context开始直到销毁Context来结束。那么究竟什么是Context?从OGL官方的定义来看,Context存储着所有的状态实例,比如,有一个概念比如target,创建VBO,FBO等等Buffer时都要bind到Context,你在一个程序中你可以创建多个Context,你要指定Context到你的窗口系统,从而让OGL知道你的目标,这个方法我在后续文中介绍。很难说清楚Context到底是个什么东西,它太抽象,只要先理解为它是一个管理者,它与OGL系统密切相关即可,慢慢的我们将彻底了解它。

    上段简单的介绍了Context,那么Context究竟控制着什么,管理着什么?其实就是一个个Object,虽然OGL形式上更像C Runtime形式的API,但是其内部有非常完整的对象概念,那么简单分可以将OGL中的对象分为:regular object ,container object 和 non-standard object。首先,regular object,姑且翻译为规则对象,其中有大名鼎鼎的Buffer Objects,Texture等等,这些对象有一个特点就是创建和销毁都遵循相同规范,并且都由Context管理,这个在后续内容中会详细描述;container object,翻译成容器对象吧,比如大名鼎鼎的VAO,FBO等等都属于container object,这些对象有什么用呢?从名字上来看它们是容器,什么的容器?regular object的容器,在STL中,容器的存储往往是使用copy,但是在OGL中这一行为是Attach,并不包含任何的额外的存储开销,这也会在之后的文章中详细的说明,今日姑且一提;剩下的就是non-standard object,从名字上看可以理解为非标准对象,这里面包含着GLSL Object和同步对象(用于多线程),这些对象也会在之后的文章中慢慢提及。对于Object和Context的基本概念简介到此。

Shader与GLslang

    在3.x时代来临后(具体哪个版本我不记得了,不要拍我),OGL也进入了完全可编程流水线时代,内置管线被废弃,也就是说不写shader不行,那么基于时代在召唤,本系列绝对要用最高级的版本来编写,这样显得我逼格高,那么内置管线那套东西我是不会用的(淘汰了用它作甚),那么shader和GLslang就是不得不说的东西了。

    先介绍介绍它们的来历,据《real time rendering》所载,shader的历史可以追溯回1984年名曰“Cook‘s shade trees”,而在80年代后期,大名鼎鼎的RenderMan规范(皮克斯公司)推出了“RenderMan Shading Language”这个规范在目前的电影特效渲染(非实时)中依旧还在广泛使用,但是目前GPU支持还是空谈(作者注:GPU的商业化产物出现在90年代后期),而成功在real time领域的shader语言是出现在Quake III中(又得夸夸卡马克)被称为“Arena Scripting Language”,这时已经到了1999年(马上跨世纪了啊哈),果然,21世纪2001年,随着DX8的发布,GPU支持的可编程shader出现了,后来发展为HLSL,NV发展了Cg,而紧随其后OGL提出了GLSL,其实因为shader是基于GPU的概念,所以使用的都是统一的shader模型,虽然语言层面不同,但是其本质却没啥区别,很多优秀的引擎都对GLSL和HLSL进行了统一。

    目前在DX也好,OGL中也好都分为几种shader,Vertex Shader,Tessellation Shader,Geometry Shader,Fragment Shader(DX中称为Pixel Shader)还有相对独立的Compute Shader(GPGPU),每种Shader的功能我会在后面的文章中娓娓道来,并不难。说白了,为什么要这么多Shader,还有可编程,为的不就是有更加霸气侧漏的效果嘛。但是说起来简单,掌握可不是一件容易的事。

Other和题外话

    首先这个系列肯定是在Windows系统下完成的,虽然OGL是跨平台的,我不太想用这个特性,也就是说我不会用glut库,一切按照windows下基于扩展来完成,所以需要的第三方库只有glew(其实只要是扩展库都行,比如KlayGE的glloder(GPL v2.0)),所以恐怕要让linux和mac的用户失望了(我就是异教徒,你放火烧我啊!),其他为了方便使用的工具库不少,但是那些用到再说吧。

    至于写这个系列的目的,一方面肯定是为了自己能有个更深入的理解,同时希望有更多的人进入这个领域(我指的是CG),所以不过是抛砖引玉而已。写的不好,大家见谅,未完待续...

和馅饼一起学opengl 第一篇——总概