本书系列
OpenGL是什么
在我们编写openGL程序之前,我们首先需要知道什么是OpenGL。
将OpenGL作为一个API
OpenGL 通常被认为是应用程序接口(API)。OpenGL API有不同编程语言的实现版本。但是,它们最终使用的都是最底层的C语言的接口。
在C语言的API中,定义了很多typedefs,#define,以及函数。typedefs用于定义openGL里面使用到的基本的数据类型,如GLint,GLfloat等。
复杂的数据结构,如struct,在openGL中没有直接暴露给用户,都是隐藏在一系列的API背后。这样有利于将OpenGL API改写成非C语言实现的版本。
在C++中,如果你想要一个包含整型,浮点型和字符串的类型,你会采取下面的措施实现:
struct Object
{
int count;
float opacity;
char *name;
}
// Create the storage for the object
Object newObject;
// Put data into the object
newObject.count = 5;
newObject.opacity = 0.4f;
newObject.name = "Some String";
在opengl中,采用api的方式实现,如下:
// Create the storage for the object
GLuint objectName;
glGenObject(1, &objectName);
// Put data in to the object
glBindObject(GL_MODIFY, objectName);
glObjectParameteri(GL_MODIFY, GL_OBJECT_COUNT, 5);
glObjectParameteri(GL_MODIFY, GL_OBJECT_OPACITY, 0.4f);
glObjectParameteri(GL_MODIFY, GL_OBJECT_NAME, "Some String");
上述的例子中,并不是真实的opengl的命令,仅仅用来说明,在opengl中一个对象是怎么通过api的方式创建以及赋值的。
opengl自己拥有存储所有opengl对象的存储空间。因此,用户只能够通过引用的方式来访问对象。几乎所有的opengl对象都会被绑定到一个非符号整型(GLuint)。对像会被类似于glGen*的函数创建,星号表示的对象的类型。第一个参数表示有多少个对象要被创建,第二个参数用来接受创建的对象的名字。
大多数对象首先需要被绑定到上下文,才能够被更改。很多对象可以被绑定到上下文的不同的位置,这就允许一个对象以不同的方式被使用。这些不同的位置被称为“targets”,所有的对象都拥有一个合法的目标列表,有些对象仅有一个目标。在上面的例子中,GL_MODIFY是objectName
被绑定到的位置。
GL_OBJECT_*
形式命名的枚举,是这个对象中能够被赋值的属性。glObjectParameter
这类的函数,作用是将对象中的参数绑定到给定的目标上。由于opengl的api是c语言版本的,对于不同类型入参的函数,必须以不同函数名分别开来,如glObjectParameteri
针对整型入参,glObjectParameterf
针对浮点型入参。
需要注意的是,opengl中的对象并不是都想示例中这么简单,有些改变对想法状态的函数,也和示例中函数的形式不同。那么,将一个对象绑定到上下文具体是什么意思呢,在下面会有解答。
opengl的结构
opengl的api被定义为状态机。几乎所有的opengl函数都是设置或是获取相应的状态。没有用来改变对象状态的唯一一类函数是,利用这些设进去的状态,来触发渲染的行为。
你可以将这个状态机设想成一个很大的,带有很多不同属性的数据结构。这样的数据结构被称为“opengl context”,上下文中的每一个属性都包含了一些用于渲染的必要的信息。
opengl中的对象被定义成上下文中的一系列的属性,并且可以被保存和恢复。将一个对象绑定到一个目标,就是将对象中的属性替换掉上下文对应目标上的属性。因此在绑定之后,后续的函数将会从上下文中调用到刚刚更新了的内容。
对象通常用GLuint表示,这些整数是链接opengl实际对象的句柄。0是一个特殊的数字,与null指针具有类似的含义。将目标绑定到0,意味着对当前绑定的对象解绑。也就是说,对应的上下文中的内容会恢复到初始状态,恢复到绑定发生之前的状态。
opengl上下文类似于:
// Opengl Object Example
struct Values
{
int iValue1;
int iValue2;
};
// Opengl Context Example
struct OpenGL_Context
{
...
Values *pMainValues;
Values *pOtherValues;
...
};
OpenGL_Context context;
要创建一个Values
对象,你将要调用类似与glGenValues
的函数,然后你要将创建的对象绑定到上下文的目标位置,如GL_MAIN_VALUES
表示指针context.pMainValues
,GL_OTHER_VALUES
表示指针context.pOtherValues
。你将通过glBindValues
分别将对象绑定到对应的目标上。这将会使得上下文中的指针指向你创建的对象。
也有用于设置绑定对象内参数的函数,glValueParam
。它将获取对象的目标位置,也就是上下文中的那个指针。也会获取一个枚举用来表示对象中的哪个值会被改变。如GL_VALUE_ONE
来表示iValue1
,GL_VALUE_TWO
用来表示iValue2
。
opengl规范
从技术上来说,opengl并不是api,而是一个规范。一个文档。c api仅仅是对这个规范的一种实现。这个规范定义了opengl初始状态,每一个函数对于改变状态和获取状态能够做些什么,当你调用渲染函数的时候,哪些会发生。
这个规范是OpenGL架构审查委员会书写的。这个机构的代表有Apple,NVIDIA和AMD等。这个委员会是Khronos Group的一部分。
这个规范是非常复杂的技术文档。但是,其中的部分内容是很容易理解的。如果你尝试去读这个文档,你会发现这个文档注重对结果的描述,而不是具体实现。也就是说,如果硬件的一部分以不同的实现方式提供了相同的结果,用户是无法分别出来的。
opengl的实现 尽管opengl ARB控制了opengl的规范,但是它并没有控制代码本身。opengl并不能通过软件中心进行下载。它的实现完全取决于硬件开发商,根据opengl规范,进行实现。
针对不同的操作系统,都有不同的人员来控制opengl的实现。针对windows操作系统,opengl的实现是受硬件开发商自己控制的。在Mac OSX操作系统,opengl的实现是受apple公司控制的,他们决定提供给用户opengl的哪个版本,以及哪些扩展。而,针对linux系统,事情就变得有点复杂了。
简单的讲,如果你写的程序超出了规范中约定的行为,这个就是你的opengl实现商的问题了(假设你的代码中并没有bug)。在windows系统,他们的opengl实现是附带在不同的图形硬件驱动中的。因此,假如遇到了他们实现版本的问题,通常可以采用升级驱动的方法,也许这个问题已经在最新的驱动中解决了。
opengl版本 目前已经有了很多不同版本的opengl规范。opengl的版本并不像大多数通常会改变大多数api的direct3d版本。在一个opengl版本中能够运行的代码,基本可以在更新的版本中运行。
唯一需要注意的是,正确处理opengl3.0和以上的版本,和3.0之前的版本之间的关系。在3.0版本的时候,让很多旧接口过时了。在3.1版本的时候将这些过时的接口中的大部分都删除了。同时将规范分解成了两个变种,核心的和兼容的。兼容的部分包含了3.1版本删除的所有旧的函数,然而在核心的部分并没有这么做。理论上,opengl规范仅仅需要核心的部分,但是这会让很多原来以来于老接口函数的功能无法正常工作。
作为一个实际问题,这些都不重要。没有一个opengl驱动开发者会仅仅实现核心的部分。因此,opengl完全是向下兼容的。
术语
- vector 向量
一个由带顺序的其他数值组成的值。向量中存储的数字的个数称为维度。向量拥有属于自己的数学操作。
- scalar 标量
单个,非向量数值,也可以考虑成是一个维度的向量。
- vector position 位置向量
表示一个位置的向量
- vector direction 方向想量
表示一个方向的向量
- vector component 向量组成成分
向量中的每一个值
- component-wise operation 面向向量各成分的操作
一个操作的效果是分别对向量各个成分进行操作的效果相同,输出结果的向量的维度和输入向量的维度相同。
- unit vector 单位向量
向量长度为1的向量是单位向量,是纯粹的方向向量
- vector normalization 向量标准化
将一个向量变成同向的单位向量的过程
- pixel 像素
数字图像的最小成分。一个像素拥有颜色空间中的特定颜色
- image 图像
二维的像素数组
- rendering 渲染
将一个3d的世界,从一个特殊的视角转变成2d图像的过程
- rasterization 光栅话
一个特殊的渲染方法,用于将一系列的3d的三角形转变成2d的图
- geometry,model,mesh
由三角形组成的,3d空间中的单个物体
- vertex 顶点
组成三角形的三个元素之一。顶点可以是任意的数字,用来表示一个点在3d空间中的位置。
- clip space, clip coordinates 裁剪空间,裁剪坐标系
由三维空间中的点转换而来,这个空间中点的位置是4个维度的。第四个维度w表示了裁剪空间的可见区域。因此裁剪坐标系中点的xyz成分必须在[-w,w]之间才是可见的。在裁剪坐标系中x的正方向是右,y的正方向是上,z的正方向是远离观察者的方向。裁剪坐标系中的点是渲染管线中的顶点处理阶段的输出结果。
- clipping 裁剪
这个过程是将裁剪坐标系中如果有顶点在裁剪空间外面,就发生分割行为
- normalized device coordinates 标准化设备坐标系
这个是将裁剪坐标系中的点除以w之后的点。这样,所有的点的成分在[-1,1]范围内就是可见的。
- window space, window coordinates 窗口空间,窗口坐标系
由标准化设备坐标系映射过去的三维空间。这些点的xy的位置和最终图像的位置是一一对应的。原点在左下方,x正方向为右,y正方向是上。z值在[0,1]的范围,0表示最近,1表示最远。在这个范围外的点是不可见的。
- scan conversion 扫描转换
这个过程是将窗口坐标系中的三角形,打成一个个片段,然后投射到对应的图像的像素中。
- sample 采样
在扫描转换过程那个中对于三角形像素边界非连续的位置判断是否产生片段的过程。单个像素的区域可以进行多次采样,从而可以产生过的片段
- fragment 片段
扫描转换后的三角形的单个元素。一个片段可以包含任何的数值,但是这个数值要是三维的,用来表示窗口坐标系中在三角形中的位置。
- invariance guarantee 不变性保证
opengl提供的一个保证,在顶点处理过程中相同的输入拥有相同的输出。
- colorspace 颜色空间
在计算机图形学中,用来表示颜色的一个集合。所有的颜色都是在特定的颜色空间的基础上定义的。
- shader 着色程序
渲染器来执行的程序,用来执行用户定义的一些行为
- shader stage 着色器
渲染管线中的一个特殊位置,着色程序用来执行的地方。这个阶段的执行结果将会作为下一个阶段的输入。
- opengl
用来规定渲染系统的规范
- opengl context opengl上下文
会用于渲染的参数的特殊集合。类似于一个很大的c语言中的struct,其中包含了很多不同的可以被访问的属性。如果你需要创建多个窗口来处理渲染任务,那么没一个窗口都会有自己独立的opengl上下文
- object binding 对象绑定
对象可以绑定到opengl上下文的特定位置上。此时,上下文特定位置的参数指向的内容就是被绑定的对象中设定的内容。同时一个对象可以被绑定到不同的位置,特定类型的对象可以绑定到不同的位点。
- opengl implementation opengl的实现
针对特殊的系统,对opengl规范实现的软件