既然我们已经介绍了3D图形的基本术语以及隐藏在3D图形背后的思路,现在就可以进入正题了。在使用OpenGL之前,我们衡要讨论什么是OpenGL以及什么不是 OpenGL,使读者能够同时了解这种API 的功能和限制。本章概括地描述了OpenGL的操作,并介绍了如何建立3D艺术品的淮染框架。(简单来说本章就是来初步认识OpenGL的)

 

2.1 什么是OpenGL?

摘要:

 严格地来说,OpenGL将定义为 "图形硬件的一种软件接口"。从本质上说,它是一个3D图形和模型库,具有高度的可移植性,并且具有非常快的速度。(一种高效并且可移植性强的图形硬件的API)

使用OpenGL,可以创建优雅而漂亮的3D图像,并且具有非常出色的视觉质量。

使用OpenGL的最大优点是它的速度远远快于光线追踪器或软件渲染引擎。

最初,它使用SGI (Silicon Graphics, Inc.)精心开发和优化的算法。

随着其他厂商不断奉献经验和智慧,发展自己的高性能实现方案,OpenGL也得到了不断的发展。

OpenGL并不像C和C++那样的编程语言,它更像一个C运行时的函数库,提供了一些预先打包的功能。

另一方面,OpenGL规范包含GLSL,即OpenGL着色语言,这实际上是一种非常类似于C语言的程序设计语言。

但是,GLSL并不会对应用程序流程和逻辑进行控制,而是用于渲染操作。

OpenGL意在供那些专门为显示和处理3D图形而进行设计和优化的计算机硬件使用。

(表面我们使用的是OpenGL提供的API,这些实现一般由显示设备厂商提供)

OpenGL具有多种用途,范围涵盖从CAD工程和建筑应用程序到那些恐怖电影中用于实现计算机生成鬼怪的建模程序等各种应用。随着硬件加速和高速PC微处理器越来越普及,3D图形已经成为消费者和商业应用程序的典型组成部分,而不再局限于游戏和科学应用程序

 

2.1.1 标准的演化

摘要:

OpenGL的前身是SGI公司 IRIS GL。

它最初是个2D图形函数库,后来逐渐演化为由该公司的高端IRIS图形工作站使用的3D编程API。

这种计算机不仅是通用计算机,他们具有专门经过优化的硬件,用于显示复杂的图形。

这种硬件提供了超快的矩阵变换能力(这是3D图形的先决条件)、硬件支持的深度缓冲以及一些其他功能。

但是在某种情况下,出于支持陈旧系统的需要,技术的发展常常受到束缚。

IRIS GL并不是一开始就设计为具有顶点风格的几何图形接口的。后来形势逐渐变得明朗,SGI必须彻底改变才能继续发展。

OpenGL 就是 SGI 对 IRIS GL的移植进行改进和提高的结果。这个新的图形API不仅具有GL的功能,而且是一个"开放"的标准。

它的输入来自其他图形硬件厂商,并且更容易应用到其他硬件平台和操作系统。

OpenGL是为了3D集合图形而完全重新设计的。

 

OpenGL ARB

如果只有某一家厂商控制标准,那么这个标准就不是真正的开放式标准。SGI当初的业务领域是高端的计算机图形

一旦已经处于某个行业的顶端,你就会发现进一步成长的机会非常有限。

SGI意识到如果它能够做一些事情来推动高端计算机图形硬件时长的成长,那么这对公司也是件非常好的事情。

一个得到众多厂商支持的真正的开放式标准能使程序员(开发者)更容易创建可以适用广泛平台的应用程序和内容。

软件确实能够促进计算机的销售,如果SGI希望卖出更多的计算机,那么就需要在它的计算机上能够运行更多的软件。

其他厂商也意识到了这一点,这样OpenGL体系结构审核委员会(ARB, OpenGL Architecture Reiview Board)就诞生了。

SGI最初控制了OpengGL API的许可, 而 ARB 的创立者包括 SGI、DEC(Digital Equitment Corporation)、IBM、Intel、和 Microsoft。

1992年7月1日,OpenGL 规范 1.0版正式出台。

随着时间的推移,ARB又陷入陆续增加一些新成员,其中有许多来自其他PC硬件社区。

ARB每隔4年召开一次会议,对规范进行维护和改善,并出台计划对OpenGL标准进行升级。

近年来,由于某些原因,SGI的业务不断下滑,具体原因超出本书的讨论范围。

2006年,实际上已经破产的SGI公司把对OpenGL标准的控制权从ARB转交给一个新的工作组: Khronos 小组(www.khronos.org)

Khronos是一个由成员提供资金的行业协会,专注于开放媒体标准的创建和维护。

大多数ARB成员已经成为 Khronos的成员,因此这个变动并没有产生太大的波动。

今天,Khronos小组继续发展和升级OpenGL以及它的姊妹API——OpenGL ES。(将在16章介绍OpenGL ES)

OpenGL以两种形式存在。

第一种形式是 "OpenGL 规范",这个规范定义了行业标准,用非常完整和明确的术语描述了OpenGL。

它完整地定义了OpenGL API、OpenGL的整个状态机,以及各种特性是如何共同工作和运行的。

然后向 ATI、NVIDA、Intel或Apple这样的硬件厂商获取这个规范,并实现它。

第二种形式就是OpenGL的实现,软件开发人员和顾客可以使它生成实时图形。(通过调用OpenGL的API)

 

OpenGL 扩展机制

读者可能会认为,既然OpenGL是一种 "标准" API,那么硬件提供商在竞争中只要考虑性能(可能还有视觉质量)因素就可以了。

然而,3D图形领域的竞争是非常激烈的,硬件提供商不仅在性能和质量方面拥有持久的创新力,在图形方面和特效方面也是如此。

OpenGL 运行提供商通过它的扩展机制进行创新。

这种机制以两种方式运作。

首先,提供商能够向OpenGL API中增加开发人员使用的新函数。

其次,可以添加能够被已存在的OpenGL函数识别的标记(Token)或枚举(Enumerant)。

利用新的标记或枚举只需在项目中添加一个提供商支持的头文件(Header)。

提供商必须在OpenGL工作组(Khronos小组的一个下属机构)注册它们的扩展,以避免提供商使用其他提供商已经使用的值。

有一个标准的头文件 glext.h 中包含了这些扩展,为我们提供了方便。

游戏需要为特定图形卡而进行重现编译的时代已经一去不复返了。

就像我们已经知道,我们可以查到一个标识提供商和OpenGL驱动版本的字符串。

确定一个扩展是否得到支持需要两个步骤。

首先,我们向OpenGL查询当前实现支持多少扩展。

然后,我们可以通过调用 glGetStringi 函数获取特定扩展的名称。这个函数将返回单个扩展的名称。

(代码: Open GL扩展支持)

GLint nNumExtension;
glGetIntegerv(GL_NUM_EXTENSIONS, &nNumExtension);

例如: 要在Windows中查询交换控制扩展,我们可以依次查阅所有扩展寻找我们需要的。一旦找到,我们要得到这个函数的函数指针并正确地调用它。

(代码: Open GL在Windows中查询交换控制扩展)

GLint nNum;
glGetIntegerv(GL_NUM_EXTENSIONS, &nNum); // 跟上一个示例一样,获取扩展支持数量// 遍历所有扩展
for (GLint i = 0; i < nNum; ++i) {// 判断是否支持扩展名为: WGL_EXT_swap_control 的扩展if (strcmp("WGL_EXT_swap_control", (const char *)glGetStringi(GL_EXTENSIONS, i)) == 0)         {// 支持则获取这个扩展函数的函数指针wglSwapIntervalExt = (PFNWGLSWAPINTERVALEXTPROC)wglGetProcAddress("wglSwapIntervalExt");if (wglSwapIntervalExt != NULL) {// 调用设置这个扩展wglSwapIntervalExt(1);}}
}

PS: 稍微扩展一下上面的示例(不感兴趣的此处可跳过)

Q: WGL_EXT_swap_control 是什么?(详情OpenGL扩展)

A:  此扩展允许应用程序指定最小周期性 颜色缓冲区交换的数量,以视频帧周期为单位。(摘抄自OpenGL扩展)

在 GLTools 库中有一个快捷工具包,稍后将对其进行讨论。

int gltIsExtSupported(const char *extension);

如果支持指定的扩展,那么这个函数返回1;反之则返回0。

GLTools库包含了一整套OpenGL帮助和使用工具函数,其中有很多在本书中自始至终都在使用。

gltools.h文件包含所有的函数的函数原型。

(代码Open GL在Windows中查询交换控制扩展)这个示例还展示了再Windows下如何获取一个指向新的OpenGL函数的指针。

Windows函数 wglGetProcAddress 返回一个指向OpenGL函数(扩展)名的指针。

获取一个由于操作系统的不同而不同的扩展,这一主题在本书的第3部分将进行更详细的阐述。

幸运的是,在99%的情况下我们使用一个叫做GLEW的快捷方式库就可以了,而我们将自动获得驱动程序支持功能的扩展函数指针。

 

这是谁的扩展

使用OpenGL扩展,我们可以在代码中规定代码路径来改着渲染表现和视觉质量,或者甚至可以添加只有特殊提供商的硬件才支持的特效。

但是这些扩展是属于谁的?

也就是说,哪家提供商创建一个特定的扩展并为其提供技术支持?

通常情况下我们只通过观察扩展名就能得出结论。

每个扩展都有一个由3个字母组成的前缀,这个前缀标识了这个扩展的来源。

表2.1展示了一些扩展的识别的示例。

表2.1 OpenGL扩展识别示例
前缀 提 供 商
SGI_Silicon Graphics
ATI_

ATI Tenchnologies

AMD_

Advanced Micro Devices

NV_NVIDIA
IBM_IBM
WGL_Microsoft
EXT_

Cross-Vendor

ARB_ARB Approved

一个提供商为另一个提供商的扩展提供支持的情况并不少见。

例如,一些NVIDIA的扩展就在ATI硬件平台上得到支持并广泛流行。

竞争提供商必须遵循原始提供商的规范(关于扩展如何工作的细节)。

通常人们都认为扩展是好东西,扩展的EXT_前缀表明这个扩展(假定)是与特定供应商无关的,并且得到众多实现的支持。

最后,还有一些ARB承认的扩展。

这些扩展的规范经过了OpenGL ARB的审核以及讨论。

这些扩展常常意味着某些新技术或函数能够加入核心OpenGL规范前的最后步骤。

 

许可和一致

OpenGL实现可以是软件库,也就是对OpenGL函数调用做出响应、创建三维图像的软件函数库。

OpenGL实现也可以是一个用于完成三维图像渲染任务的硬件设备(通常是显示卡)的驱动程序包。

硬件实现比软件实现要快上许多倍。

而且,现在即使在廉价PC上这类硬件也已经非常普遍。

如果厂商希望创建并销售OpenGL实现,首先必须从Khronos小组获得OpenGL许可。

如果申请者是PC硬件厂商,Khronos小组在批准许可时会顺带提供一个示例实现(纯软件形式)和一个设备驱动程序。

然后,厂商就可以据此创建经过优化的实现,并且可以通过扩展提高产品的价值。

在典型的情况下,厂商之间的竞争就是性能、图像质量和驱动程序稳定性的竞争。

此外,厂商的实现必须通过OpenGL一致性测试。这些测试的设计目标就是保证实现方案是完整的(包含所有必须的函数调用),并且对于一组特定的函数,这种实现所产生的3D渲染输出结果是可以接受的。

软件开发人员在使用OpenGL驱动程序时并不需要获得OpenGL许可或支付任何费用。
OpenGL是得到操作系统原生支持的,并且获得许可的驱动程序是由硬件厂商本身提供的。

 

2.1.2 OpenGL的未来

