说明:跟着learnopengl的内容学习,不是纯翻译,只是自己整理记录。
强烈推荐原文,无论是内容还是排版。 原文链接
本文地址: http://blog.csdn.net/aganlengzi/article/details/50354162
在真正开始我们的探索之旅时,我们应该首先知道:OpenGL是什么。OpenGL通常被定义为一组用于操作图形和图像的应用程序编程接口(应用程序编程接口,简称API)。然而,OpenGL本身并不是API,而仅仅是由Khronos组织开发和维护的规格说明。
OpenGL规格精确地指定了每个函数的功能和结果/输出。但是如何来实现这个功能是由实现这些OpenGL规格的开发者来决定的。因为OpenGL规格并没有给出具体实现的细节,OpenGL真正的开发版本就可以有不同的实现方式,但是有一点是需要保证的,即要满足OpenGL的规格定义,从而向用户提供统一的接口。
真正实现OpenGL规格指定的函数体的通常是图形卡(姑且叫做显卡)制造商。我们购买的不同显卡可能支持不同的OpenGL版本。Apple系统维护他们自己的OpenGL图形库,Linux系统中既包含显卡制造商提供的版本也包含业余爱好者提供的版本。这也就表明,当OpenGL有时候表现出本不该出现的奇怪行为时,很可能是显卡制造商或者开发/维护我们所用OpenGL库的开发者的错。
既然OpenGL的实现基本由显卡制造上完成,所以更新显卡的驱动通常能够解决实现中存在的bug,这些显卡驱动(通常)包含了我们所购买的的显卡支持的最新OpenGL版本。这也是为什么我们被建议时不时更新显卡驱动。Khronos组织在其主页上展示着OpenGL所有版本的规格文档。感兴趣的读者能够找到OpenGL3.3版本的文档,这是我们将在这个教程中使用的版本。如果读者想要钻研OpenGL的细节,这将会是非常好的读资。规格的说明通常也提供了特定接口在使用时候用法的参考示例。
在早些时候,使用OpenGL意味着使用立即渲染模式(immediate mode,也被叫做固定功能流水线),这是一种简单易用的图形绘制模式。在这种模式中,大多数OpenGL的功能都被隐藏在库中,而(使用这些库的)开发者在控制OpenGL如何完成计算上并没有什么自由。他们十分渴求更大的灵活性,并最终使得规格定义变得更加灵活,开发者获得了对他们图形更多的控制权。立即渲染模式虽然十分容易理解和使用,但是它也十分的低效。因此,OpenGL规格自3.2版本开始不推荐使用立即渲染模式功能并且鼓励开发者以兼容配置模式(Compatibility profile,简称Core-profile)开发,Core-profile模式是OpenGL规格中去除弃用功能函数(比如立即渲染模式相关功能函数)的一个分支。
在使用OpenGL的兼容配置模式时,OpenGL强制我们使用最新的方法。当我们尝试使用OpenGL弃用的函数的时候,OpenGL会给出错误提示并退出。新方式的优点它的灵活性和高效性,但是不幸的是,相比立即渲染模式,新模式并不容易学习。立即渲染模式对OpenGL执行的太多操作进行了抽象,虽然它易学,但是学习者并不能够对理解OpenGL实际怎样工作;新的模式要求学习者能够通过这个学习过程真正理解OpenGL和图形编程,虽然难学,但是它具有更好的灵活性、更高的效率,更重要的是,学习者对图形编程更好的理解。
这也是为什么我们的教程采用基于Core-Profile的OpenGL3.3版本。虽然是有点难,但是这是值得的。
现今,更高版本的OpenGL已经公布了(目前最新的好像是4.5+),或许有人会问,我们为什么不直接学习最新版本的呢?对这个问题的回答相对简单。自3.3版本之后的OpenGL版本不会改变其核心内容,而只是改进或者增加额外高效的函数。结果是,自OpenGL3.3的新版本中,所有的原理和技术保持和3.3版本一致,所以学习3.3版本是十分合适的。一旦掌握3.3版本之后,更新的版本也就不在话下了。
需要注意的是,只有最新的显卡才能够支持最新的OpenGL版本,这也是为什么大多数的开发者通常针对较低版本的OpenGL开发而只是提供可选的高版本的功能支持。
OpenGL一个显著的特性是他对扩展的支持。当一个公司提出新的渲染技术或者一项重大改进时,通常都能够在驱动的扩展中找到。如果硬件支持这种扩展,开发者就能够利用扩展提供的功能编写更好更高效的图形程序。基于这种方法,图形开发者不用等到新的技术被OpenGL正式接收,而只要简单地确认自己的显卡支持这种技术,就能够使用新的渲染方法。十分流行和有用的扩展通常都能够被后来的OpenGL版本接纳。
开发者可以通过以下这种方式来检查某种扩展被自己的显卡支持:
if(GL_ARB_extension_name)
{
// Do cool new and modern stuff supported by hardware
}
else
{
// Extension not supported: do it the old way
}
我们使用的OpenGL3.3版本极少会用到扩展,如果使用,会在教程中说明。
OpenGL本身是一个很大的状态机:由一组变量来定义OpenGL应该怎样进行当前的操作。OpenGL的当前状态通常被称作OpenGL上下文。在使用OpenGL时,我们经常通过设置某些属性来改变它的状态,操作某些缓冲区,并且利用当前的上下文来进行渲染。
例如,当我们告诉OpenGL我们当前想要绘制的是线而不是三角形的时候,我们通过改变一些上下文中设置OpenGL绘制方式的参数来改变OpenGL的状态。一旦我们通过设置属性改变了它的状态,下一条绘制命令就会绘制线而不是三角形。
当我们使用OpenGL的时候,我们会遇到很多用于改变状态的函数来改变上下文,同时也会遇到很多用于使用状态的函数在OpenGL当前状态的基础上执行某些操作。只要我们时刻记住OpenGL本质上就是一个大的状态机,它的大多数函数都会很容易理解。
OpenGL函数库是用C语言开发的,它允许在其他语言中的派生,但是它的核心保持为C库。因为一些C语言的数据结构并没有很好地翻译到其他的高级语言,OpenGL在开发时保留了几种抽象。其中之一就是OpenGL中对象的概念。
OpenGL中的对象是一些代表OpenGL状态子集的属性。举例来说,我们可以定义一个代表绘制窗口设置的对象;然后我们可以设置它的尺寸,它支持的颜色等等。而且,我们可以用C语言结构体的风格开表示一个OpenGL中的对象,如下所示:
struct object_name {
GLfloat option1;
GLuint option2;
GLchar[] name;
};
基本数据类型
需要注意的是,我们使用OpenGL时,使用OpenGL自身定义的基本数据类型是方便的。例如,我们使用
GLfloat
而不是float
(为其加上GL
前缀)对int
,unit
,char
,bool
等类型也是同样。因为在不同的操作系统中,相同的数据类型可能由不同的字节数来表示。所以OpenGL使用其自己定义的GL
基本数据类型的内存格式。个人认为可以理解为一层统一的封装。使用OpenGL定义的基本数据类型能够保证我们的程序在不同的平台上都能够正常运行。
当我们使用对象的时候,通常看上去像下面这种方式(其中OpenGL的上下文被标示程一个大的结构体):
// The State of OpenGL
struct OpenGL_Context {
...
object* object_Window_Target;
...
};
// Create object
GLuint objectId = 0;
glGenObject(1, &objectId);
// Bind object to context
glBindObject(GL_WINDOW_TARGET, objectId);
// Set options of object currently bound to GL_WINDOW_TARGET
glSetObjectOption(GL_WINDOW_TARGET, GL_OPTION_WINDOW_WIDTH, 800);
glSetObjectOption(GL_WINDOW_TARGET, GL_OPTION_WINDOW_HEIGHT, 600);
// Set context target back to default
glBindObject(GL_WINDOW_TARGET, 0);
这段代码展示了我们在使用OpenGL时候会经常用到的流程。首先创建一个对象(本例中是一个窗口)并保存它的索引,即id
,真正的对象存储在别处。接着我们将对象绑定到一个OpenGL的上下文中(在本例中,创建的对象的索引objectId
被绑定到了目标GL_WINDOW_TARGET
)。然后我们设置对象的属性。最终我们通过设置当前的目标值为0
来解除对象和目标的绑定。其中,我们设置的属性被存储在objectId
指向的对象中,一旦我们将这个对象重新绑定到GL_WINDOW_TARGET
,这些属性也就会恢复。
目前示例代码展示的只是大概OpenGL执行的方式,在整个教程中你将会接触到足够多的真实示例。能够使用这些对象的一大好处就是我们能在应用中定义不止一个的对象,设置他们的属性,而且在开始利用OpenGL状态来进行操作的时候,我们可以按照我们的喜欢的方式来设定。比如,可以创建类似对象容器的对象来存储3D
模型数据(一间房子或者一个字符),当我们想要绘制其中任意一种的时候,我们只需要绑定存储我们想要绘制的那个对象的容器就可以了(假设我们已经在创建的时候设置了所有对象的属性)。多个对象允许我们可以设置多个模型,当我们想要绘制其中一个的时候,我们只需要在绘制的时候简单地绑定相关的对象而不用每次都设置他们的属性。
到这儿,我们已经学习了一点关于OpenGL的知识,知道了它是一个规格定义和一个函数库,知道了OpenGL在底层大致的实现技巧(比如为了屏蔽不同的操作系统数据格式的不同定义自己的基本数据类型)和大致的使用方法(前面举例一个对象的使用方法)。如果对其中的内容没能全部理解也不要紧,通过这个教程我们将会涵盖每一个细节,而且在其中你将会看到足够多的示例来帮助你理解OpenGL。准备好了咱就开始这段学习OpenGL之旅吧~