概述

学会一门图形api并不容易,除了图形学算法以外,还得花时间去理解图形api本身的架构。对于OpenGL而言,这个架构就是它的状态机模型,由于OpenGL版本的不断迭代,整个架构发生了一定的迭代,不同版本的api风格不一致,导致初学者更难入门。不过,如果能抓住一定的脉络,整个api的架构也会变得相当清晰。下文中,将会按照迭代的顺序,对OpenGL状态机架构的常见设计做一个阐述。

buffer object和target

在最早期的立即模式的OpenGL中,是不存在buffer object这个概念的。如果要绘制一个图元,必须调用相关api告诉OpenGL指向顶点数据的指针,随后OpenGL立即将存储在CPU中的顶点数据复制到GPU中并绘制相应的结果(这也是为什么叫立即模式)。这种方式的问题在于,每次绘制前都必须把数据从CPU复制到GPU一次,降低了渲染效率。我们完全可以在每次绘制前就把CPU中存储的顶点数据传递到GPU,绘制的时候直接在GPU中读取顶点数据即可。为了做到这一点,OpenGL设计出了buffer object和target。在绘制前把CPU中的数据绑定到buffer object并传递到GPU,每次绘制时只要把buffer object绑定到target上即可。(不过,为了方便数据的更新,一些buffer object中的数据仍旧会在CPU中存储一些备份,并通过某些机制保持一致性(coherent)(在红宝书中把coherent翻译成了连续性,这个翻译是不准确的,coherent应该代表缓存一致性,参见缓存一致性协议。))

子状态对象

我们知道,OpenGL是一个状态机,由很多个状态组成。但是,考虑到这么一种情况:有一些状态,这些状态总是同时被切换。如果每次都一个个的去切换这些状态,多次调用OpenGL api,无疑会降低性能。为了解决这一问题,OpenGL将一些总是同时切换的状态打包成一个object,每次要切换这些状态时,只需要切换对应target绑定的object即可,而不用多次调用api去切换每一个状态。
目前来说,这些object有:vertex array object,transform feedback object,program object, framebuffer object。这些object每一个都代表了一个状态的子集。在较早期的OpenGL api中,如果要设置这些object所管理的子状态,必须把这个object绑定到相应的target上,然后才能设置这些object所管理的状态。这种机制具有一定的模糊性,因为对于初学者来说,他很难去分辨,哪些状态是直接由整个大的状态机管理,哪些状态是由这些object管理。所以,在后来的OpenGL api中,可以直接指定某一个object去设置这个object所管理的子状态,这也就是direct state access。

绑定点机制

在较早期的OpenGL api中,每个target对应一种object类型。于是,同一次绘制中,只能一种object只能使用一个,这显然是不够的。为了同时使用多个同一类型的object,OpenGL又设计了绑定点机制,一种类型的绑定点有多个,将object绑定到相应的绑定点上,随后根据相应的绑定点去使用相应的object。值得一提的事,在现在很多的教材上,对于绑定点机制的介绍不够准确(再次点名红宝书),事实上,vbo,ubo,xfb都采用了绑定点机制。下面上一张图:

更多推荐

OpenGL状态机设计思路剖析