作为前言,这是一篇非常长非常长的文章——在文末,你会得到一个纯色三角形,这将经历非常漫长的努力,甚至你还会觉得气馁,因为花费一整天画了个平面三角形。本文呢包括如何准备VS2019,如何开始第一个HelloWorld,渲染管线的简介.......你需要自行学习完毕C语言,全文的代码是C++编译通过的,因此,你或许还需要一丁点的C++知识,但我保证不多。最后,你需要理解Win32开发里面的东西——资源文件、预编译头、按键消息,这些有助于你对代码的领悟,请务必熟悉基本的Win32开发,接下来将使用它的知识。最后的最后,请不要在乎最终程序的效率,我知道你会抱怨它那个while看着开销大上天,总而言之我们才刚开始,好了,开始吧。
对了一点点约定,我用粉色表示我认为关键的地方,用蓝色表示一些无所谓的强调,用绿色表示一些关键概念。如果有加粗,那当然是更重要啦~API名称或者宏定义会使用紫色或者是超链接(超链接意味着它指向一篇文档)。如果你讨厌这长透顶的篇幅,或者你准备好了某些东西,可以直接跳转:
目录
0. OpenGL?
有点好奇各位是怎么知道它的呢。
它其实是一个API规范,它仅仅是一个由Khronos组织制定并维护的规范(Specification)。这套规范描述了每个函数的具体任务、返回值等等,负责编写OpenGL库的人将会按照它实现。我们将使用的4.0版本也有着规范文档,可以点开来看看。OpenGL可以渲染3D图形,我们也会用它来渲染3D图形。
这些简介不再说了。
1. 准备你的Visual Studio,创建项目
虽然这是VS2019的例子,但是它依然通用于其它的IDE。在此之前,请新建一个空项目,注意什么也不要包含。在VS2019里面,你需要选择这个,注意在后续步骤中一定不要包含任何代码:
创建完成之后,项目中应该是没有任何代码的。
接下来的这步可以省略——如果看不懂或者之类的......设置预编译头,我最开始学习C/C++的时候就爱上了预编译头这玩意,总而言之看着很舒服就对了。添加pch.h和pch.cpp,把项目(注意有Debug和Release两个配置,都要设置,下面的子系统选择也是一样的)的预编译头设置为"使用",选择pch.cpp,把它的预编译头选择为"创建"。
关键一步,我想你不会爱那个黑色的控制台窗口,因此,我们要更改子系统:在项目上右键单击,选择属性 -> 链接器 -> 所有选项,找到子系统,把它改为窗口:
这样,我们的入口点函数就从main变成了winMain。在配置属性 -> 高级里面,你可以看到你的项目用的字符集,一般是Unicode。这意味着我们的入口点函数名称叫做wWinMain。更改Debug和Release的配置都为这个,然后添加一个main.cpp文件,开始编写你的代码。需要注意的是,因为我喜欢预编译头,所以我#incldue的是pch.h,如果不会配置它或者不想使用它,把它更换成你自己需要包含的头文件就是:
#include "pch.h"
int APIENTRY wWinMain(
_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow
)
{
return 0;
}
编译试试?如果编译通过了,那么上面的所有步骤表示正确。如果你收到了无法重定位函数、无法找到符号"_XXXXX"这类的链接器错误,说明你没有编写正确的入口点函数。请检查你的步骤,如果实在无法,那么你可以丢弃掉上面的步骤——你依然可以使用传统的方式,用main函数作为入口点函数。这没什么大不了的,除了那个黑窗口。
到此,最开始的配置就完成了。
2. 添加glfw。
在继续之前,我想说一个无奈的事实,微软一直很推荐自家的DirectX,因此OpenGL总是被当作捡来的一样对待——不过OpenGL有着强硬的地位,因此各大显卡厂家都支持它。在Windows下,OpenGL的版本是1.0(还是1.1,我忘记了),这意味着哪怕你在用着Windows 10,上面可访问的OpenGL版本依然是20年前的玩意。因此,我们需要glad——用它们去用显卡厂商给的新版本API。
首先,你需要准备glfw——在这里去下载glfw。为了省略构建的烦恼,请直接下载编译好的版本。也就是这个:
可能你在猜测我想要64位的可以吗?这当然是可以的,不过,要注意你在VS里面的配置,也要用64位。不过用作学习,32位也差不多够了,因此我们选择32位。
把它放在你的VS的包含目录下。或者把它扔到你的项目的源代码目录下去,那里默认是一个包含目录。具体的来说,你需要下面这些东西,点开压缩包:
然后,把include目录下的内容丢到你的项目中去,选择合适自己的版本,这里是vs2019——按照自己的需求选择。在lib-vc2019下有三个文件,一个是glfw3.lib, 一个是glfw3dll.lib,和一个glfw3.dll。glfw3.lib是静态库版本,另外的一个lib是dll所带的那个。在这里我选择了dll,你可以选择静态库版本,这没什么。把对应的lib添加到你的项目中,这样就完成了。作为测试,你可以试试编译下面的代码:
#include "pch.h"
#include "GLFW/glfw3.h"
#pragma comment(lib, "glfw3dll.lib")
int APIENTRY wWinMain(
_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow
)
{
return 0;
}
我强调了glfw和glfw3dll.lib,在这之后,你不会看见它们,不过要记住,那个pch.h中是一直有着这两个的。如果编译通过,说明上面的步骤全部正确,如果没有通过(比如出现找不到什么什么的错误之类),请检查你的VC++目录,保证编译器和链接器可以找到那几个文件。自此,glfw添加完成了。
3. 添加glad
同样的,我们在网上下载glad。glad的底层依然是调用Windows的API去取得对应函数的地址。前面说过,这些API由显卡厂家负责,而且OpenGL常年在Windows上处于捡来的地位,因此,我们无法直接访问它们。去这里下载GLad。glad可以方便的帮我们搞定这个取得API的步骤。如果你好奇——在Windows上,这可以通过wglGetProcAddress完成。点开那个页面后,你会看到下面的配置选项,按照截图所示的配置,修改红框中的内容,其余都不动:
然后点击"GENERATE",它在页面最下面。对了,有个"Generate a loader"选项,一定保证它勾选上。
在这之后,有个zip文件,选择它即可。
解压之后,你会看到两个文件夹:include和src。把src目录下的所有文件添加到你的项目中去,把include中的所有东西添加到你的项目中。在这之后,你的项目中应该有两个源文件:main.cpp和glad.c,你可以选择把glad.c的名字改成glad.cpp,这没有任何影响。如果你有预编译头,你还应该有一个pch.cpp,不过这无关紧要。作为测试,可以试试如下的代码:
#include "pch.h"
#include "GLFW/glfw3.h"
#include "glad/glad.h"
#pragma comment(lib, "glfw3dll.lib")
int APIENTRY wWinMain(
_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow
)
{
return 0;
}
如果编译成功,说明之前所有步骤都正确了,如果失败,那么参照第二小节,把问题解决好。在这你可能遇到的错误是无法打开文件"khrplatform.h",并且会携带非常多的符号未定义错误,这是因为我们没有把它丢到VS默认的VC++编译器的包含目录下的关系,如果你真的遇到了,请打开glad.h,把里面的#include <KHR/khrplatform.h>更改成正确的文件位置。它在include/KHR目录下。如果你收到了没有预编译头的错误,这多半是glad.c导致的,请修改它,为它添加预编译头。
4. 创建窗口
一切准备就绪,我们可以开始创建窗口了。因为我使用了预编译头,所以有必要展示一下pch.h里面的内容