绝大多数公司认识到从长远而言,竞争对于每家公司都是件好事,因此它们都认可并支持行业标准,甚至对行业标准作出贡献。

Khronos小组下属的体系结构审核委员会(ARB)如今已经非常壮大,生气勃勃且充满活力。

最近,OpenGL规范的修订工作已经达不到一年就能推进一个版本的速度。

到本书编写时,OpenGL最新的版本已经更新到了3.3和4.0,这两个版本都是在2010年游戏开发者大会上发布的。

15年以来,加起来人们已经在各种类型的OpenGL技术、书籍、教程、示例代码和应用程序上花费了数百万的人工。

这种持续的动力将使OpenGL在可预见的未来保持广大应用程序和硬件平台首选API的地位。

所有这些都使OpenGL获得良好的定位,以充分利用未来3D图形技术创新成果。

OpenGL2.0中加入的OpenGL着色语言(GLSL),使OpenGL显示出了长久的适应性,能够满足不断发展的3D图形程序设计管线所带来的挑战。

最后,OpenGL是一种规范,能够应用于各种各样程序设计规范。从C/C++、Java到Visual Basic,甚至C#这样的新兴程序设计语言,现在都已经用来使用OpenGL创建PC游戏和应用程序。

OpenGL已经被广泛地接受和关注。

 

OpenGL 和 Direct3D

就像政治上或宗教上的联盟一样,对程序设计语言或API的旋转警察在某种程度上是某些原因和情感考虑。

——"我就是这样成长的" 或者 "这就是最早学习的API,我还是用它最顺手"。

这当然是任何人会选择Direct3D而不选择OpenGL的唯一符合逻辑的原因。

如果读者是3D图形程序设计领域的新手,那么可能还不知道在两种互相竞争的标准OpenGL与Direct3D之间有一场 "战争"。

这是非常遗憾的,因为这两种标准都是可行的选择,而且他们都有各自的优点。

人们经常将OpenGL与DirectX相比,这是不公平的。

DirectX是一个来自微软公司的游戏技术SPI族,其中包括Direct3D这种由微软公司与游戏程序涉及而开发的渲染API。

一个人可能更喜欢自己的汉堡而不是别人的牛扒,但是拿别人的牛排和一家餐馆来比较是不公平的!

实际上,大多数使用OpenGL的Windows游戏同时还使用DirectX的非渲染组件来更方便地进行声音的回放、游戏手柄控制和联网游戏等。

Direct3D是微软公司专有的标准,被广泛地应用于Windows平台的游戏上,并且还有一些Direct3D的变体被用于XBox游戏控制台平台和一些Windows移动设备上。

在Direct3D发展的早期,这种API是非常不好用的,和OpenGL相比缺乏大量特征,并且存在一些固有的软件低效情况。

微软公司采取了一些有争议的策略来帮助Direct3D成为Windows游戏程序设置的 "标准",这种情况持续了几年,被人们称为 "API战争"。

在很多人看来,这场战争还在继续。

公平地说,微软公司到现在为止已经与硬件提供商和软件提供商合作了十多年,现在Direct3D已经成为一种有用且有良好口碑的API,并且在那些只对微软公司平台感兴趣的游戏程序设计人员非常流行。

但是,OpenGL在Windows游戏开发人员中仍然非常流行,并且是那些制作非游戏3D应用程序(例如: 视觉模拟(Vis-sim))行业、内容创建工具、科学可视化和商业图形等的软件开发人员的首选。

在OpenGL和Direct3D之间 "根据情感" 的选择常常可以归结为喜欢或不喜欢Direct3D的面向对象 COM (Component Object Model组件对象模型)方法和OpenGL的状态机抽象化,或者仅仅是出于喜欢或不喜欢微软公司。

在诸如Mac OSX、iPhone、Linux(不仅仅是桌面系统,大多数手持只能电话设备使用的都是UNIX的变体)和Sony或任天堂游戏设备这样的非微软平台上,OpenGL或者类型OpenGL的API则是实际上的标准。

如果我们将整个3D图形产业看作一个整体,OpenGL所占据的份额比Direct3D要大得多。

还有一些原因使我们可能选择OpenGL而不是Direct3D。

第一个原因是,OpenGL的跨平台和可移植的,并且几乎现有的所有3D硬件设备都有对应的OpenGL驱动。

如果读者对游戏感兴趣,那么就可以做一些市场调查。

Windows桌面系统在游戏产业中并没有占据大部分份额。

第二个原因是,OpenGL是一个开放的标准,它能从所有领先的3D硬件提供商的知识和经验中收益。

这些提供商必须合作,使OpenGL对开发人员而言是有吸引力和强大的,毕竟这些开发人员只做了促使人们购买他们硬件的软件。

由于OpenGL是 "到图形硬件的软件接口",让软件提供商参与规范的演变是必要的。

这就是人们可能选择OpenGL而不是Direct3D最后的也是最重要的原因——扩展机制。

扩展机制使硬件供应商不仅能在性能和图像质量方面进行竞争,同时还能在真正的技术革新上一较高下。

硬件提供商可以在硬件上加入新的特征,并且他们愿意的时候通过OpenGL来公开这些新特征。

他们不需要ARB的许可,不需要微软公司的许可,也不需要等待下一班的OpenGL(或Direct3D)发布。

在Direct3D领域没有这些条条框框。

微软公司通过代理来决定将什么加入API中,以及在某种程度上(有些情况应该说是不公平的)影响硬件的体系结构。

最新和最优秀的硬件特性总是能够通过供应商OpenGL驱动程序和相关扩展来轻松获得应用。

例如,当支持DirectX 10的硬件发布时,Windows用户就需要使用Windows Vista才能享受应用了最新DirectX 10特性的游戏。

但是,所有的新功能同时也在OpenGL上通过扩机制发布,并且如果Windows XP用户的游戏使用OpenGL,那么他们马上就鞥呢使用这些新功能,当然同时也能得到最新的硬件和驱动程序。

许多年来,诸如NVIDIA或ATI(现在的AMD)这样的提供商都可以通过OpenGL上编写的演示程序来展示他们最新的硬件革新技术,而他们也确实这样做了。

仅此一点就使OpenGL在最新和最优秀的3D硬件新技术应用方面始终保持稍稍领先Direct3D的优势。

 

"不鼓励使用" 的功能

十多年来,OpenGL标准通过在每个版本发布时加入新功能来不断演化着。

新功能通常通过扩展过程来进行审核的,在扩展过程中一些特征将作为提供商指定或提供商联合扩展而添加进来,这些特性将被进一步完善并成为ARB扩展,最终将进入核心API规范中。

在这个过程中从来没有哪些功能从OpenGL中被移除。这样就保证了对旧代码百分之百的向下兼容性,而且随着新硬件投入使用,现存应用程序只会运行得更快。

开发人员也能够轻松地逐步升级代码,以在最新的渲染技术或性能强劲的新功能退出时充分利用它们,而不必重写已经完成或正在编写的代码。

然而,实际上这种过程也将到此为止。

随着时间的流逝,GPU和计算机体系结构已经发生了巨大的变革。

15年来一直保持性能上的权衡与工程上的妥协如今已再难适用。

其结果就是,一些OpenGL API变得有些陈旧过时。

很多提供商都首次开始寻求通过移除那些在现代代码中几乎不再适用,或性能远远低下于最新技术的特性和功能来精简OpenGL API的规模。

最终,ARB决定以OpenGL 3.0为突破口,在OpenGL诞生以来首次抛弃一些负担,即过时的OpenGL API。

提供商仍然可以为了一些过时代码而继续支持OpenGL 2.1驱动程序,单定位于OpenGL 3.0或更新版本的最新应用程序应当抛弃旧的API函数和约定。

这在当时看来确实是一个不错的决定。

 

OpenGL 3.0

ARB 是由图形硬件提供商组成的,而提供商是有客户的,而且必须使这些客户满意。

很多客户(软件开发人员)意识到这种将OpenGL 2.1作为古董束之高阁的模式实际上意味着一件事情,这就是这些驱动程序对于提供商来说将迅速降至低优先级,并且在新的硬件上不会很好地保存或更新,而这些软件开发者将被迫浪费在OpenGL 上价值数百万美元的投资。

最终他们在次序上达成了妥协,即在OpenGL 3.0中,不会真正移除任何功能,而是将这些功能标记为 "不鼓励使用"。

"不鼓励使用"的功能将仍然保留在驱动程序中,但它们是作为一种通告来提供的,通知软件提供商应该停止使用某些OpenGL特性并转向更新的和更现代的工作方式。

据说在OpenGL 3.1中这些特性将被移除,或者说我们认为可能是这样。

 

OpenGL 3.x

OpenGL 3.1遇到了前所未有的苛刻要求。

这种要求会让任何圆滑的政客都感到确实太过苛刻。

确实,所有"不鼓励使用"功能都从OpenGL核心范围中移除了,但是却引入了一个新的OpenGL扩展GL_ARB_compatibility。

很多正在寻求一个更合理的API的 软件提供商将这个扩展看作仅仅是"加入了所有我们承诺移除但却没有做到的'不鼓励使用'OpenGL特性"。

这意味着一个硬件提供商至少可以选择在OpenGL 3.1驱动程序中不包含任何"不鼓励使用"功能。

但是,这种情况并没有发生。

ARB的成员之一NVIDIA公开声明不会移除任何老旧功能。

在某些种类应用程序(尤其是游戏)的开发人员开始指责这种行为的同时,我们应该客观地说,NVIDIA或者其他硬件提供商还鞥呢怎么做呢?

难道一个硬件提供商应该忽视它的客户而强制推行一种标准,仅仅是因为这家公司认为这样做最符合自己的利益嘛?

我们以前曾经遇到过这种情况。

这样做几乎得不到好的结果,而且没人希望在OpenGL社区中发生这样的丑闻。

OpenGL 3.2在这件事情上做的漂亮了些,它废除了这个扩展,取而代之将OpenGL分成了核心框架和完整框架。

核心框架规范将更加精简,并且不包含任何老旧的"不鼓励使用"功能。

规范的一致性要求具有核心功能,但将兼容框架列为可选项。

事实上,那些"不鼓励使用"OpenGL特性对于如今使用OpenGL的绝大多数开发人员来说要容易理解得多。

这些特性中有很多可能会比最新的方法慢,但是它们更加容易使用,而且非常方便。

工程师们都知道,在简易性、可实现性、可维护性和开发人员熟练程度,当然还有性能之间经常要做出权衡。

性能并不是在所有类型的应用程序开发中都是绝对主要的考虑因素。

兼容框架看起来还要存在相当长的时间。

 

只有核心

那么,这种情况会给我们带来什么影响呢?

本书的第4版涵盖了OpenGL 2.1的内容,这个版本是允许选择使用着色器的固定管线的经典OpenGL实现。

核心框架则是它的最简形式,"只有着色器"。

这样我们无论做什么,都需要编写一个着色器。

这里没有内建的光照模式,没有方便的矩阵堆栈,没有简单的纹理应用程序,也没有轻松编写代码的立即模式来传输顶点数据。

实际上,一些几何图元也被削减掉了。

难怪众多开发人员并不急于让他们的代码"现代化"了。

让事情变得更糟的是,迄今为止的大多数教程和图书都专注于展示如何从固定管线移植到着色器,似乎这是唯一的方式。

这当然就意味着对于新入行的OpenGL程序员来说掌握OpenGL最简单的方式就是先从固定功能开始,然后再想着色器过渡。

只是这并不是一种促进新的OpenGL核心框架应用的生产方式,也不算本书所采用的方式。

 

2.2 使用OpenGL

OpenGL是一种过程性而不是描述性的图形API。

事实上,程序员并不需要描述场景和它的外观,而是事先确定一些操作步骤,实现一定的外观或效果。

这些步骤需要调用许多OpenGL命令。

