OpenGL(LearnOpenGL With Qt)

0. 说明

写本文的初衷是能够更好的让自己理解,同时便于日后查询,对有相同需求且过去完全没有接触过OpenGL的开发者来说,也许能提供一定的帮助。

目前学习OpenGL也许是最好的教程是来自Joey de Vries的LearnOpenGL,很幸运,该文章也有对应的中文版LearnOpenGL CN。因此文章绝大部分文字教程来自翻译Joey de Vries的LearnOpenGL CN,由于原文已经相当出色,并不需要过多修改(当然也没有那个水平),我仅根据原文增减成适合Qt开发的教程,读者也可以自行查看原文或翻译好的中文版。

如果查看目录,可以点击这里

1. OpenGL

在开始这段旅程之前我们先了解一下OpenGL到底是什么。一般它被认为是一个API(Application Programming Interface, 应用程序编程接口),包含了一系列可以操作图形、图像的函数。然而,OpenGL本身并不是一个API,它仅仅是一个由Khronos组织制定并维护的规范(Specification)。

OpenGL规范严格规定了每个函数该如何执行,以及它们的输出值。至于内部具体每个函数是如何实现(Implement)的,将由OpenGL库的开发者自行决定(译注:这里开发者是指编写OpenGL库的人)。因为OpenGL规范并没有规定实现的细节,具体的OpenGL库允许使用不同的实现,只要其功能和结果与规范相匹配(亦即,作为用户不会感受到功能上的差异)。

实际的OpenGL库的开发者通常是显卡的生产商。你购买的显卡所支持的OpenGL版本都为这个系列的显卡专门开发的。当你使用Apple系统的时候,OpenGL库是由Apple自身维护的。在Linux下,有显卡生产商提供的OpenGL库,也有一些爱好者改编的版本。这也意味着任何时候OpenGL库表现的行为与规范规定的不一致时,基本都是库的开发者留下的bug。

由于OpenGL的大多数实现都是由显卡厂商编写的,当产生一个bug时通常可以通过升级显卡驱动来解决。这些驱动会包括你的显卡能支持的最新版本的OpenGL,这也是为什么总是建议你偶尔更新一下显卡驱动。

所有版本的OpenGL规范文档都被公开的寄存在Khronos那里。有兴趣的读者可以找到OpenGL3.3(我们将要使用的版本)的规范文档。如果你想深入到OpenGL的细节(只关心函数功能的描述而不是函数的实现),这是个很好的选择。如果你想知道每个函数具体的运作方式,这个规范也是一个很棒的参考。

1.1 核心模式与立即渲染模式

早期的OpenGL使用立即渲染模式(Immediate mode,也就是固定渲染管线),这个模式下绘制图形很方便。OpenGL的大多数功能都被库隐藏起来,开发者很少有控制OpenGL如何进行计算的自由。而开发者迫切希望能有更多的灵活性。随着时间推移,规范越来越灵活,开发者对绘图细节有了更多的掌控。立即渲染模式确实容易使用和理解,但是效率太低。因此从OpenGL3.2开始,规范文档开始废弃立即渲染模式,并鼓励开发者在OpenGL的核心模式(Core-profile)下进行开发,这个分支的规范完全移除了旧的特性。

此处说法可能并不严谨,立即渲染模式确切的说并不是在OpenGL3.2废弃,而是在OpenGL3.1就废弃了,我们可以从Khronos网站查看OpenGL所有版本的规范文档

在这里插入图片描述

其中OpenGL3.0到OpenGL3.3之间变化较大,在Khronos网站关于OpenGL上下文百科中提到这种变化过程,原文是这样的。

Until version 3.0, all versions of OpenGL were fully backwards compatible with earlier ones. Code written against OpenGL 1.1 could execute just fine on OpenGL 2.1 implementations.

OpenGL version 3.0 introduced the idea of deprecating functionality. Many OpenGL functions were declared deprecated, which means that users should avoid using them because they may be removed from later API versions. OpenGL 3.1 removed almost all of the functionality deprecated in OpenGL 3.0. This includes the Fixed Function Pipeline.

However, since many implementations support the deprecated and removed features anyway, some implementations want to be able to provide a way for users of higher GL versions to gain access to the old APIs. Several techniques were tried, and it has settled down into a division between Core and Compatibility contexts.

There are other context variations as well.

