OpenGL 低级着语言与高级着语言
节选自偶的毕业论文,主要是对可编程图形处理器, ARB_VERTEX_PROGRAM和glslang 的简单介绍。抛砖引玉,希望大家不吝赐教:)
Octane3d@hotmail
第二章可编程图形处理器
现代图形流水线如图-2.1所示。
(图-2.1,图形流水线)
应用程序级(Application)实现物理模拟,处理用户输入,修改数据结构,数据库访问,几何基元生成等
功能。命令级(Command)提供命令缓冲区,解释执行命令和管理图形系统的状态。几何级(Geometry)对多项式表达的弯曲曲面求值,进行几何变换,光照计算,纹理坐标生成,实现剪切,拣选与几何基元组装等功能。光栅化级(Rasterization)进行三角形设置,对三角形进行抽样以生成片断,并且对颜和纹理坐标进行插值。纹理级(Texture)对纹理坐标进行变换,进行纹理访问与过滤。片断级(Fragment)混合片断颜与纹理,进行雾化操作,执行深度、透明以及模版测试,最终生成要写入帧缓冲区的象素。显示级(Display)对象素颜进行嘎玛校正后写入帧缓冲区。命令与几何级属于对象空间的操作,其上的操作是每顶点的,主要是进行几何变换和光照计算,它的特点是大量(>= 1000万个顶点)复杂的浮点操作。纹理与片断级输入图像空间的操作,其上的操作是每片断的,主要是进行纹理混合,它的特点是进行海量(>= 10 亿个片断)但是较简单的定点运算。
图形硬件的发展是一个逐渐对图-2.1中的流水级增加硬件加速功能的过程。最早的图形硬件只是一个简单的帧缓冲区,没有任何硬件加速功能。之后逐渐添加硬件光栅化,硬件纹理映射,硬件几何变换与光照计算,硬件反走样等功能。按照图形硬件所支持的硬件加速功能的多少与性质,可以把图形硬件分为几代。1984年,SGI公司发布了IRIS 1400图形系统,其中集成的图形硬件可以实时光栅化平坦着(flat-shaded)的多边形,这是第一代图形加速硬件。第二代图形加速硬件以HP的SPX和SGI的GT为代表。它们提供Phong光照计算,Gouraud着和Z-Buffer功能,光栅化性能也有了大大提高。1992年,SGI推出了RealityEngine [Akeley93],之后又于1996年推出了InfiniteReality [Montrym97],标志着图形
加速硬件进入第三代。RealityEngine在第二代图形加速硬件的基础上,增加了纹理映射与与全场景反走样的功能,大大提高了实时渲染的真实感。上述的图形加速硬件价格昂贵,只有高档的图形工作站才能够配置,但是1996年3Dfx公司推出的面向PC平台的Voodoo系列3D加速卡改变了这一局面。Voodoo卡介于第二与第三代图形加速硬件之间,它提供Gouraud 着,硬件光栅化,硬件纹理映射,Z缓冲区等功能,将PC带入了实时3D的时代。随后,PC图形硬件蓬勃发展,更高的三角形生成速率,更高的填充速率,多纹理功能,硬件几何变换与光照计算,碰撞与环境映射以及全屏反走样将其带入了第三代图形加速硬件的时代。此后,随着PC游戏以及PC工作站的发展,可以说,PC图形硬件的发展代表了图形硬件技术发展的主流方向。
无论是传统工作站图形硬件,还是PC图形硬件,从第一代到第三代,它们的一个共同特征就是都只实现了固定功能的渲染流水线,而不具备可编程能力。与这些图形硬件相匹配,主要的3D API例如OpenGL 和 Direct3D作为一个状态机实现,用户通过3D API提供的函数设置好相应的状态,例如变换矩阵、材质参数、光源参数、纹理混合模式等,然后传入顶
点流。图形硬件则利用内置的固定渲染流水线和渲染算法对这些顶点进行几何变换、光照计算、光栅化、纹理混合、雾化操作、最终将处理结果写入帧缓冲区。这种渲染体系限制用户只能使用图形硬件中固化的各种渲染算法。这虽然可以很好的满足对渲染质量要求不高的应用,但难以满足那些需要更高的灵活性和更真实的渲染质量的实时图形应用。用户已经不再满足于基于顶点的近似Phong模型光照计算
(这是OpenGL和Direct3D采用的光照计算模型,这两种API上的光照计算或者在支持硬件光照计算的的图形硬件之上进行,或者在CPU 之上进行。)和简单的多纹理混合。用户需要硬件加速的角动画支持,需要使用定制的光照模型(基于片断的光照模型、各向异性光照模型、基于BRDF的光照模型),需要非真实渲染(卡通渲染、素描渲染),需要每片断Fresnel效果,需要各种体积效果,需要过程纹理,以及各种以往只有在Renderman [Upstill89] 之上才能看到的3D效果。对于这些需求,传统的图形硬件是无能为力的。满足这些需求的答案就是硬件可编程性。
2001年3月,nVidia公司推出了具有可编程能力的GeForce 3 [Lindholm01],从而将图形加速硬件带入了可编程的时代,即可编程图形处理器,我们不妨将它们视为第四代图形处理器。现代图形处理器的可编程引擎如图-2.2所示。
(图-2.2,现代图形处理器的可编程引擎)
低级编程语言图-2.2中的VE(顶点引擎或者顶点处理器)和PP(片断引擎或者片断处理器)就是可编程图形处理器中的可编程部件。运行于顶点处理器之上的程序称为顶点着程序(Vertex Shader),它们的工作是进行几何变换和光照计算等操作。运行于片断处理器之上的程序称为片断着程序(Fragment Shader),它们的工作是进行纹理混合等操作。顶点着程序的输入是顶点流,输出是处理后的顶点流。后者经固定的光栅化模块进行三角形组装和光栅化处理后用于生成片断流送入片断着程序。片断着程序访问纹理,进行纹理混合并最终计算出片断的颜与深度信息送入后续流水级以进行透明、深度、模板与雾化等操作。一个图形处理器中一般包含多个顶点处理器和多个片断处理器。如图-2.2所示的图形处理器包含2个顶点处理器,4个片断处理器。同类型的处理器上运行的总是同一个着程序的一个拷贝,这些着程序并行运行,处理输入流中的不同数据元素。输入流中的数据元素类型相同,并且相互之间独立。Shader在一个处理器上的每一次运行只能处理输入流中的一个元素。因此,顶点处理器或者片断处理器可以看作是使用同一指令对不同数据元素进行处理的单指令多
数据处理器(SIMD Processor)。
图-2.3是顶点处理器的体系结构。图-2.4是片断处理器的体系结构 [Michael02]。这两种处理器都没
(图-2.3,顶点处理器体系结构)
(图-2.4,片断处理器体系结构)
有内存的概念,所有的运算都在寄存器之上进行。它们的每一个寄存器都是四分量浮点寄存器,指令集中的指令可以同时对四个分量进行运算,因此可以把顶点处理器看作是一个基于寄存器的向量处理器。图-2.3中的顶点数据寄存器用于只读访问当前被处理的顶点的属性,例如顶点位置、法向量、颜和纹理坐标。常量寄存器用于用于为顶点程序提供只读常量参数或者程序参数。只写的地址寄存器用于间接访问常量寄存器。临时寄存器用于保存中间运算结果。只写的输出寄存器则用于输出处理后的顶点属性。图-2.4中的颜寄存器用于只读访问片断的颜。只读的纹理坐标寄存器用于访问纹理坐标和对纹理进行抽样。常量寄存器用于为片断程序提供只读常量参数或者程序参数。临时寄存器用于保存中间运算结构,临时寄存器r0也是输出寄存器。图-2.3和图-2.4是DirectX所定义的顶点/片断处理器体系结构,但是它们也恰当的抽象了例如OpenGL等其它类型的顶点/片断处理器。
更详细的寄存器与指令集说明将放在下一章进行介绍。这里重点讨论一下顶点/片断处
理器区别于微处理器的一些特殊之处。微处理器的特点是标量运算、无内在向量并行性、算术运算单元少、有完善控制流指令、延迟低、带宽小。而图形应用的特点确是大运算量、大规模并行性、允许较长的延迟与深度前向流水。这就决定了图形处理器和微处理器在体系结构上存在着很大的差别。首先图形处理器要尽可能的实现高度并行性。这种并行性分为两种:数据并行性与流水线并行性。为了充分利用
数据并行性,图形处理器在两个层次上进行并行处理。第一层利用输入数据流中数据元素之间的无关性,多个顶点(或片断)处理器运行一个顶点(或者片断)着程序的多个拷贝,同时作用于输入数据流中的多个数据元素之上。从这个意义上说,图形处理器是一个流处理器。第二层是利用图形运算包含大量向量运算的特点,实现指令级并行性。具体地说就是把多个同类型的标量运算合并到一个向量运算之中。因此,图形处理器又是一个向量处理器。其次流水并行性允许顶点着程序与片断着程序同时运行,前者的输出恰为后者的输入,构成一种生产者消费者的关系。再次为了保证大规模并行运算,图形处理器基本上不支持转移指令或者只提供有限的支持,着程序的静态长度和动态可执行长度也非常短。最后图形处理器有着比微处理器多的多的算术逻辑运算单元。图形处理器的所有指令都具有相同的延迟,不存在各类流水竞争。
现在图形处理器向着通用流处理器的方向发展,虽然它的重点应用仍然是图形应用,通用性也远不是微处理器概念上的通用性。可以预测,未来的图形处理器将支持功能更丰富的算术指令,提供更高程度的指令正交性和更强的转移指令功能。而它的应用范围除了实时图形应用外,也会扩展到全局光照计算、多体问题、分子动力学、弹性形变、流体模拟等科学计算领域 [Boltz03]。