Linux 下的 OpenGL 之路(二):為什么說使用 Linux 系統學習 OpenGL 更方便


前言

上一篇隨筆介紹了我的電腦,同時也介紹了 Ubuntu 20.10 系統的安裝和美化。這一篇,我將正式開始 OpenGL 之旅。使用 Ubuntu 來進行開發,不僅僅只是因為我對 Linux 桌面比較熟悉,其實我對 Windows 也很熟悉,主要還是因為在 Linux 系統下搭建 OpenGL 的開發環境確實是太方便了。在 Windows 下開發 OpenGL,每用到一個庫,我們都要單獨去搜索和下載,然后在 Visual Studio 中進行設置,很繁瑣。在 Ubuntu 中,安裝這些庫,就是一條命令的事,下面詳細論述。

搭建 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 <iostream>
#include <GL/glew.h>
#include <GLFW/glfw3.h>

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 = 1920SCR_HEIGHT = 1080,窗口看起來也不是特別大。上面那個程序是純 C 版的,等程序變大之后,會在 main() 函數之外遺留很多全局的變量和函數,不是那么清爽,所以常規會使用 C++ 封裝一下,建立一個 App 類,以后要添加鼠標鍵盤輸入的功能也在這個類里面加,建立新程序時,只需要繼承這個類就可以了。App 類的內容如下:

#ifndef __APP_HPP__
#define __APP_HPP__

#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <iostream>

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 渲染一點有用的東西。

版權申明

該隨筆由京山游俠在2021年01月27日發布於博客園,引用請注明出處,轉載或出版請聯系博主。QQ郵箱:1841079@qq.com


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM