编译并使用bgfx
riteme.site

编译并使用bgfx

最近发现了一个似乎很好的图形引擎bgfx
它是使用C++进行编写的,也有C99的接口,功能还比较丰富,支持DirectX 9/11和OpenGL 2.1+。
这是一个跨平台的库,我在Ubuntu 14.04上编译了成功了。
编译过程十分简单。本文就介绍Linux下的编译过程。

编译

这里我只写了Linux的编译过程,其他平台我还没试过,具体参见bgfx’s document
首先需要编译器支持,确保你的编译器满足下列要求:

  • Clang 3.3及以上
  • GCC 4.6及以上

同时准备依赖库:

1
sudo apt-get install libgl1-mesa-dev x11proto-core-dev libx11-dev

然后从GitHub上下载源码。因为bgfx依赖于bx,因此我们还要下载bx:

1
2
3
4
cd ~/Downloads  # 下载到Downloads目录
git clone git://github.com/bkaradzic/bx.git
git clone git://github.com/bkaradzic/bgfx.git
cd bgfx  # 进入源码目录,准备编译

使用make生成工程:

1
make

接下来进行编译:

1
make linux-release64

提示

使用make还可以生成其他版本的,其格式是:
make [平台]-[release/debug][32/64]
例如,如果要生成32位的bgfx,使用以下命令:
make linux-release32

如果编译过程中没有报错,那么在.build/文件夹下会有对应的编译后的文件。

运行样例

bgfx提供了很多样例,编译后的样例在.build/对应的文件夹下(我的就在.build/linux64_gcc/bin/下),在目前bgfx没有什么教程的情况下是很好的学习资料。
由于样例需要很多资源文件(如着色器、材质之类的),而它们都在exmaples/runtime目录下,
因此需要在exmaples/runtime目录下用相对路径来运行。

首先我们切换到样例的目录。对我而言,使用以下命令:

1
cd .build/linux64_gcc/bin/

如果你编译的是32位,将上面的64改成32即可。

然后使用ls命令查看样例,可以看到目前有28个样例(包括Hello, world!):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
example-00-helloworldRelease         example-17-drawstressRelease
example-01-cubesRelease              example-18-iblRelease
example-02-metaballsRelease          example-19-oitRelease
example-03-raymarchRelease           example-20-nanovgRelease
example-04-meshRelease               example-21-deferredRelease
example-05-instancingRelease         example-22-windowsRelease
example-06-bumpRelease               example-23-vectordisplayRelease
example-07-callbackRelease           example-24-nbodyRelease
example-08-updateRelease             example-25-c99Release
example-09-hdrRelease                example-26-occlusionRelease
example-10-fontRelease               example-27-terrainRelease
example-11-fontsdfRelease            geometrycRelease
example-12-lodRelease                libbgfxRelease.a
example-13-stencilRelease            libbgfx-shared-libRelease.so
example-14-shadowvolumesRelease      libexample-commonRelease.a
example-15-shadowmaps-simpleRelease  shadercRelease
example-16-shadowmapsRelease         texturecRelease

可以看到每个样例都十分具有代表性,今后的学习就从它们开始。

同时注意到,有两个.a的静态库和一个.so的动态库,我们可以用它们来编译程序。

然而并不是在这里运行样例,我们要回到runtime目录来运行:

1
cd ../../../examples/runtime

假如要运行00-helloworld,使用以下命令:

1
../../.build/linux64_gcc/bin/example-00-helloworldRelease

如果不出意外,能看到以下窗口(图片来自bgfx document):

bgfx helloworld

bgfx的文档也给出了样例的列表。

编译我们自己的bgfx程序

为了通用性,这里使用最基础的终端编译的方法,我们直接用g++进行编译。

首先,我们创建一个目录来存放我们的程序:

1
2
mkdir my-first-bgfx
cd my-first-bgfx

然后将bgfx的静态库复制过来:

1
cp [bgfx的目录]/.build/linux64_gcc/bin/*.a .

这时目录下会多出两个.a文件: libbgfxRelease.alibexample-commonRelease.a

静态库 vs 动态库


我们在这里选用了静态库,而不是动态库,是考虑了用户的原因。
因为bgfx没有什么很方便的安装方法,使用静态库就可以避免安装的过程,尽可能少的对用户的系统进行修改。
当然,使用静态库会增大程序体积。静态链接后,我们的bgfx程序会有3MB多…

然后需要将bgfx及其依赖库的bx的头文件给复制进来。为了方便,我们还将bgfx用于样例的common库也复制进来:

1
2
3
cp -rf [bgfx的目录]/include/* .         # bgfx
cp -rf [bgfx的目录]/examples/common/ .  # bgfx-common
cp -rf [bx的目录]/include/* .           # bx

下面我们来照着bgfx的hello, world来写第一个程序。我们将会在窗口的左上角打出Hello, world!的字样:

创建程序main.cpp

1
2
touch main.cpp
[使用你的编辑器] main.cpp

首先添加头文件:

1
2
3
4
#include <cstdint>  // uint32_t

#include "bgfx/bgfx.h"      // bgfx
#include "common/common.h"  // bgfx-common

然后添加_main_函数。注意之所以是_main_而不是main,是因为bgfx的common库为了更好的处理以已经将main实现了,
于是设置_main_作为程序入口。

1
2
3
int _main_(int argc, char *argv[]) {
    /* 代码 */
}