这些命令可以在三维空间中绘制各种图元,例如: 点、直线和三角形等。

另外,OpenGL还支持纹理贴图、混合、透明、动画以及其他许多特殊的效果和功能。

关于这些如何实现的具体内容将在第3章详细介绍。

本章主要关注如何建立并运行OpenGL项目。

OpenGL并不包含任何负责窗口管理、用户交互或文件I/O的函数。

每个宿主环境(例如Mac OSX或Microsoft Windows)都提供了一些函数实现这些功能,并且负责实现一些方法,向OpenGL移交窗口绘制的控制。

我们无法使用类似 "OpenGL文件格式" 这样的东西来表示模型或虚拟环境,因为它们并不存在。

程序员构造这些环境以适合自己的高层需要,然后使用底层的OpenGL命令精心地对它们进行编程。

 

2.2.1 支持阵容

任何计算机程序都必须包含一些除渲染操作以外的其他东西才能使用。

用户必须通过某种方式来使用键盘、鼠标、游戏手柄或其他一些输入机制来与程序进行互动。

此外,必须打开并保持窗口(大多数但并非全部操作系统中都是如此),找到并载入文件等。

C和C++都是良好的可移植程序设计语言,它们如今在大多数平台上都适用。

但是,在典型的程序中,编程语言要使用API来完成大量工作。

遗憾的是,与操作系统连接意味着与用户或在屏幕中的管理窗口的互动大多数情况下常常是由不可移植的操作系统特定API完成的。

 

GLUT

首先出现的是AUX,也就是OpenGL辅助函数库。

AUX函数库的目的是帮助人们学习和编写OpenGL程序,而不必为任何平台特定环境的细枝末节而分神,不必顾虑所使用的是UNIX、Windows还是其他平台。

如果使用AUX,我们不是编写"最终"代码,它更像是一个预备阶段,对自己的想法进行测试。

由于缺乏基础的GUI特性,这就限制了这个函数库在创建实用的应用程序方面的应用。

在跨平台的示例程序和演示程序中,AUX渐渐为GLUT函数库所取代。

GLUT代表OpenGL实用工具箱(OpenGL utility toolkit, 不要与标准的GLU——OpenGL utility library, 即OpenGL实用库混淆)。

Mark Kilgard在SGI时编写了GLUT,把它作为AUX函数库的一个功能更强的替代品,并添加了一些GUI功能,至少使示例程序在 X Window下显得更为实用。

它的改进包括实用了弹出式菜单、增加了对其他窗口的管理,甚至提供了对操纵杆的支持。

GLUT并不是一个公众领域的产品,但它是免费的,并且可以自由地进行重新发布。

GLUT在绝大多数UNIX系统(包括Linux)中都得到了支持,并且得到了Mac OSX的本地支持,Apple对这个函数库进行了维护和扩展。

在Windows中,GLUT的开发已经中断。

由于GLUT最初并不是作为一种开源代码的软件,因此一种新的GLUT实现freeglut已经崛起并取代了它的位置。

在本书中,所有基于GLUT的Windows示例程序都利用了freeglut函数库。

读者也可以通过本书的网站下载这个函数库。

在绝大多数情况下,本书使用GLUT作为编程框架。

这出于两个目的。

首先,它可以使本书面向更广的读者。

只要稍下功夫,有经验的Windows、Linux或Mac程序员应该很容易在编程环境中设置GLUT,并且顺序地创建本书的绝大多数示例程序。

第二个目的就是使用GLUT可以使读者不必了解任何特定平台的基本GUI编程。

尽管我们解释了一些基本的GUI概念,但本书并不是一本讲诉GUI编程的书籍,而是专门讲诉OpenGL的。

把OpenGL API的范围限制在GLUT,Windows/Mac/Linux新手也更容易上手。

商业应用程序的所有功能不可能全部包含在3D绘图代码之中。

虽然GLUT确实包含一些有限的GUI功能,但是非常简单和精简,就像GUI的工具包一样,因此我们不能依赖GLUT函数库来完成应用程序的所有任务。

然而,GLUT函数具有非常优秀的学习和演示功能,并且隐藏了像窗口创建和OpenGL环境初始化等平台特定的细节。

即使是经验丰富的程序员,把3D图形集成到完整的应用程序之前,使用GLUT函数来整理3D图形代码也是非常方便的。

 

GLEW

正如前面所提到的,OpenGL API主要通过扩展机制来发展。

这种扩展机制能够用来获得指向任何加入OpenGL 1.0之后任何版本核心的OpenGL函数的函数指针。

有一个实现OpenGL 3.3 API完全存取的简单方法,就是使用一个自动初始化所有新函数指针并包含所需类型定义、常量和枚举的扩展加载库。

不止一种这样的扩展加载库可供选择,其中一种维护最好的开源库就是GLEW。

通过驱动程序使用这种库依赖初始化全部可用的OpenGL功能并不太容易。

我们需要在项目中添加一个单独的C源文件及头文件,并且在程序启动时调用一个单独的初始化函数。

稍后开始编写我们的第一个OpenGL程序时将讨论相关细节。

为了使事情更简单,GLEW将预先封装在了GLTools库中。

实际上,GLTools库就是基于GLEW库的。

 

GLTools

每一个工匠都有一个工具箱,里面装满了自己喜爱的工具,程序员也是如此。

有一些有用并且可重用的函数,所有程序员在编写几乎所有OpenGL时都要用到它们。

GLTools是在本书第3版出现的。

随着时间的流逝,这个库已经逐渐发展起来,并提供许多快捷方式和便捷的工具,就像过去的OpenGL应用库(GLU)那样。

GLTools包含一个用于操作矩阵和向量的3D数学库,并依赖GLEW获得OpenGL 3.3中用来产生和渲染一些简单3D对象的函数,以及对视觉平截头体、相机类和变换矩阵进行管理的函数的充分支持。

 

2.2.2 OpenGL API 特性

OpenGL是由一些充满智慧的人设计的,他们拥有丰富的图形程序设计API设计经验。

他们在函数命名和变量声明方法上面采用了一些标准规则。

API简单清晰,便于提供商进行扩展,并且便于程序员记忆。

OpenGL试图尽可能地避免策略。

这里的策略是指设计者做出的关于程序员如何使用API的假设。

这使OpenGL能够保持灵活、强大和快速。

我们只要灵活地运用API和着色语言,就可以真正地发明一种全新的方法来渲染一个特效或场景。

这种理念为OpenGL的长寿和精华做出了贡献。

即使如此,随着时间的推移,硬件性能出乎意料的发展和开发人员与硬件提供商的创造力在OpenGL经历了这些年的发展后还是给它带来了负面的影响。

尽管如此,OpenGL的基础API仍然显示出对新兴的不可预测特征的适应能力。

只作很少甚至不作改动就能编译十年前源码的能力对于众多应用程序开发人员来说是最重要的优势,而OpenGL多年来一直坚持做到加入新特性的同时尽量少地与旧代码发生冲突。

现在,我们有了更加简洁和现代化的OpenGL,可以重新开始这个过程了。

 

数据类型

为了使OpenGL代码更易从一个平台移植到另一个平台,OpenGL定义了数据类型。

这些数据类型可以映射到所有平台的特定最小格式。

各种编译器和环境都有自己的规则来定义各种变量类型的大小的内存布局,因此通过使用OpenGL定义的变量类型,可以使代码避免因为类型在变量表示上不一致所带来的的影响。

表2.2列出了OpenGL数据类型和最小位宽。

表2.2 OpenGL变量类型和最小位宽
OpenGL数据类型最小位宽描    述
GLboolean1布尔值,真或假
GLbyte8有符号8位整数
GLubyte8无符号8位整数
GLchar8字符串
GLshort16有符号16位整数
GLushort16无符号16位整数
GLhalf16

半精度浮点数

GLint32有符号32位整数
GLuint32无符号32位整数
GLsizei32无符号32位整数
GLenum32无符号32位整数
GLfloat3232位浮点数
GLclampf32[0, 1]范围内的32位浮点数
GLbitfield32

32位

GLdouble6464位双精度浮点数
GLclampd64[0, 1]范围内的64位双精度浮点数
GLint6464有符号64位整数
GLuint6464

无符号64位整数

GLsizeiptr本地指针大小无符号整数
GLintptr本地指针大小有符号整数
GLsync本地指针大小同步对象句柄

所有的数据类型都以GL开头,表示OpenGL。

函数后面是他们的最小位宽和相关描述,请注意它们并没有必要直接与C数据类型直接对应。

OpenGL规范要求这些数据类型所需要的最小存储空间参见表2.2。

但是,虽然某些数值超出表中之处的范围是可能的,但只有大小在指定范围内的数值对OpenGL来说才是有意义的。

请注意,有些类型前面还有个字母u,表示这是一种无符号数据类型。

例如,ubyte表示无符号byte类型。在某些应用中,还有一些更具描述性的名称,就像size表示一个数值的长度或深度那样。

例如,GLsizei是一个OpenGL变量类型,表示整数形式的size参数。

名称Clamp则是一种提示,表示这个值的范围将"截取"在 0.0 ~ 1.0的范围内。

GLboolean变量表示真假条件。

GLenum表示枚举变量。

GLbitfield表示那些包含二进制位段的变量等。

OpenGL并没有对指针和数组作特殊的考虑。

我们可以像下面这样声明一个包含10个GLshort变量的数组。

GLshort shorts[10];

下面这行代码则声明了一个长度为10的指向GLdouble类型变量的指针数组。

GLdouble *doubles[10];

 

2.2.3 OpenGL 错误

在任何项目中,我们都希望编写出表现良好的程序,能够友好地相应用户,并且有一定程度的灵活性。

使用OpenGL的图形程序也不例外,而且如果我们希望程序能够流畅运行,就需要考虑程序可能出现的错误以及一些出乎意料的情况。

OpenGL提供了一种有用的机制,可以在代码中执行一种偶然健全性检查。

这个功能是非常重要的。

例如,单纯从代码的角度而言,要分辨程序的输出到底是"空间站自由度"还是"空间站融化的蜡笔"几乎是不可能的!

OpenGL在内部保留了一组错误标志(共4个),其中每个标志代表一种不同类型的错误。

当一个错误发生时,与这个错误相对应的标志就会被设置。

为了观察哪些标志被设置,可以调用 glGetError 函数。

GLenum glGetError(void);

glGetError 函数返回表2.3所列出的其中一个值。

如果被设置的标志不止一个,glGetError 仍然只返回一个唯一的值。

当 glGetError 函数被调用时,这个值随后被清除,然后在 glGetError 在此被调用时将返回一个错误标志或GL_NO_ERROR。

通常情况下,我们需要在一个循环中调用 glGetError 函数,持续检查错误标志,直到返回值是GL_NO_ERROR为止。

表2.3 OpenGL错误代码
错误代码描    述
GL_INVALID_ENUM枚举参数超出范围
GL_INVALID_VALUE数值参数超出范围
GL_INVALID_OPERATION在当前的状态中操作非法
GL_OUT_OF_MEMORY没有足够的内存来执行这条命令
GL_NO_ERROR没有错误出现

如果一个错误是由于对OpenGL的非法调用所致,那么这条命令或函数调用将会被忽略。

对此,我们可能会稍微感到安心,此时,唯一可能造成麻烦的是那些接受指向内存的指针作为参数的函数(如果指针无效,可能导致程序崩溃)。

 

2.2.4 确认版本

如前面所述,有时候我们希望利用一个特定实现中的一些已知行为。

如果我们确实知道程序将运行于一个特定提供商所生产的图形卡纸上,就想依赖这个生产商特有的一些性能特征来强化程序。

我们可能还希望限制这个特定厂商所提供的驱动程序的最低版本。

为此,需要查询OpenGL的渲染引擎(OpenGL驱动程序)的生产商和版本号。

