前言

这是学习油管大佬Cherno的OpenGL教程的个人笔记,供参考

b站上有从YouTube上搬运过来的熟肉视频,链接:【【译】TheCherno-OpenGL系列教程】 https://www.bilibili.com/video/BV1Ni4y1o7Au/?share_source=copy_web&vd_source=4175708e3d0d482d0df075930a6bbbdf

LearnOpenGL教程文档:

英文版:https://learnopengl.com/

中文版:https://learnopengl-cn.github.io/

【P1】欢迎来到OpenGL

什么是OpenGL

首先如果你听过 OpenGL,但不确定它是什么,你可能想到与图形有关的,那就是 OpenGL。OpenGL 是一种图形接口,现在 API 代表应用程序接口,基本上就是一大堆我们能够调用的函数去做一些事情。在这种情况下,由于 OpenGL 是一种图形 API,它允许我们做一些与图形相关的事情,特别的是它允许我们访问 GPU 也就是显卡,图形处理单元(Graphics Processing Unit)

调用显卡也更好地绘制图形,所以实际上为了利用电脑或其他设备(比如手机)中强大的图形处理器,需要调用一些 API 访问固件。OpenGL 正好是允许访问和操作 GPU 的许多接口中的一种,当然我们也有一些其他的接口,比如 Direct3D、Vulcan 和 Metal 等等,所以某种角度来说 OpenGL 允许我们控制显卡

关于OpenGL误解

让我们来澄清一些人关于 OpenGL 的误解

首先,许多人称它为一个类库或一种引擎或一些其他的框架,但这些都不是。OpenGL 核心本身只是一种规范,和 CPP 规范差不多。实际上它没有确定任何代码和类似的事情本身就是规范,比如“嘿,这个函数应该存在,需要这些参数并且返回这个值”,它只是一种你能利用这种 API 做什么规范,没有任何具体的实现,这意味它绝不是一个类库,因为 OpenGL 本身没有代码,它只是一种规范。

去哪儿下载 OpenGL 是另一个常见的问题

然而,答案是你不需要真的去下载 OpenGL,它本身只是一种规范。那么,谁去实现它呢,谁去为你调用的 OpenGL 函数写代码呢?这个答案是 GPU 制造商,所以如果你使用的是 NVIDIA 显卡,那么你的显卡驱动(也就是 NVIDIA 驱动)实际上包含了 OpenGL 的实现,每个显卡制造商比如 AMD、Intel 等都会有它们自己的实现,每家关于 OpenGL 的实现都会有些不同,这也就是有些游戏能在 NVIDIA 驱动的显卡上运行但在一个 AMD 电视或者其他显卡设备上运行有些区别、甚至会出现问题的原因

但不管怎么说,关键在于你的显卡制造商实现的 OpenGL,这又可能导致下一个有关 OpenGL 常见的误解:它是开源的。我不知道人们为什么会这么觉得,也许是名字中带着“Open”,但是它根本不是开源的,你看不到 OpenGL 的源码,因为首先它是由 GPU 制造商实现的,它们肯定不会发布它们的驱动源码

OpenGL 提供了什么能激发和导致许多人不幸地说 OpenGL 是无与伦比的?原因在于它是跨平台的,所以你的 OpenGL 代码可以在 Windows、Mac、Linux 和 Android 上正常执行,以至于人们立马就会意识到 OpenGL 比 Direct3D 更优越,因为它能在所有平台上运行,但是请不要这么说

从我 EA 技术中心并处理过许多引擎中图形接口的经验来看,因为 OpenGL 是跨平台的 API,而制作一款游戏不会只涉及实现一个独立的图形 API。如果游戏引擎是跨平台的,那意味着它不仅实现了 Xbox,也包括其他一些平台,它们不得不实现大量其他的图形接口

因此,我们面临的问题是图形 API 是为特定平台设计的。例如 Direct3D 是微软为 Windows 设计的,它在 Windows 上的表现要比跨平台的 API 好些。现在请记住,实际编写这些代码的人不是微软,即使微软的确为了更好的代码质量和 GPU 制造商合作过。所以关于 API 的比较是没有任何意义的,因为通常平台原生的东西会更健壮更友好

就 OpenGL 的复杂性而言,它可以说是现在可以学习的最简单的 API 了,所以 OpenGL 是绝对值得学习的。Vulkan 是另一个跨平台的 API,但它更底层更严谨,不适合初学者直接入门,老实说我并不想使用 Vulkan 去开发游戏,OpenGL 更加稳定

传统与现代OpenGL

另外本系列学习的主要是现代 OpenGL。OpenGL 于 90 年代发布,那时的情况和现在大不相同,那时的 GPU 是可编程的,十分灵活,制造商给了程序员和开发者更多的控制权

传统 OpenGL 和现代 OpenGL 之间最大的区别就是着色器。如果你对图形学感兴趣的话可能听说过着色器,它可能有点像 shadow 这个单词或者是光源,有些人将它和光源或其他比较。抛开这些,着色器是程序,它是在你 GPU 上运行的代码,这就是着色器,它是在你 GPU 上运行的一段程序

那么如果你用 C++、Java 或 C# 或不管什么语言写代码,这段代码都会运行在你的 CPU 上。但当我们开始处理图形的大部分时间里,我们想要更为精确的控制显卡运行,可能要将大部分代码从 CPU 转到 GPU 上,因为它在 GPU 上运行更快,这就是着色器存在的意义:允许我们在 GPU 上运行代码。所以可编程的着色器是最大的区别

【P2】设置OpenGL和在C++中创建一个窗口

介绍:GLFW是一个轻量级类库,它能为我们做的事情就是创建一个窗口,创建一个OpenGL环境以及给我们访问一些像输入之类的基础东西,它能做的就这么多,不像SDL(Simple DirectMedia Layer)那样全面,SDL实际上就像是一个渲染器,可以画任何你想要的图形,并且能实现DirectX和OpenGL,所以它像是一个完整的框架。但在这里我们不需要任何东西,而是需要我们去写我们自己的东西。在这一节课要做的就是将第一步自动化:创建一个窗口并且不需要为每个平台编写不同的代码,换句话说就是这份代码将具有良好的可移植性,适配多种平台

设置OpenGL

访问GLFW官网(An OpenGL library | GLFW),这里我们选择 32 位的GLFW下载

在这里有些人可能会问,对于Windows的这些二进制文件,是该下载32位的还是64位的呢?

这其实与实际的操作系统没有任何关系,而是和你的目标应用程序相关。如果你正在编译的应用程序是win32程序(x86),那么就要32位的二进制文件,当然你的操作系统却可能是win10 64位

将下载后的压缩文件解压

然后在VS新建一个项目,在新建项目的文件目录下新建一个Dependencies文件夹,然后在该文件里面创建一个GLFW文件夹

将解压的文件中的include文件夹和lib-vc文件夹复制粘贴到GLFW文件夹下

在VS中的项目属性 - C/C++ - 常规 - 附加包含目录 里添加GLFW的包含目录

注意:右上角的配置项要选择所有配置,平台选择Win32

在VS中的项目属性 - 链接器 - 常规 - 附加库目录 里添加GLFW的链接目录

然后将GLFW官网的文档下面的代码复制粘贴进VS里运行,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <GLFW/glfw3.h>

int main(void)
{
GLFWwindow* window;

/* Initialize the library */
if (!glfwInit())
return -1;

/* Create a windowed mode window and its OpenGL context */
window = glfwCreateWindow(640, 480, "Hello World", NULL, NULL);
if (!window)
{
glfwTerminate();
return -1;
}

/* Make the window's context current */
glfwMakeContextCurrent(window);

/* Loop until the user closes the window */
while (!glfwWindowShouldClose(window))
{
/* Render here */
glClear(GL_COLOR_BUFFER_BIT);

/* Swap front and back buffers */
glfwSwapBuffers(window);

/* Poll for and process events */
glfwPollEvents();
}

glfwTerminate();
return 0;
}

画一个三角形

glClear(GL_COLOR_BUFFER_BIT);后面写上:

1
2
3
4
5
glBegin(GL_TRIANGLES);
glVertex2f(-0.5f, -0.5f);
glVertex2f( 0.0f, 0.5f);
glVertex2f( 0.5f, -0.5f);
glEnd();

运行结果:

【P3】在C++中使用现代OpenGL

现代OpenGL库的开发者通常是显卡的生产商。我们所购买的显卡所支持的OpenGL版本都是为这个系列的显卡专门开发的,也就是说OpenGL库里的函数都是在显卡驱动中被实现的,而我们所需要做的就是去访问我们的驱动,获取函数(即获取它们的声明,然后链接函数),并调用它们,所以我们需要做的就是访问驱动的动态链接库文件,然后检索库里面那些函数的函数指针

基本上现代OpenGL有一大堆函数。但是有两个问题:

  1. 它不是跨平台的。访问显卡驱动并从中取出这些函数,我们需要使用一些Win32接口,然后加载函数指针等,但这些操作只适用于Windows系统
  2. 函数非常多,需要手动检索它们然后为它们写代码

所以我们实际上需要使用另一个库:GLEW,它能做的就是找到我们使用的显卡驱动,然后找到对应的dll文件,加载所有的函数指针,提供OpenGL接口规范,各种函数声明,符号声明和常量等。

GLEW库下载链接:http://glew.sourceforge.net/

下载后将解压出来的文件放入Dependences文件夹

此处我将其改名为GLEW

GLEW文档:GLEW: The OpenGL Extension Wrangler Library

查阅文档我们知道:

  1. 不能从glew里面使用opengl函数,直到你调用了glewInit()
  2. 在调用glewInit()之前,需要创建一个有效的opengl渲染上下文(rendering context)

提醒:
如果你在使用一个新的库,先看文档