_main_函数内,我们先做一些设定:

1
2
3
4
uint32_t width  = 1280;              // 窗口宽度
uint32_t height = 720;               // 窗口高度
uint32_t debug  = BGFX_DEBUG_TEXT;   // debug模式开启,可以直接在窗口输出文字
uint32_t reset  = BGFX_RESET_VSYNC;  // 设置垂直同步

注意

似乎widthheight的设置是没有用的,似乎无论设为多少,打开后都会变成1280x720,我也不知道是为什么... 如果你也出现了这种情况,不要惊慌,不要害怕...

下面是加载bgfx:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
bgfx::init();                             // 载入
bgfx::reset(width, height, reset);        // 设置
bgfx::setDebug(debug);                    // 启用调试
bgfx::setViewClear(                       // 设置清空的状态
    0,                                    // bgfx中有View的概念,默认情况下是View 0
    BGFX_CLEAR_COLOR | BGFX_CLEAR_DEPTH,  // 表示要清空颜色缓冲和深度缓冲
    0xFFFFFFFF,                           // 颜色缓冲的清空值,0xFFFFFFFF是白色
    1.0f,                                 // 深度缓冲的清空值,默认为1.0f
    0                                     // 模板缓冲的清空值,默认为0
);

接下来就是渲染了,我们使用一个循环同时处理事件和渲染:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
// entry::processEvents是common库一个很方便的函数
// 它能帮我们处理几乎所有的窗口事件。
// 如果窗口大小有变化,width和height的值就会变化
// 如果窗口被关闭,就会返回true
while (not entry::processEvents(width, height, debug, reset)) {
    bgfx::setViewRect(0, 0, 0, width, height);  // 由于窗口大小会变化,因此这里要重新设置

    bgfx::touch(0);  // 切换到View 0

    bgfx::dbgTextClear();                                                      // 清空调试输出的文字
    bgfx::dbgTextPrintf(0, 0, 0x4F, "Hello, world!");                          // 打印"Hello, world!"
    bgfx::dbgTextPrintf(0, 1, 0x4F, "width = %d, height = %d", width, height); // 输出窗口大小

    bgfx::frame();  // 提交所有的渲染操作,准备切换到下一帧
}

注意

在函数bgfx::dbgTextPrintf中,第三个参数是设置文字的前景色和背景色。 其中,bgfx用0-F作为颜色的标记。在上面两位的十六进制数中,第一个指定背景色,第二个制定前景色。 这里将这些颜色标记列出来: 0: 无色 1: 暗红色 2: 暗绿色 3: 黄色 4: 蓝色 5: 紫色 6: 深蓝色 7: 灰色 8: 深灰色 9: 红色 A: 嫩绿色 B: 米黄色 C: 浅蓝色 D: 浅紫色 E: 天蓝色 F: 白色

最后,当窗口关闭时,entry::processEvents就会返回true,于是退出循环,最后要退出bgfx:

1
bgfx::shutdown();

现在来编译main.cpp

1
g++ -std=c++11 main.cpp -o exec -L. -lbgfxRelease -lexample-commonRelease -lpthread -lGL -lX11

注意编译器要打开C++11支持,如果你的编译器不支持C++11,那我很好奇你是怎么把bgfx编译成功的。
-lpthread-lGL-lX11是bgfx依赖的库,分别是POSIX线程库、OpenGL库和X11库。

如果没有错误报出,运行exec就能看到以下窗口:

1
./exec

helloworld

如果你出现了什么意外,下面贴出了完整的程序用于对照:

 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
#include <cstdint>  // uint32_t

#include "bgfx/bgfx.h"
#include "common/common.h"

int _main_(int argc, char *argv[]) {
    uint32_t width = 1280;
    uint32_t height = 720;
    uint32_t debug = BGFX_DEBUG_TEXT;
    uint32_t reset = BGFX_RESET_VSYNC;

    bgfx::init();
    bgfx::reset(width, height, reset);
    bgfx::setDebug(debug);
    bgfx::setViewClear(
        0,
        BGFX_CLEAR_COLOR | BGFX_CLEAR_DEPTH,
        0xFFFFFFFF,
        1.0f,
        0
    );

    while (not entry::processEvents(width, height, debug, reset)) {
        bgfx::setViewRect(0, 0, 0, width, height);

        bgfx::touch(0);

        bgfx::dbgTextClear();
        bgfx::dbgTextPrintf(0, 0, 0x4F, "Hello, world!");
        bgfx::dbgTextPrintf(0, 1, 0x4F, "width = %d, height = %d", width, height);

        bgfx::frame();
    }

    bgfx::shutdown();
}

至此,我们的第一个bgfx程序就完成了。解决了编译的问题,就可以继续学习使用bgfx了。