GL函数库可以通过调用glGetString来返回与它们的版本号和生产商有关的特定信息。

const GLubyte *glGetString(GLenum name);

这个函数返回一个静态的字符串,描述GL函数库中所请求的信息。

附录C中列出了glGetString条目下所有合法的参数值,以及它们所代表的的GL函数库的相关信息。

 

2.2.5 使用glHint获取线索

俗话说,给猫剥皮的方法不止一种。

在3D图形算法中,情况也是如此。

例如,为了追求高性能,我们常常需要做一些权衡。

或者,如果视觉逼真度是最重要的因素,那么性能就会退居其次。

一种OpenGL实现常常包含两种方法来执行一个特定的任务.

一种是快速的方法,在性能上稍作妥协.

另一种是慢速的方法,着重于改进视觉质量。

glHint函数允许我们指定偏重于视觉质量还是速度,以适应各种不同类型的操作需求。

这个函数定义如下所示:

void glHint(GLenum target, GLenum mode);

我们可以在target参数中指定希望进行修改的行为类型。

附录C中的glHint条目下列出了这些值,其中还包括关于纹理压缩质量和抗锯齿准确性等的提示。

mode参数告诉OpenGL我们最为关心的,例如更快的渲染速度还是最好的输出质量,

或者我们可能并不关心这些(只有在这种情况下我们才会使用默认行为)。

但是,我们还是应该小心在意,因为所有的OpenGL实现都不要求必须在glHint函数的调用上保持一致。

在OpenGL中,这是唯一一个行为完全依赖生产商的函数。

 

2.2.6 OpenGL状态机

绘制3D图形是一项复杂的任务。

在接下来的章节,我们将讨论许多OpenGL函数。

对于一个特定的几何图形,有许多因素可能会影响它的绘制。

对象是不是与背景混合?

要不要进行正面或背面剔除?

当前限制的是什么纹理?

这样的问题数不胜数。

我们把这类变量的集合称为管线。
状态机是一个抽象的模型,表示一组状态变量的集合。

每个状态变量可以有各种不同的值,或者只能可以打开或关闭等。

当我们在OpenGL中进行绘图时,如果每次都要指定所有这些变量显然有点不切实际。

反之,OpenGL使用了一种状态模型(或称状态机)来追踪所有OpenGL状态变量。

当一个状态值被设置之后,他就一直保持这个状态,知道其他函数对它进行修改为止。

许多状态只能简单地打开或关闭。

例如,深度测试(参见第3章)就是打开和关闭。

打开深度测试的几何图形将会被检查以确保在进行渲染之前总会在任何位于它后面的对象前方。

在深度测试关闭后进行的集合图形绘制(例如2D覆盖)则会不进行深度比较的情况下进行绘制 。

(这部分的内容是关于深度测试的,此处为书本为此做的简单介绍,不懂的详看第3章即可)

为了打开这些类型的状态变量,可以使用下面这个OpenGL函数。

void glEnable(GLenum capability);

我们可以使用下面这个对应的函数,把这些变量的状态设置为关闭。

void glDisable(GLenum capability);

以深度测试为例,可以使用下面这个函数调用打开深度测试。

glEnable(GL_DEPTH_TEST);

也可以使用下面这个函数调用关闭深度测试。

glDisable(GL_DEPTH_TEST);

如果希望对一个状态变量进行查询,以判断它是否已被打开,OpenGL还提供了一种方便的机制。

GLboolean glIsEnabled(GLenum capability);

但是,并不是所有的状态变量都只是简单地打开或关闭。

许多OpenGL函数专门用于设置变量的值,以后这些变量一直保持被设置时的值,知道再次被修改。

我们在任何时候都可以查询这些变量的值。

OpenGL提供了一组查询函数,可以查询布尔值、整数、单精度浮点数和双精度浮点数型变量的值。

这4个函数的原型如下所示:

void glGetBooleanv(GLenum pname, GLboolean *params);    // 布尔值
void glGetDoublev(GLenum pname, GLdouble *params);      // 双精度浮点型
void glGetFloatv(GLenum pname, GLfloat *params);        // 单精度浮点型
void glGetIntegerv(GLenum pname, GLint *params);        // 整形

PS: 简单说一下这几个函数的命名吧(懂的此处忽略吧~)

glGet + 类型 + v(variable(变量)) = glGet + Boolean + v = glGetBooleanv

每个函数都会返回单个值,或者返回一个数组,把一些值存储到我们执行的地址中。

附录C的参考部分列出了各种不同的参数(数量非常多)。

现在,读者可能还无法理解其中的大多数参数,但是随着对本书学习的深入,读者将会逐渐开始欣赏OpenGL状态机的简洁和强大。

 

2.3 建立Windows项目

此处我当大家都会了,就忽略了哈~

(如果不会的话,评论留言吧,因为本篇文章已经很长了,在加上这个建立的介绍可能就更长了,有需要再单独开一篇介绍吧)

 

2.4 建立 Mac OS X项目

.......

 

2.5 第一个三角形(终于开始OpenGL的编写了)

现在我们已经打好了基础,终于开始编写代码了!

我们的第一个示例程序仅仅是在蓝色的背景上绘制一个红色的三角形。

这咋看起来似乎没有什么挑战性,但是它实践了所有必要的步骤,并创建了一个完整的颜色框架供本书以后使用。

在创建过程中,我们能够学习GLUT,并使用一个GLTools帮助程序和类。

我们的三角形程序如图2.17所示,而程序清单2.1则完整地列出了我们的第一个程序。

接下来我们将一行一行地讨论它。

(图2.17 [书])

(图2.17 [VS2017])

程序清单2.1 简单绘制一个三角形

(代码略..., 下面会进行拆分讲解,具体代码可以看本章末尾)

 

2.5.1 要包含什么

在编写任何C++(或者只是C)程序之前,都要先将要用到的函数和类定义的头文件包含起来。

为了达到目的,最低限度也要包含如下头文件。

#include <GL/glew.h>       // OpenGL toolkit
#include "GLTools.h"#include "GLShaderManager.h"
#ifdef __APPLE__
#include "glut/glut.h"     // OS X version of GLUT
#else
#define FREEGLUT_STATIC
#include "GL/glut.h"       // Windows FreeGlut equivalent
#endif

GLTools.h头文件中包含了大部分GLTools中类似C语言的独立函数,而每个GLTools的C++类则有自己的头文件。

GLShaderManager.h移入了GLTools着色器管理器(Shader Manager)类。

没有着色器,我们就补鞥呢在OpenGL(核心框架)中进行着色。

着色器管理器不仅允许我们创建并管理着色器,还提供了一组"存储着色器"(Stock Shader),它们能够进行一些初步和基础的渲染操作。

在第3章中我们将详细讨论这部分内容。

根据应用程序是否是在Mac上创建的,GLUT将采取不同的处理方式。

在Windows和Linux上,我们使用freeglut的静态库版本,这就需要在它前面添加FREEGLUT_STATIC处理宏。

 

2.5.2 启动GLUT

下面我们直接跳到程序清单的最后一个函数,即所有C程序的入口点,这里才是程序处理实际开始的地方。

// Main entry point for GLUT based programs
int main(int argc, char* argv[])

控制台模式的C语言和C++程序总是从"main"函数开始处理。

对于有经验的Windows迷来说,在本例中找不到WinMain函数是十分奇怪的。

这是因为哦们是以一个控制台模式的应用程序开始的,所以没必要从创建窗口和消息循环开始。

在Win32中,我们可以从控制台应用程序简历图形窗口,就像我们可以从GUI应用程序简历控制台窗口一样。

GLUT库隐藏了这些细节(请记住,GLUT库就是设计用来隐藏这些平台相关细节的)。

(从而更方便我们学习OpenGL,而不必在去学习诸如Win32等等的知识)

 

gltSetWorkingDirectory(argv[0]);

GLTools函数glSetWorkingDirectory用来设置当前工作目录。

实际上在Windows中是不必要的,因为工作目录默认就是程序的可执行程序相同的目录。

但是在Mac OS X中,这个程序将当前工作文件夹改为应用捆绑包(Application Bundle)中的/Resource文件夹。

GLUT的优先设定自动进行了这种设置,但是这种方法更加安全,也总是奏效的,即使其他程序改变了这项设置时也是如此。

这在我们以后想要载入纹理文件或模型数据时会派上用场。

接下来,我们将进行一些基于GLUT的标准设置。

 

glutInit(&argc, argv);

首先要调用glutinit函数,这个函数只是传输命令行参数并初始化GLUT库。

 

glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_STENCIL);

然后我们必须告诉GLUT库,在创建窗口时要使用哪种类型的显示模式。

这里的标志告诉它要使用双缓冲窗口(GLUT_DOUBLE)和RGBA颜色模式(GLUT_RGBA)。

双缓冲窗口是指绘制图形命令实际上是在离屏缓冲区执行的,然后迅速切换成窗口视图。

这种方式经常用来生成动画效果,本章稍后将做演示。

GLUT_DEPTH位标志将一个深度缓冲区分配为显示的一部分,因此我们能够执行深度测试。

同样GLUT_STENCIL确保我们也会有一个可用的模板缓冲区。

深度和模板测试后面都会讲解。

 

glutInitWindowSize(800, 600);    // 宽高为 800 x 600 的窗口

接下来,我们要告诉GLUT窗口的大小

 

glutCreateWindow("Triangle");

并继续创建以 "Triangle" 为标题窗口。

 

GLUT内部运行一个本地消息循环,拦截适当的消息,然后调用我们为不同时间注册的回调函数。

与使用真正的系统特定框架相比有一定局限性,但是大大简化了组织并运行一个程序的过程,并且支持一个演示框架的最低限度的事件。

在这里,我们必须为窗口改变大小而设置一个回调函数,以便能够设置视点,还要注册一个函数以包含OpenGL渲染代码。

glutReshapeFunc(ChangeSize);
glutDisplayFunc(RenderScene);

ChangeSize和RenderScene函数很快就会讲到,但是在开始运行主消息循环之前,还要解决两件事情。

第一件事情就是初始化GLEW库。

重新调用GLEW库初始化OpenGL驱动程序中所有丢失的入口点,以确保OpenGL API对我们来说完全可用。

调用glewInit一次就能完成这一步,在试图做任何渲染之前,还要检查确定驱动程序的初始化过程中没有出现任何问题。

GLenum err = glewInit();
if (GLEW_OK != err) {fprintf(stderr, "GLEW ERROR:%s\n", glewGetErrorString(err));return 1;
}

 

最后一项准备工作就是调用SetupRC。

SetupRC();

实际上这个函数对GLUT没有什么影响,但是在实际开始渲染之前,我们在这里进行任何OpenGL初始化都非常方便。

这里的RC代表渲染环境(Render Context),这是一个运行中的OpenGL状态的句柄。

在任何OpenGL函数起作用之前必须创建一个渲染环境,而GLUT在我们第一次创建窗口时就完成了这项工作。

 关于特定操作的章节(第13章到第16章)中会研究这方面的更多细节。

纵观全书,我们将在这里进行预加载纹理,简历几何图形,渲染器等工作。

最后我们可以开始主消息循环并结束main函数了。

glutMainLoop();

glutMainLoop函数被调用之后,在主窗口被关闭之前都不会返回,并且一个应用程序中只需要调用一次。

这个函数负责处理所有操作系统特定的消息、按键动作等,知道我们关闭程序为止。它还能确保我们注册的这些回调函数被正确地调用。

 

2.5.3 坐标系基础

在早期的所有窗口化环境中,用户可以在任何时候改变窗口的大小和维度。

甚至在编写一个总是运行在全屏模式下的游戏时,窗口仍然认为至少改变一次窗口大小——就是在窗口创建时。

在进行这些改变时,窗口通常会根据新的维度相应重绘它的内容。