文中提到在OpenGL3.0之前,所有的OpenGL版本支持完全向后兼容,包括OpenGL1.1编写的代码也可以在OpenGL2.1上很好地执行,因为这些都是采用立即渲染模式;OpenGL3.0则引入了弃用功能的想法,许多OpenGL函数被宣布为已弃用,但实际该版本仍为完全向后兼容;然而在OpenGL3.1删除了OpenGL3.0中几乎所有不推荐使用的功能,包括立即渲染模式(Immediate mode,也就是固定渲染管线),但为了保证OpenGL之前的代码可用,引入了ARB_compatibility扩展,因此有了一个向后兼容的规范和一个非向后兼容的规范,读者也可以查看OpenGL3.1的规范文档。

在这里插入图片描述

在这里插入图片描述

但是ARB_compatibility扩展使用时存在一定的问题,因此在OpenGL3.2中引入了核心模式(Core Profile)和兼容模式(Compatibility Profile),感兴趣的读者可以阅读百科全文。

当使用OpenGL的核心模式时,OpenGL迫使我们使用现代的函数。当我们试图使用一个已废弃的函数时,OpenGL会抛出一个错误并终止绘图。现代函数的优势是更高的灵活性和效率,然而也更难于学习。立即渲染模式从OpenGL实际运作中抽象掉了很多细节,因此它在易于学习的同时,也很难让人去把握OpenGL具体是如何运作的。现代函数要求使用者真正理解OpenGL和图形编程,它有一些难度,然而提供了更多的灵活性,更高的效率,更重要的是可以更深入的理解图形编程。

由于原教程使用的是面向OpenGL3.3的核心模式,因此我在将其以Qt代码复现的时候,也会优先选用OpenGL3.3。

现今,更高版本的OpenGL已经发布(写作时最新版本为4.6),你可能会问:既然OpenGL 4.6 都出来了,为什么我们还要学习OpenGL 3.3?答案很简单,所有OpenGL的更高的版本都是在3.3的基础上,引入了额外的功能,并没有改动核心架构。新版本只是引入了一些更有效率或更有用的方式去完成同样的功能。因此,所有的概念和技术在现代OpenGL版本里都保持一致。当你的经验足够,你可以轻松使用来自更高版本OpenGL的新特性。

当使用新版本的OpenGL特性时,只有新一代的显卡能够支持你的应用程序。这也是为什么大多数开发者基于较低版本的OpenGL编写程序,并只提供选项启用新版本的特性。

OpenGL不仅需要显卡的支持,同时还需要安装对应的显卡驱动。如何才能知道自己的电脑是否支持对应版本的OpenGL呢?这里我推荐使用AIDA64这款软件,读者可以在官网自行下载。

在这里插入图片描述

打开软件,在左侧树形列表中选择"显示设备"子节点,展开后再选择"OpenGL",右侧主界面可以显示用户当前所使用计算机的显卡信息,下方是该显卡支持的OpenGL兼容列表,从列表中可以看出,目前最新为OpenGL4.6。

由于本教程依旧沿用原教程OpenGL3.3,所以用户自行查看时,要关注OpenGL3.3是否100%支持,非100%支持可能会造成图像显示异常。同时笔者建议将计算机系统更新至Windows 7及以上,Windows XP下提供的显卡驱动较老,很难支持100%的OpenGL3.3及以上,即使为较新的显卡。

1.2 扩展

OpenGL的一大特性就是对扩展(Extension)的支持,当一个显卡公司提出一个新特性或者渲染上的大优化,通常会以扩展的方式在驱动中实现。如果一个程序在支持这个扩展的显卡上运行,开发者可以使用这个扩展提供的一些更先进更有效的图形功能。通过这种方式,开发者不必等待一个新的OpenGL规范面世,就可以使用这些新的渲染特性了,只需要简单地检查一下显卡是否支持此扩展。通常,当一个扩展非常流行或者非常有用的时候,它将最终成为未来的OpenGL规范的一部分。

使用扩展的代码大多看上去如下:

if(GL_ARB_extension_name)
{
    // 使用硬件支持的全新的现代特性
}
else
{
    // 不支持此扩展: 用旧的方式去做
}

使用OpenGL3.3时,我们很少需要使用扩展来完成大多数功能,当需要的时候,本教程将提供适当的指示。

1.3 状态机

