搭建 OpenGL 的开发环境
学 OpenGL,C/C++ 应该是首选,所以先安装 C/C++ 的开发环境,无论是选择 GCC,还是选择 CLang,在 Ubuntu 中就是一条命令的事,我这里选 GCC。在 Ubuntu 中,可以直接安装 build-essential,更省事。命令如下:
sudo apt install build-essential
OpenGL 不提供和 GUI 相关的 API,所以 OpenGL 不能处理诸如创建窗口、处理用户的键盘鼠标输入这样的任务。这时,我们需要 GLFW。使用 GLFW 库,我们可以简化搭建 OpenGL 程序框架的任务,同时还可以轻松获得跨平台的功能。安装 GLFW 也是一条命令的事:
sudo apt install libglfw3 libglfw3-dev
除此之外,我们还需要 GLEW。有了GLEW 扩展库,就再也不用为找不到函数的接口而烦恼了,因为GLEW能自动识别平台所支持的全部 OpenGL 高级扩展涵数。安装命令如下:
sudo apt install libglew2.1 libglew-dev
另外,在写 OpenGL 程序的过程中,会经常需要进行向量、矩阵的计算,所以有一个顺手的数学库是很重要的,我这里选择 GLM。安装命令如下:
sudo apt install libglm-dev
学会了 OpenGL 的基本概念后,当然会忍不住想加载个 3D 模型看看效果,这时候,就可以考虑使用 Assimp 库了。安装命令如下:
sudo apt install assimp-utils libassimp5 libassimp-dev
assimp-utils 包提供了一个assimp命令,使用该命令可以显示 Assimp 库支持哪些格式的 3D 模型文件,也可以使用该命令显示 3D 模型文件的详细信息,如下图:
当然,3D 的东西,还是应该用可视化的方式看起来更直观一些。好在,Linux 中可用的 3D 建模动画软件有 Blender。安装起来也只是一条命令的事:
sudo apt install blender
下面,让大家看一下我的老婆,用的就是 Blender:
最简单的 OpenGL 程序框架
下面,开始写我们的第一个 OpenGL 程序,如下:
#include
#include
#include
const int SCR_WIDTH = 1920;
const int SCR_HEIGHT = 1080;
int main(int argc, char** argv){
glfwInit();
GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, “StudyOpenGL”, nullptr, nullptr);
if (window == NULL)
{
std::cerr << “Failed to create GLFW window” << std::endl;
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
if(glewInit() != GLEW_OK){
std::cerr << "Failed to initalize GLEW" << std::endl;
return -1;
}
while (!glfwWindowShouldClose(window))
{
glfwSwapBuffers(window);
glfwPollEvents();
}
glfwDestroyWindow(window);
glfwTerminate();
return 0;
}
这段程序比较短,使用任何编辑器如 vim、gedit 等等都可以。当后面程序变长之后,我选择使用 Visual Studio Code。我们将这个文件保存为 FirstStep.cpp。编译执行的命令也很简单:
g++ FirstStep.cpp -o FirstStep -lGL -lGLEW -lglfw
./FirstStep
就可以看到我们的第一个 OpenGL 窗口了,目前,它还只是空洞洞的漆黑一片,如下图:
因为我是 4K 屏,所以即使定义了SCR_WIDTH = 1920和SCR_HEIGHT = 1080,窗口看起来也不是特别大。上面那个程序是纯 C 版的,等程序变大之后,会在 main() 函数之外遗留很多全局的变量和函数,不是那么清爽,所以常规会使用 C++ 封装一下,建立一个 App 类,以后要添加鼠标键盘输入的功能也在这个类里面加,建立新程序时,只需要继承这个类就可以了。App 类的内容如下:
#ifndef APP_HPP
#define APP_HPP
#include
#include
#include
class App{
private:
const int SCR_WIDTH = 1920;
const int SCR_HEIGHT = 1080;
public:
static App* the_app;
App(){
}
virtual void init(){
}
virtual void display(){
}
virtual void run(App* app){
if(the_app != NULL){ //同一时刻,只能有一个App运行
std::cerr << "The the_app is already run." << std::endl;
return;
}
the_app = app;
glfwInit();
GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "StudyOpenGL", NULL, NULL);
if (window == NULL)
{
std::cerr << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return;
}
glfwMakeContextCurrent(window);
if(glewInit() != GLEW_OK){
std::cerr << "Failed to initalize GLEW" << std::endl;
return;
}
init(); //用来准备各种数据
while (!glfwWindowShouldClose(window))
{
display(); //这里才是渲染图形的主战场
glfwSwapBuffers(window);
glfwPollEvents();
}
glfwDestroyWindow(window);
glfwTerminate();
return;
}
};
App* App::the_app = NULL;
#define DECLARE_MAIN(a)
int main(int argc, const char ** argv)
{
a *app = new a;
app->run(app);
delete app;
return 0;
}
#endif
把这个文件保存在 include 目录下,命名为 app.hpp,然后,把之前的 FirstStep.cpp 改成如下内容:
#include “…/include/app.hpp”
class MyApp : public App {
private:
public:
void init(){
}
void display(){
}
~MyApp(){
}
};
DECLARE_MAIN(MyApp)
编译运行,结果是一样的。以后,只需要把初始化数据的代码放到 init() 方法中,把渲染图形的代码放到 display() 方法中即可。程序运行时,init() 方法只调用一次,而 display() 方法每渲染一帧图像就调用一次。
OK,到这里,我们的环境就搭建好了。在下一篇随笔中,我们争取使用 OpenGL 渲染一点有用的东西。