有时候,我们可能想要在一个小的窗口中截取绘图内容,或者在原始的大窗口中显示完整的绘图内容。

为了达到目的,我们常常希望缩放绘图内容来适应窗口,不论绘图内容和窗口的大小如何。

这样一个很小的窗口可以显示一个完整但是很小的绘图内容,而一个很大的窗口也可以显示相似但是更大的绘图内容。

在第一章,我们讨论了视口和视景体是如何影响2D和4D绘图内容在计算机屏幕上2D窗口上的坐标范围和缩放的。

现在,我们来讨论OpenGL中视口和裁剪区域坐标(Clipping Volume Coordinate)。

在某种程度上,设置坐标系是绘制对象将它们显示到我们希望的屏幕位置的先决条件!

尽管我们所绘制的图形是一个2D的平面三角形,但它实际是一个3D坐标空间中绘制的。

在本章中,我们将使用默认的笛卡尔坐标系统,这个坐标系统在x、y和z方向上从 -1 到 +1 延伸。

x是坐标系的横坐标,y是纵坐标,而z轴正方向从屏幕向外指向使用者。

坐标(0, 0, 0)则位于屏幕的正中。

在第4章,我们将更细致地讨论关于建立替代坐标系的问题。

为了达到目的,我们在z=0的xy平面上绘制三角形。

我们视角是从z轴的正半轴看去,所看到的是z=0情况下的三角形(如果读者对这方面的内容感到困惑,可以回顾第1章的相关资料)

图2.18所示是基本笛卡尔坐标系的外观。

许多绘图和图形库都是用窗口坐标(像素)来完成绘制命令。

使用实数浮点(这看上去有点随意)坐标系统进行渲染,这常常令许多新手非常不习惯。

不过,在创建了几个程序之后,读者很快就会对此习以为常。

(图2.18)

 

定义视口

由于在不同环境下窗口的大小改变的检测和处理方式也不同,GLUT库为此专门提供了glutReshapeFunc函数,这个函数注册了一个回调,供GLUT库在窗口维度改变时调用。

我们传递到glutReshapeFunc的函数原型如下:

void ChangeSize(int w, int h)

我们选择ChangeSize作为这个函数的描述名称,并且在以后的示例中也会使用这个名称。

void ChangeSize(int w, int h)
{glViewport(0, 0, w, h);
}

ChangeSize函数在窗口大小改变时接受新的宽度和高度。

我们可以使用这个信息,在OpenGL函数glViewort的帮助下修改从目的坐标系到屏幕坐标系上的映射。

要理解视口分辨率,让我们更仔细地观察ChangeSize函数中调用带有窗口的新宽度和高度的glViewport函数的部分。

glViewport函数定义如下:

void glViewport (GLint x, GLint y, GLsizei width, GLsizei height);

其中x参数和y参数代表窗口中视口的左下角坐标,而宽度和高度参数是用像素表示的。

通常x和y都为0,但是我们可以使用视口在窗口中的不同区域渲染多个图形。

视口以实际屏幕坐标定义了窗口中的区域,OpenGL可以在这个区域中进行绘图(如图2.19所示)的裁剪区域被映射到新的窗口。

如果指定了一个比窗口坐标更小的视口,渲染区域就会缩小,如图2.19所示。

(图2.19)

 

 

从笛卡尔坐标系到像素

在开始将几何图形光栅化(实际绘制)到屏幕上时,OpenGL负责笛卡尔坐标系和窗口像素间的映射。

我们要牢记一点,就是改变视口并不会改变基础坐标系。

由于我们采用的是默认的从 -1 到 +1 的映射,为三角形改变窗口大小会产生一些有趣的结果,如图2.20所示。

(图2.20)

图2.20左侧图中,我们可以看到 +1 到 -1 的范围在垂直方向是如何比水平方向延伸得更多的,而在右侧图中,则可以看到相反的效果。

我们要先了解更多内容,然后才能考虑如何改变坐标系以影响窗口大小的改变,就像前面说过的,我们会在第4章完整地做完这项工作。

 

2.5.4 完成设置

在开始main函数中的GLUT主循环之前,我们先调用SetupRC函数。

这时我们要为程序做一些一次性的设置。

首先要做的就是通过以下调用来设置背景颜色。

glClearColor(0.0f, 1.0f, 1.0f, 1.0f);

这个函数设置用来进行窗口清除的颜色,它的函数原型如下所示。

void glClearColor (GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha);

在大多数OpenGL实现下,GLclampf都被定义为一个浮点数。

每个参数都包含最终颜色所要求的这种颜色分量的权值。

这个函数不会立即清除背景,而是设置在以后颜色缓冲区被清除(可能是重复的)时使用的颜色

 

RGB颜色空间

在OpenGL中,某种颜色是由红、绿、蓝和Alpha(用于表示透明度)分量混合而成的。

每种分量值的范围从0.0到1.0。

这类似在Windows中使用RGB宏创建COLORREF值的方式。

不同的是,在Windows中,COLORREF值的每种颜色成分范围在0至255之间,总共可以产生256 x 256 x 256(超过1600万)种颜色。

在OpenGL中,每种成分的值可以是0至1之间任何有效的浮点数,因此理论上可以产生的颜色数量是无限的。

从现实的角度将,在绝大多数设备中,颜色的输出限制在24为(1600万种以上颜色)。

很自然OpenGL接受这个颜色值,并在内部把它转换为能够与可用的视频硬件准确匹配的最接近颜色。

表2.2列出了一些常见的颜色以及它们的分量值。

我们可以在任何与颜色相关的OpenGL函数中使用这些值。

表2.4 一些常见的组合颜色
组合颜色红色分量绿色分量蓝色分量
Black (黑)0.00.00.0
Red (红)1.00.00.0
Green (绿)0.01.00.0
Yellow (黄)1.01.00.0
Blue (蓝)0.00.01.0
Magenta (洋红)1.00.01.0
Cyan (青)0.01.01.0
Dark gray (深灰)0.250.250.25
Light gray (浅灰)0.750.750.75
Brown (褐)0.600.400.12
Pumpkin orange (南瓜橙)0.980.6250.12
Pastel pink (粉红)0.980.040.70
Bamey purple (巴尼紫)0.600.400.70
White (白)1.01.01.0

glClearColor的最后一个参数是alpha分量,它用来进行混合,并且可以产生一些特殊的效果。

例如,透明。

透明是指一个物体运行光线穿过它。

假定我们希望创建一块染成红色的玻璃,并且它的后面正好有一束蓝色的光。

这道蓝光就会影响这块玻璃上的红色(蓝+红=紫)。

我们可以用alpha成分值生成一种半透明的红色,使它看上去像是一块玻璃,它后面的物体也能够显示。

这种类型的效果并不是仅仅靠使用alpha值就行了。

在第3张,我们将详细讨论这个话题。

在这之前,可以一直把alpha值设置为1。

 

存储着色器

没有着色器,在OpenGL核心框架中就无法进行任何渲染。

在第6章"跳出 '盒子':非存储着色器"中,我们将讨论如何编写着色器,以及如何编译和链接它们从而使它们可用。

在那之前,我们先使用一些简单存储着色器,它们可以用着色器管理器进行管理。

我们要在源文件的开头部分声明一个着色器管理器的实例,如下所示:

GLShaderManager shaderManager;

我们也可以在第3章来熟悉这些着色器,并学习如何使用它们。

但是着色器管理器需要编译和链接它自己的着色器,所以我们必须在OpenGL初始化时调用InitializeStockShaders方法。

shaderManager.InitializeStockShaders();

 

指定顶点

接下来我们要做的是设置三角形。

在OpenGL中三角形是一种"图元"类型,是一种基本的3D绘图元素。

在第3章,我们会非常详细地讨论在OpenGL将会用到的所有7种图元。

但在这里,我们只要了解一个三角形图元就是空间中的一系列组成一个三角形的顶点或点就可以了。

我们通过将这些顶点放进一个单精度浮点数组来指定它们。

这个数组命名为vVerts,其中包含所有3个顶点的x、y、z笛卡尔坐标系。

请注意我们将所有3个点的z坐标都设为0.

GLfloat vVerts[] = {-0.5f, 0.0f, 0.0f,0.5f,  0.0f, 0.0f,0.0f,  0.5f, 0.0f
};

在本书中有两章会讲解关于提交一个批次的顶点用于渲染的内容,即第3章和第12章,其中第12章将涉及更多的底层细节。

一个简单的GLTool封装了(Wrapper Class)会将三角形顶点批次进行封装,而我们则在源文件顶部附近声明一个这个GLBatch类的实例。

GLBatch triangleBatch;

在我们的设置函数中,下列代码建立了一个三角形的批次,仅包含3个顶点。

在第3章我们将对此做进一步的讨论。

triangleBatch.Begin(GL_TRIANGLES, 3);
triangleBatch.CopyVertexData3f(vVerts);
triangleBatch.End();

 

2.5.5 言归正传

最后,我们终于可以真正开始渲染了!

前面我们将清除颜色设为蓝色,现在我们需要执行一个函数真正进行清除。

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);

glClear函数清除一个或一组特定的缓冲区。

缓冲区是一块存储图像信息的存储空间。

红色、绿色、蓝色和alpha分量通常一起作为颜色缓冲区或像素缓冲区引用。

在OpenGL中有不止一种缓冲区(颜色缓冲区、深度缓冲区和模板缓冲区)供使用。

在本书后面的内容中将详细介绍这些缓冲区。

在前面的示例中,我们使用按位或(Bitwise OR)操作来同时清除所有这3种缓冲区。

在接下来的几章中,我们真正需要理解的是,颜色缓冲区是显示图像在内部存储的地方,以及通过glClear清除缓冲区会将屏幕上最后绘制的内容剔除。

我们还会看到术语"帧缓冲区"(Framebuffer),指的是所有这些缓冲区一起串联工作。

下面的3行代码将真正执行操作,这又是整个第3章的主要课题!

我们设置一组浮点数来表示红色(其alpha值设为1.0),并将它传递到存储着色器,即GLT_SHADER_IDENTITY着色器。

这个着色器只是使用指定颜色以默认笛卡尔坐标系在屏幕渲染几何图形。

//设置一组浮点数来表示红色
GLfloat vRed[] = { 1.0f, 0.0f, 0.0f, 1.0f };//传递到存储着色器
shaderManager.UseStockShader(GLT_SHADER_IDENTITY, vRed);//将几何图形提交到着色器
triangleBatch.Draw();

GLBatch的Draw方法指示将集合图形提交到着色器,然后.......啊哈!——红色三角形.........哦,差不多是。

还有最后一个细节。

当设置OpenGL窗口时,我们指定要一个双缓存区的渲染环境。

这就意味着将在后台缓冲区进行渲染,然后在结束时交换到前台。

这种形式能够防止观察者看到可能伴随着动画帧与动画帧之前闪烁的渲染过程。

缓冲区交换将以平台特定的方式进行,但是GLUT有一个单独的函数调用可以完成这项工作。

//指定一个双缓冲区渲染环境,在后台缓冲区进行渲染,然后再结束时交换到前台
glutSwapBuffers();

现在我们可以鞠躬谢幕了。我们已经用OpenGL渲染了第一个三角形。

 

2.6 加一(亿)点儿活力!

现在我们已经了解如何使GLUT完成一个他U型演示框架所能完成的最重要工作,也就是在屏幕上渲染图形了。

我们可以再加入一点小功能,使它能让用户对渲染进行一些互动,例如,通过按箭头↑←↓→键进行移动图形。

一点动画效果就能使图形演示变得有活力。

示例程序的 "Move" 就能做到这一点。

它在窗口的正中绘制了一个正方形(实际上我们使用了另一个图元,这次是GL_TRIANGLE_FUN)。

在按箭头键时,正方形将上下或左右移动。