OpenGL自身是一个巨大的状态机(State Machine):一系列的变量描述OpenGL此刻应当如何运行。OpenGL的状态通常被称为OpenGL上下文(Context)。我们通常使用如下途径去更改OpenGL状态:设置选项,操作缓冲。最后,我们使用当前OpenGL上下文来渲染。

假设当我们想告诉OpenGL去画线段而不是三角形的时候,我们通过改变一些上下文变量来改变OpenGL状态,从而告诉OpenGL如何去绘图。一旦我们改变了OpenGL的状态为绘制线段,下一个绘制命令就会画出线段而不是三角形。

当使用OpenGL的时候,我们会遇到一些状态设置函数(State-changing Function),这类函数将会改变上下文。以及状态使用函数(State-using Function),这类函数会根据当前OpenGL的状态执行一些操作。只要你记住OpenGL本质上是个大状态机,就能更容易理解它的大部分特性。

1.4 对象

OpenGL库是用C语言写的,同时也支持多种语言的派生,但其内核仍是一个C库。由于C的一些语言结构不易被翻译到其它的高级语言,因此OpenGL开发的时候引入了一些抽象层。“对象(Object)”就是其中一个。

在OpenGL中一个对象是指一些选项的集合,它代表OpenGL状态的一个子集。比如,我们可以用一个对象来代表绘图窗口的设置,之后我们就可以设置它的大小、支持的颜色位数等等。可以把对象看做一个C风格的结构体(Struct):

struct object_name {
    float  option1;
    int    option2;
    char[] name;
};
译注
原教程将所有的类型都改为了自带类型。但是请仍然记住,使用OpenGL的类型的好处是保证了在各平台中每一种类型的大小都是统一的。你也可以使用其它的定宽类型(Fixed-width Type)来实现这一点。

当我们使用一个对象时,通常看起来像如下一样(把OpenGL上下文看作一个大的结构体):

// OpenGL的状态
struct OpenGL_Context {
    ...
    object* object_Window_Target;
    ...     
};
// 创建对象
unsigned int objectId = 0;
glGenObject(1, &objectId);
// 绑定对象至上下文
glBindObject(GL_WINDOW_TARGET, objectId);
// 设置当前绑定到 GL_WINDOW_TARGET 的对象的一些选项
glSetObjectOption(GL_WINDOW_TARGET, GL_OPTION_WINDOW_WIDTH, 800);
glSetObjectOption(GL_WINDOW_TARGET, GL_OPTION_WINDOW_HEIGHT, 600);
// 将上下文对象设回默认
glBindObject(GL_WINDOW_TARGET, 0);

这一小段代码展现了你以后使用OpenGL时常见的工作流。我们首先创建一个对象,然后用一个id保存它的引用(实际数据被储存在后台)。然后我们将对象绑定至上下文的目标位置(例子中窗口对象目标的位置被定义成GL_WINDOW_TARGET)。接下来我们设置窗口的选项。最后我们将目标位置的对象id设回0,解绑这个对象。设置的选项将被保存在objectId所引用的对象中,一旦我们重新绑定这个对象到GL_WINDOW_TARGET位置,这些选项就会重新生效。

目前提供的示例代码只是OpenGL如何操作的一个大致描述,通过阅读以后的教程你会遇到很多实际的例子。

使用对象的一个好处是在程序中,我们不止可以定义一个对象,并设置它们的选项,每个对象都可以是不同的设置。在我们执行一个使用OpenGL状态的操作的时候,只需要绑定含有需要的设置的对象即可。比如说我们有一些作为3D模型数据(一栋房子或一个人物)的容器对象,在我们想绘制其中任何一个模型的时候,只需绑定一个包含对应模型数据的对象就可以了(当然,我们需要先创建并设置对象的选项)。拥有数个这样的对象允许我们指定多个模型,在想画其中任何一个的时候,直接将对应的对象绑定上去,便不需要再重复设置选项了。

1.5 让我们开始吧

你现在已经知道一些OpenGL的相关知识了,OpenGL规范和库,OpenGL幕后大致的运作流程,以及OpenGL使用的一些传统技巧。不要担心你还没有完全消化它们,后面的教程我们会仔细地讲解每一个步骤,你会通过足够的例子来真正掌握OpenGL。如果你已经做好了开始下一步的准备,我们可以开始创建OpenGL上下文以及我们的第一个窗口了。

1.6 附加资源

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值