分别用向上箭头和向下箭头中的哪一个来实现这两种运动则由读者决定。

 

2.6.1 特殊按键

GLUT还提供了另一个回调函数,即 glutSpecialFunc。

它注册了一个能够在按一个特殊按键时被调用的函数。

在GLUT的语法中,特殊按键是指功能键或者方向键(↑←↓→,Page up/down等)中的一个。

在主函数中加入下面的代码行,来注册SpecialKeys回调函数。

glutSpecialFunc(SpecialKeys);

它在按键时接受一个相应的按键编码,以及在使用鼠标时光标的x和y坐标位置(像素形式)。

在 "Move" 示例程序中,我们将顶点存储在一个全局(对于这个模型来说)数组中,这样我们就能够在按键时相应修改正方形的位置了。

程序清单2.2展示了SpecialKeys函数的完整代码,这里我们还进行了碰撞检测,这样正方形就不会移出窗口范围了。

请注意,我们可以轻松地更新批次位置,只需复制新的顶点数据即可。

squareBatch.CopyVertexData3f(vVerts);

程序清单2.2 用箭头键操纵正方形在屏幕范围内移动

// Respond to arrow keys by moving the camera frame of reference
void SpecialKeys(int key, int x, int y)
{GLfloat stepSize = 0.025f;GLfloat blockX = vVerts[0];    // Upper left XGLfloat blockY = vVerts[7];    // Upper left Yif(key == GLUT_KEY_UP)blockY += stepSize;if(key == GLUT_KEY_DOWN)blockY -= stepSize;if(key == GLUT_KEY_LEFT)blockX -= stepSize;if(key == GLUT_KEY_RIGHT)blockX += stepSize;// Collision detectionif(blockX < -1.0f) blockX = -1.0f;if(blockX > (1.0f - blockSize * 2)) blockX = 1.0f - blockSize * 2;if(blockY < -1.0f + blockSize * 2)  blockY = -1.0f + blockSize * 2;if(blockY > 1.0f) blockY = 1.0f;// Recalculate vertex positionsvVerts[0] = blockX;vVerts[1] = blockY - blockSize*2;vVerts[3] = blockX + blockSize*2;vVerts[4] = blockY - blockSize*2;vVerts[6] = blockX + blockSize*2;vVerts[7] = blockY;vVerts[9] = blockX;vVerts[10] = blockY;squareBatch.CopyVertexData3f(vVerts);glutPostRedisplay();
}

 

2.6.2 刷新显示

SpecialKeys函数的最后一行代码用来告诉GLUT需要更新窗口内容。

glutPostRedisplay();

默认情况下,在窗口创建、改变大小或者需要重绘时,GLUT通过调用RenderScene函数来更新窗口。

只要窗口发生最小化、回复、最大化、覆盖或重新显示灯变化,就会发生更新。

我们可以人工调用 glutPostRedisplay 来告诉GLUT发生了某些改变,应该对场景进行渲染了。

不过,用后面将要介绍的方法来完成这项工作尤为方便。

 

2.6.3 简单的动画片

在 "Move" 示例中,当我们按箭头时,更新了集合图形位置,然后调用 glutPostRedisplay 函数激活屏幕刷新动作。

如果我们将 glutPostRedisplay 函数调用在 RenderScene函数末尾将会发生什么呢?

如果读者想到的是得到一个持续自动刷新的程序,那么恭喜,答对了。

但是不要担心,这并不是一个无线循环。

重绘消息实际是一条传递到一个内部消息循环中的消息,在屏幕刷新的间隔中,也会发生其他窗口事件。

这就是说,我们仍然可以检测按键动作、鼠标移动、改变窗口大小和程序结束等动作。

程序清单2.3显示了我们在 "Move" 示例程序中的 RenderScene 函数经过修改后的代码。

///
// Called to draw scene
void RenderScene(void)
{// Clear the window with current clearing colorglClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);GLfloat vRed[] = { 1.0f, 0.0f, 0.0f, 1.0f };shaderManager.UseStockShader(GLT_SHADER_IDENTITY, vRed);squareBatch.Draw();// Flush drawing commandsglutSwapBuffers();BounceFunction();glutPostRedisplay(); // Redraw
}

 

2.7 总结

在本章,我们讨论了许多基础知识。我们介绍了OpenGL,并简单地介绍了它的历史。

另外,还介绍了OpenGL工具箱(GLUT),并讨论了使用OpenGL编写程序的基础知识。

利用GLUT,我们还展示了创建窗口并使用OpenGL命令在窗口中进行绘图的最简便方法,并学习了如何使用GLUT函数库创建一个可以改变大小的窗口,并创建了一个简单的动画示例程序。

此外,我们还介绍了使用OpenGL进行绘图的过程——合成和选择颜色、清除屏幕、绘制三角形和矩形并在窗口帧中设置视口。

我们还讨论了各种OpenGL数据类型以及生成OpenGL程序所需要的头文件,并引导读者分别在Visual Studio(Windows系统)和Xcode(Mac OS X)系统中创建项目(详细看书本,本章省略了这部分)。

OpenGL状态机奠定了我们以后用到的几乎所有操作的基础。

扩展机制使我们能够访问自己所使用的硬件驱动程序支持的所有OpenGL特性,而不必考虑自己所使用的是什么开发工具。

我们还学习了如何检查OpenGL错误,确保程序中并未出现任何非法的状态改变或渲染命令。

稍微再熟悉一些代码以后,读者就可以掌握进一步学习所需的一些知识了。

 

本书的所有示例源码将放到Github中,详细地址:Github-OpenGL-Example

本章的Chapter02的示例源码详细地址:Github-Chapter02

 

 

 

 

 

查看全文
如若内容造成侵权/违法违规/事实不符,请联系编程学习网邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!

相关文章

  1. java 线程池学习记录

    目录线程池实现原理使用线程池的注意事项参考与说明线程池实现原理创建线程,需要底层调用操作系统的API申请各种资源,这个成本很高,所以线程是一个重量级的对象,应该避免频繁创建和销毁。因此避免方案就是采用线程池。目前业界线程池的设计,普遍采用的都是生产者 - 消费者…...

    2024/4/24 10:13:01
  2. IPFS赋能新基建,未来发展势在必行!

    近日,由西安高新技术产业开发区管理委员会指导、中国民营科技促进会分布式存储分会主办的“新基建新机遇:区块链分布式存储行业大会”在西安成功举办,该会议引起了巨大的反响。西安高新技术开发区管理委员会主任杨华致辞演讲。据悉,本次大会邀请了20余位行业领军人物、500名…...

    2024/4/30 14:45:09
  3. 同样大火的DeFi和IPFS,谁将真正脱颖而出?

    最近,区块链行业,最火爆的项目莫过于Filecoin和DeFi项目了。DeFi是一个分布式金融项目,而IPFS则是一个分布式存储网络项目,但都因为其开创性的模式被市场看好,热度一直居高不下。但无论是Filecoin还是DeFi,想要在竞争激烈的市场中长久发展,其必定拥有能够长久发展的价值…...

    2024/4/28 5:42:09
  4. 《动手学深度学习》(PyTorch版)代码注释 - 4 【Softmaxs_regression_with_simple_way】

    目录说明配置环境此节说明代码 说明 本博客代码来自开源项目:《动手学深度学习》(PyTorch版) 并且在博主学习的理解上对代码进行了大量注释,方便理解各个函数的原理和用途 配置环境 使用环境:python3.8 平台:Windows10 IDE:PyCharm 此节说明 次节对应书本上3.7节 次节功能…...

    2024/4/24 21:05:45
  5. 辣鸡准备稍微碰一下深度学习系列(1/21)---上

    Hello Tensorflow(2.3.0)集ctrlCV之大成 -- MNIST 手写数字检测上前BBMNIST数据集一、是啥玩意?二、怎么得到?第一种:找大腿(keras)第二种:伸手要讨要过程处理过程第三种:自食其力结论:keras NB!三、怎么使用?1.分验证集2.标签换成独热编码3.最后样子叫上大哥拿家伙…...

    2024/4/23 13:50:23
  6. 《动手学深度学习》(PyTorch版)代码注释 - 5 【Multi_Layer_Perception_with_zero】

    目录说明配置环境此节说明代码 说明 本博客代码来自开源项目:《动手学深度学习》(PyTorch版) 并且在博主学习的理解上对代码进行了大量注释,方便理解各个函数的原理和用途 配置环境 使用环境:python3.8 平台:Windows10 IDE:PyCharm 此节说明 次节对应书本上3.9节 次节功能…...

    2024/4/10 11:50:58
  7. centos7.8 安装部署 k8s 集群

    centos7.8 安装部署 k8s 集群 文章目录centos7.8 安装部署 k8s 集群环境说明Docker 安装k8s 安装准备工作Master 节点安装 k8s版本查看安装 kubelet,kubeadm,kubectl下载镜像初始化 Master启动测试参考文章 刚接触 k8s,配置环境折腾了一天,感觉有很多坑,网上很多教程已经不…...

    2024/4/25 10:08:00
  8. 第二届全国大学生智能汽车竞赛获奖名单

    01第二届全国大学生智能汽车竞赛获奖名单序号 学校 队名 决赛名次 获奖等级1 上海交通大学 CyberSmart队 1 18.942 上海交通大学 速度之星队 2 19.933 天津工业大学 天津工大二队 3 20.394 北京科技大学 北京科技大学二队 4 20.825 吉林大学 爱德2队 5 21.056 清华大学 三角洲队…...

    2024/4/24 4:10:51
  9. 第三届全国大学生智能汽车竞赛获奖名单

    第三届全国大学生智能汽车竞赛获奖名单序号 学校 队名 竞赛组别 获奖等级1 东北大学 猎豹 摄像头组 特等奖2 北京科技大学 北京科技大学CCD一队 摄像头组 特等奖3 上海交通大学 SpeedStar 摄像头组 特等奖4 武汉科技大学 首安一队 光电组 特等奖5 北京科技大学 北京科技大学光电…...

    2024/4/15 2:36:29
  10. 第四届全国大学生智能汽车竞赛获奖名单

    第四届全国大学生智能汽车竞赛获奖名单序号 学校 队名 竞赛组别 获奖等级1 北京科技大学 北京科技大学CCD2队 摄像头组 特等奖2 上海交通大学 SmartStar 摄像头组 特等奖3 上海大学 XX 摄像头组 特等奖4 北京科技大学 科大中冶队 光电组 特等奖5 清华大学 三角洲光电队 光电组 …...

    2024/4/17 16:06:23
  11. 百万JAVA程序员最关注的问题:今年的“金九银十”还在吗?

    大家都知道由于今年年初疫情的关系,所以今年的“金三银四”并没有如约到来,反而很多企业出现了大批裁员, 导致大家都没有企业可跳。而现在8月份也都已经过了一大半。那么问题来了,今年的金九银十到底还存在吗?想要弄清楚这个答案,我们首先要知道为什么会存在“金三银四”…...

    2024/4/27 20:58:22
  12. Linux 离线安装mysql

    1.离线安装配置Mysql 1.1 上传mysql安装包mysql-5.7.23-1.el7.x86_64.rpm-bundle.tar 到服务器上的/zking/mysql目录上 1.2解压mysql tar -xvf mysql-5.7.23-1.el7.x86_64.rpm-bundle.tar 1.3卸载mariadb,否则安装MySql会出现冲突 1)rpm -qa|grep mariadb 2)rpm -e --nodep…...

    2024/4/15 13:42:09
  13. AVR单片机期末自选2 4x4薄膜开关控制led灯

    AVR单片机期末自选2 4x4薄膜开关控制led灯 具体内容 :AVR单片机 ATmega16 PD接跑马灯 PB接软膜 功能 按1第一个亮 再按就灭 按2第二个亮 再按就灭 …… 按8第八个亮 再按就灭 1-8就是对应8个灯 一按亮一按灭 按A全亮 按B全灭 按c奇数亮 按d偶数亮 按9跑马灯依次…...

    2024/4/24 20:14:15
  14. Java JSP 基础知识总结

    Java Server Page(Java 服务页) 一:JSP 初识 使用 JSP 文件 JSP 是 HTML 文件与 Java 文件的混合文件,HTML 文件可以直接通过浏览器打开,Java 文件使用Java虚拟机进行编译。 JSP 文件需要借助第三方(比如:Tomcat)编译 -> .java -> 执行 -> .class -> 信息传…...

    2024/4/25 17:59:37
  15. 字符串中的模式匹配(Pattern Matching) 以及KMP详解

    一、模式匹配(Pattern Matching)简介 模式匹配是数据结构中字符串的一种基本操作,它用于在一条字符串中寻找与另一条子串相同的所有子串。例如 在"hjh123abc"中寻找"hjh"二、简单模式匹配 暴力匹配 int Index(String S,String T){int i=1,j=1;while(i&l…...

    2024/4/25 5:33:04
  16. OpenCV Error: Insufficient memory解决方法

    故障: window 10 下qt 5.9.6 opencv2.4 进行视频处理,低概率出现内存不足而异退 OpenCV Error: Insufficient memory (Failed to allocate xxxxx bytes) in cv::OutOfMemoryError 解决方法: 项目构建x86架构更新为x64架构 升级qt 到5.14.2 选择 Desktop MinGW 42bit 进行构建…...

    2024/4/15 8:02:27
  17. 嵌入式面试之《多进程多线程编程系列》说说虚拟地址、物理地址、MMU、分页机制、TLB问题汇总

    文章目录什么是物理地址引入虚拟地址的好处简述分页机制什么是MMU什么是TLB 什么是物理地址 物理地址(Physical Address) 是指出现在CPU外部地址总线上的、用于寻址物理内存的地址信号,是地址变换的最终结果地址。如果启用了分页机制,那么线性地址会使用页目录和页表中的项…...

    2024/4/29 1:37:24
  18. Java初学注意小事项——多线程的sleep、wait、join

    sleep 作用Sleep(n)的作用:是让当前线程睡眠n毫秒,以便执行其他线程,如果没有其他线程,那睡眠n毫秒后,继续执行。(前提:不涉及线程同步) sleep()不释放同步锁。(当涉及线程同步,即必须抢到锁才能才能执行),此时因为 sleep()不释放同步锁,占着cpu去睡觉,所以其它线…...

    2024/4/29 23:56:11
  19. opencv交叉编译 讯为itop4412

    下载cmake-3.9.0-Linux-x86_64.sh,cmake是帮助生成makefile的辅助工具,源码需要makefile才能编译,所以需要一个cmake程序,和上文中交叉编译工具配置方法一样,先用ssh复制到Ubuntu中,然后在terminal输入chmod 777 cmake-3.9.0-Linux-x86_64.sh获取最好权限,等出来一行do …...

    2024/4/26 23:54:34
  20. Java类加载器及双亲委托机制

    Java类加载器及双亲委托机制Java程序是如何运行起来的,如何做到“一次编译,到处运行”的,Java虚拟机内部是怎么工作的,它的设计遵循着哪些原则,程序出现异常,除了代码层面,还有哪些地方需要排查…… 带着上面的问题,开始探索神秘的Java虚拟机​ 简单说来,类的加载就是…...

    2024/4/26 6:44:15

最新文章

  1. 从Paint 3D入门glTF

    Paint 3D Microsoft Paint 3D是微软的一款图像编辑软件&#xff0c;它是传统的Microsoft Paint程序的升级版。 这个新版本的Paint专注于三维设计和创作&#xff0c;使用户可以使用简单的工具创建和编辑三维模型。 Microsoft Paint 3D具有直观的界面和易于使用的工具&#xff0…...

    2024/5/1 10:54:55
  2. 梯度消失和梯度爆炸的一些处理方法

    在这里是记录一下梯度消失或梯度爆炸的一些处理技巧。全当学习总结了如有错误还请留言&#xff0c;在此感激不尽。 权重和梯度的更新公式如下&#xff1a; w w − η ⋅ ∇ w w w - \eta \cdot \nabla w ww−η⋅∇w 个人通俗的理解梯度消失就是网络模型在反向求导的时候出…...

    2024/3/20 10:50:27
  3. 备战蓝桥杯Day37 - 真题 - 特殊日期

    一、题目描述 思路&#xff1a; 1、统计2000年到2000000年的日期&#xff0c;肯定是需要遍历 2、闰年的2月是29天&#xff0c;非闰年的2月是28天。我们需要判断这一年是否是闰年。 1、3、5、7、8、10、12月是31天&#xff0c;4、6、9、11月是30天。 3、年份yy是月份mm的倍数…...

    2024/4/30 17:25:17
  4. 用html实现一个日历便签设计

    <!DOCTYPE html> <html lang"en" > <head><meta charset"UTF-8"><title>日历便签设计</title><link hrefhttps://fonts.googleapis.com/css?familyMontserrat:700,400 relstylesheet typetext/css><link hr…...

    2024/4/30 18:51:46
  5. 416. 分割等和子集问题(动态规划)

    题目 题解 class Solution:def canPartition(self, nums: List[int]) -> bool:# badcaseif not nums:return True# 不能被2整除if sum(nums) % 2 ! 0:return False# 状态定义&#xff1a;dp[i][j]表示当背包容量为j&#xff0c;用前i个物品是否正好可以将背包填满&#xff…...

    2024/5/1 10:25:26
  6. 【Java】ExcelWriter自适应宽度工具类(支持中文)

    工具类 import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.CellType; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet;/*** Excel工具类** author xiaoming* date 2023/11/17 10:40*/ public class ExcelUti…...

    2024/4/30 0:57:52
  7. Spring cloud负载均衡@LoadBalanced LoadBalancerClient

    LoadBalance vs Ribbon 由于Spring cloud2020之后移除了Ribbon&#xff0c;直接使用Spring Cloud LoadBalancer作为客户端负载均衡组件&#xff0c;我们讨论Spring负载均衡以Spring Cloud2020之后版本为主&#xff0c;学习Spring Cloud LoadBalance&#xff0c;暂不讨论Ribbon…...

    2024/4/29 18:43:42
  8. TSINGSEE青犀AI智能分析+视频监控工业园区周界安全防范方案

    一、背景需求分析 在工业产业园、化工园或生产制造园区中&#xff0c;周界防范意义重大&#xff0c;对园区的安全起到重要的作用。常规的安防方式是采用人员巡查&#xff0c;人力投入成本大而且效率低。周界一旦被破坏或入侵&#xff0c;会影响园区人员和资产安全&#xff0c;…...

    2024/5/1 4:07:45
  9. VB.net WebBrowser网页元素抓取分析方法

    在用WebBrowser编程实现网页操作自动化时&#xff0c;常要分析网页Html&#xff0c;例如网页在加载数据时&#xff0c;常会显示“系统处理中&#xff0c;请稍候..”&#xff0c;我们需要在数据加载完成后才能继续下一步操作&#xff0c;如何抓取这个信息的网页html元素变化&…...

    2024/4/30 23:32:22
  10. 【Objective-C】Objective-C汇总

    方法定义 参考&#xff1a;https://www.yiibai.com/objective_c/objective_c_functions.html Objective-C编程语言中方法定义的一般形式如下 - (return_type) method_name:( argumentType1 )argumentName1 joiningArgument2:( argumentType2 )argumentName2 ... joiningArgu…...

    2024/4/30 23:16:16
  11. 【洛谷算法题】P5713-洛谷团队系统【入门2分支结构】

    &#x1f468;‍&#x1f4bb;博客主页&#xff1a;花无缺 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! 本文由 花无缺 原创 收录于专栏 【洛谷算法题】 文章目录 【洛谷算法题】P5713-洛谷团队系统【入门2分支结构】&#x1f30f;题目描述&#x1f30f;输入格…...

    2024/5/1 6:35:25
  12. 【ES6.0】- 扩展运算符(...)

    【ES6.0】- 扩展运算符... 文章目录 【ES6.0】- 扩展运算符...一、概述二、拷贝数组对象三、合并操作四、参数传递五、数组去重六、字符串转字符数组七、NodeList转数组八、解构变量九、打印日志十、总结 一、概述 **扩展运算符(...)**允许一个表达式在期望多个参数&#xff0…...

    2024/4/29 21:25:29
  13. 摩根看好的前智能硬件头部品牌双11交易数据极度异常!——是模式创新还是饮鸩止渴?

    文 | 螳螂观察 作者 | 李燃 双11狂欢已落下帷幕&#xff0c;各大品牌纷纷晒出优异的成绩单&#xff0c;摩根士丹利投资的智能硬件头部品牌凯迪仕也不例外。然而有爆料称&#xff0c;在自媒体平台发布霸榜各大榜单喜讯的凯迪仕智能锁&#xff0c;多个平台数据都表现出极度异常…...

    2024/5/1 4:35:02
  14. Go语言常用命令详解(二)

    文章目录 前言常用命令go bug示例参数说明 go doc示例参数说明 go env示例 go fix示例 go fmt示例 go generate示例 总结写在最后 前言 接着上一篇继续介绍Go语言的常用命令 常用命令 以下是一些常用的Go命令&#xff0c;这些命令可以帮助您在Go开发中进行编译、测试、运行和…...

    2024/4/30 14:53:47
  15. 用欧拉路径判断图同构推出reverse合法性:1116T4

    http://cplusoj.com/d/senior/p/SS231116D 假设我们要把 a a a 变成 b b b&#xff0c;我们在 a i a_i ai​ 和 a i 1 a_{i1} ai1​ 之间连边&#xff0c; b b b 同理&#xff0c;则 a a a 能变成 b b b 的充要条件是两图 A , B A,B A,B 同构。 必要性显然&#xff0…...

    2024/4/30 22:14:26
  16. 【NGINX--1】基础知识

    1、在 Debian/Ubuntu 上安装 NGINX 在 Debian 或 Ubuntu 机器上安装 NGINX 开源版。 更新已配置源的软件包信息&#xff0c;并安装一些有助于配置官方 NGINX 软件包仓库的软件包&#xff1a; apt-get update apt install -y curl gnupg2 ca-certificates lsb-release debian-…...

    2024/5/1 6:34:45
  17. Hive默认分割符、存储格式与数据压缩

    目录 1、Hive默认分割符2、Hive存储格式3、Hive数据压缩 1、Hive默认分割符 Hive创建表时指定的行受限&#xff08;ROW FORMAT&#xff09;配置标准HQL为&#xff1a; ... ROW FORMAT DELIMITED FIELDS TERMINATED BY \u0001 COLLECTION ITEMS TERMINATED BY , MAP KEYS TERMI…...

    2024/4/30 22:57:18
  18. 【论文阅读】MAG:一种用于航天器遥测数据中有效异常检测的新方法

    文章目录 摘要1 引言2 问题描述3 拟议框架4 所提出方法的细节A.数据预处理B.变量相关分析C.MAG模型D.异常分数 5 实验A.数据集和性能指标B.实验设置与平台C.结果和比较 6 结论 摘要 异常检测是保证航天器稳定性的关键。在航天器运行过程中&#xff0c;传感器和控制器产生大量周…...

    2024/4/30 20:39:53
  19. --max-old-space-size=8192报错

    vue项目运行时&#xff0c;如果经常运行慢&#xff0c;崩溃停止服务&#xff0c;报如下错误 FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - JavaScript heap out of memory 因为在 Node 中&#xff0c;通过JavaScript使用内存时只能使用部分内存&#xff08;64位系统&…...

    2024/5/1 4:45:02
  20. 基于深度学习的恶意软件检测

    恶意软件是指恶意软件犯罪者用来感染个人计算机或整个组织的网络的软件。 它利用目标系统漏洞&#xff0c;例如可以被劫持的合法软件&#xff08;例如浏览器或 Web 应用程序插件&#xff09;中的错误。 恶意软件渗透可能会造成灾难性的后果&#xff0c;包括数据被盗、勒索或网…...

    2024/5/1 8:32:56
  21. JS原型对象prototype

    让我简单的为大家介绍一下原型对象prototype吧&#xff01; 使用原型实现方法共享 1.构造函数通过原型分配的函数是所有对象所 共享的。 2.JavaScript 规定&#xff0c;每一个构造函数都有一个 prototype 属性&#xff0c;指向另一个对象&#xff0c;所以我们也称为原型对象…...

    2024/4/29 3:42:58
  22. C++中只能有一个实例的单例类

    C中只能有一个实例的单例类 前面讨论的 President 类很不错&#xff0c;但存在一个缺陷&#xff1a;无法禁止通过实例化多个对象来创建多名总统&#xff1a; President One, Two, Three; 由于复制构造函数是私有的&#xff0c;其中每个对象都是不可复制的&#xff0c;但您的目…...

    2024/4/29 19:56:39
  23. python django 小程序图书借阅源码

    开发工具&#xff1a; PyCharm&#xff0c;mysql5.7&#xff0c;微信开发者工具 技术说明&#xff1a; python django html 小程序 功能介绍&#xff1a; 用户端&#xff1a; 登录注册&#xff08;含授权登录&#xff09; 首页显示搜索图书&#xff0c;轮播图&#xff0…...

    2024/5/1 5:23:20
  24. 电子学会C/C++编程等级考试2022年03月(一级)真题解析

    C/C++等级考试(1~8级)全部真题・点这里 第1题:双精度浮点数的输入输出 输入一个双精度浮点数,保留8位小数,输出这个浮点数。 时间限制:1000 内存限制:65536输入 只有一行,一个双精度浮点数。输出 一行,保留8位小数的浮点数。样例输入 3.1415926535798932样例输出 3.1…...

    2024/4/30 20:52:33
  25. 配置失败还原请勿关闭计算机,电脑开机屏幕上面显示,配置失败还原更改 请勿关闭计算机 开不了机 这个问题怎么办...

    解析如下&#xff1a;1、长按电脑电源键直至关机&#xff0c;然后再按一次电源健重启电脑&#xff0c;按F8健进入安全模式2、安全模式下进入Windows系统桌面后&#xff0c;按住“winR”打开运行窗口&#xff0c;输入“services.msc”打开服务设置3、在服务界面&#xff0c;选中…...

    2022/11/19 21:17:18
  26. 错误使用 reshape要执行 RESHAPE,请勿更改元素数目。

    %读入6幅图像&#xff08;每一幅图像的大小是564*564&#xff09; f1 imread(WashingtonDC_Band1_564.tif); subplot(3,2,1),imshow(f1); f2 imread(WashingtonDC_Band2_564.tif); subplot(3,2,2),imshow(f2); f3 imread(WashingtonDC_Band3_564.tif); subplot(3,2,3),imsho…...

    2022/11/19 21:17:16
  27. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机...

    win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”问题的解决方法在win7系统关机时如果有升级系统的或者其他需要会直接进入一个 等待界面&#xff0c;在等待界面中我们需要等待操作结束才能关机&#xff0c;虽然这比较麻烦&#xff0c;但是对系统进行配置和升级…...

    2022/11/19 21:17:15
  28. 台式电脑显示配置100%请勿关闭计算机,“准备配置windows 请勿关闭计算机”的解决方法...

    有不少用户在重装Win7系统或更新系统后会遇到“准备配置windows&#xff0c;请勿关闭计算机”的提示&#xff0c;要过很久才能进入系统&#xff0c;有的用户甚至几个小时也无法进入&#xff0c;下面就教大家这个问题的解决方法。第一种方法&#xff1a;我们首先在左下角的“开始…...

    2022/11/19 21:17:14
  29. win7 正在配置 请勿关闭计算机,怎么办Win7开机显示正在配置Windows Update请勿关机...

    置信有很多用户都跟小编一样遇到过这样的问题&#xff0c;电脑时发现开机屏幕显现“正在配置Windows Update&#xff0c;请勿关机”(如下图所示)&#xff0c;而且还需求等大约5分钟才干进入系统。这是怎样回事呢&#xff1f;一切都是正常操作的&#xff0c;为什么开时机呈现“正…...

    2022/11/19 21:17:13
  30. 准备配置windows 请勿关闭计算机 蓝屏,Win7开机总是出现提示“配置Windows请勿关机”...

    Win7系统开机启动时总是出现“配置Windows请勿关机”的提示&#xff0c;没过几秒后电脑自动重启&#xff0c;每次开机都这样无法进入系统&#xff0c;此时碰到这种现象的用户就可以使用以下5种方法解决问题。方法一&#xff1a;开机按下F8&#xff0c;在出现的Windows高级启动选…...

    2022/11/19 21:17:12
  31. 准备windows请勿关闭计算机要多久,windows10系统提示正在准备windows请勿关闭计算机怎么办...

    有不少windows10系统用户反映说碰到这样一个情况&#xff0c;就是电脑提示正在准备windows请勿关闭计算机&#xff0c;碰到这样的问题该怎么解决呢&#xff0c;现在小编就给大家分享一下windows10系统提示正在准备windows请勿关闭计算机的具体第一种方法&#xff1a;1、2、依次…...

    2022/11/19 21:17:11
  32. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”的解决方法...

    今天和大家分享一下win7系统重装了Win7旗舰版系统后&#xff0c;每次关机的时候桌面上都会显示一个“配置Windows Update的界面&#xff0c;提示请勿关闭计算机”&#xff0c;每次停留好几分钟才能正常关机&#xff0c;导致什么情况引起的呢&#xff1f;出现配置Windows Update…...

    2022/11/19 21:17:10
  33. 电脑桌面一直是清理请关闭计算机,windows7一直卡在清理 请勿关闭计算机-win7清理请勿关机,win7配置更新35%不动...

    只能是等着&#xff0c;别无他法。说是卡着如果你看硬盘灯应该在读写。如果从 Win 10 无法正常回滚&#xff0c;只能是考虑备份数据后重装系统了。解决来方案一&#xff1a;管理员运行cmd&#xff1a;net stop WuAuServcd %windir%ren SoftwareDistribution SDoldnet start WuA…...

    2022/11/19 21:17:09
  34. 计算机配置更新不起,电脑提示“配置Windows Update请勿关闭计算机”怎么办?

    原标题&#xff1a;电脑提示“配置Windows Update请勿关闭计算机”怎么办&#xff1f;win7系统中在开机与关闭的时候总是显示“配置windows update请勿关闭计算机”相信有不少朋友都曾遇到过一次两次还能忍但经常遇到就叫人感到心烦了遇到这种问题怎么办呢&#xff1f;一般的方…...

    2022/11/19 21:17:08
  35. 计算机正在配置无法关机,关机提示 windows7 正在配置windows 请勿关闭计算机 ,然后等了一晚上也没有关掉。现在电脑无法正常关机...

    关机提示 windows7 正在配置windows 请勿关闭计算机 &#xff0c;然后等了一晚上也没有关掉。现在电脑无法正常关机以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;关机提示 windows7 正在配…...

    2022/11/19 21:17:05
  36. 钉钉提示请勿通过开发者调试模式_钉钉请勿通过开发者调试模式是真的吗好不好用...

    钉钉请勿通过开发者调试模式是真的吗好不好用 更新时间:2020-04-20 22:24:19 浏览次数:729次 区域: 南阳 > 卧龙 列举网提醒您:为保障您的权益,请不要提前支付任何费用! 虚拟位置外设器!!轨迹模拟&虚拟位置外设神器 专业用于:钉钉,外勤365,红圈通,企业微信和…...

    2022/11/19 21:17:05
  37. 配置失败还原请勿关闭计算机怎么办,win7系统出现“配置windows update失败 还原更改 请勿关闭计算机”,长时间没反应,无法进入系统的解决方案...

    前几天班里有位学生电脑(windows 7系统)出问题了&#xff0c;具体表现是开机时一直停留在“配置windows update失败 还原更改 请勿关闭计算机”这个界面&#xff0c;长时间没反应&#xff0c;无法进入系统。这个问题原来帮其他同学也解决过&#xff0c;网上搜了不少资料&#x…...

    2022/11/19 21:17:04
  38. 一个电脑无法关闭计算机你应该怎么办,电脑显示“清理请勿关闭计算机”怎么办?...

    本文为你提供了3个有效解决电脑显示“清理请勿关闭计算机”问题的方法&#xff0c;并在最后教给你1种保护系统安全的好方法&#xff0c;一起来看看&#xff01;电脑出现“清理请勿关闭计算机”在Windows 7(SP1)和Windows Server 2008 R2 SP1中&#xff0c;添加了1个新功能在“磁…...

    2022/11/19 21:17:03
  39. 请勿关闭计算机还原更改要多久,电脑显示:配置windows更新失败,正在还原更改,请勿关闭计算机怎么办...

    许多用户在长期不使用电脑的时候&#xff0c;开启电脑发现电脑显示&#xff1a;配置windows更新失败&#xff0c;正在还原更改&#xff0c;请勿关闭计算机。。.这要怎么办呢&#xff1f;下面小编就带着大家一起看看吧&#xff01;如果能够正常进入系统&#xff0c;建议您暂时移…...

    2022/11/19 21:17:02
  40. 还原更改请勿关闭计算机 要多久,配置windows update失败 还原更改 请勿关闭计算机,电脑开机后一直显示以...

    配置windows update失败 还原更改 请勿关闭计算机&#xff0c;电脑开机后一直显示以以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;配置windows update失败 还原更改 请勿关闭计算机&#x…...

    2022/11/19 21:17:01
  41. 电脑配置中请勿关闭计算机怎么办,准备配置windows请勿关闭计算机一直显示怎么办【图解】...

    不知道大家有没有遇到过这样的一个问题&#xff0c;就是我们的win7系统在关机的时候&#xff0c;总是喜欢显示“准备配置windows&#xff0c;请勿关机”这样的一个页面&#xff0c;没有什么大碍&#xff0c;但是如果一直等着的话就要两个小时甚至更久都关不了机&#xff0c;非常…...

    2022/11/19 21:17:00
  42. 正在准备配置请勿关闭计算机,正在准备配置windows请勿关闭计算机时间长了解决教程...

    当电脑出现正在准备配置windows请勿关闭计算机时&#xff0c;一般是您正对windows进行升级&#xff0c;但是这个要是长时间没有反应&#xff0c;我们不能再傻等下去了。可能是电脑出了别的问题了&#xff0c;来看看教程的说法。正在准备配置windows请勿关闭计算机时间长了方法一…...

    2022/11/19 21:16:59
  43. 配置失败还原请勿关闭计算机,配置Windows Update失败,还原更改请勿关闭计算机...

    我们使用电脑的过程中有时会遇到这种情况&#xff0c;当我们打开电脑之后&#xff0c;发现一直停留在一个界面&#xff1a;“配置Windows Update失败&#xff0c;还原更改请勿关闭计算机”&#xff0c;等了许久还是无法进入系统。如果我们遇到此类问题应该如何解决呢&#xff0…...

    2022/11/19 21:16:58
  44. 如何在iPhone上关闭“请勿打扰”

    Apple’s “Do Not Disturb While Driving” is a potentially lifesaving iPhone feature, but it doesn’t always turn on automatically at the appropriate time. For example, you might be a passenger in a moving car, but your iPhone may think you’re the one dri…...

    2022/11/19 21:16:57