Bgfx is a rendering library that supports Direct3D, Metal and OpenGL variants across 11 platforms and counting. It's easy to build and there are many examples available to help submerse you in the details. Understanding the line of separation between bgfx and the example code was relatively easy but took me more time than I would have thought. If you're interested in a quick example on how to use bgfx with your own project, read on.

I assume you have some prior graphics programming experience and that you've already followed the build instructions here. I'll be borrowing from the example-01-cubes project so make sure you build using the --with-examples option if you'd like to follow along.

You'll find the debug and release libraries in a folder named something like winXX_vs20XX located inside the .build directory (making sure you link bgfx*.lib, bimg*.lib and bx*.lib). To test if all is well, call the bgfx::init function.



#include "bgfx/bgfx.h" int main(void) { bgfx::init(); return 0; }

With all this in place, you should be able to initialize the system without error.

We'll need a window to render to. I use GLFW but SDL or anything else will be fine.



#include "bgfx/bgfx.h" #include "GLFW/glfw3.h" #define WNDW_WIDTH 1600 #define WNDW_HEIGHT 900 int main(void) { glfwInit(); GLFWwindow* window = glfwCreateWindow(WNDW_WIDTH, WNDW_HEIGHT, "Hello, bgfx!", NULL, NULL); bgfx::init(); return 0; }

Now we have to make sure bgfx has a handle to the native window. This is done via the bgfx::PlatformData struct and the 'nwh' member. If you're using GLFW, make sure you define GLFW_EXPOSE_NATIVE_WIN32 and include the glfw3native header. Now is also a good time to properly define a bgfx::Init object.



... #define GLFW_EXPOSE_NATIVE_WIN32 #include "GLFW/glfw3native.h" ... bgfx::PlatformData pd; pd.nwh = glfwGetWin32Window(window); bgfx::setPlatformData(pd); bgfx::Init bgfxInit; bgfxInit.type = bgfx::RendererType::Count; // Automatically choose a renderer. bgfxInit.resolution.width = WNDW_WIDTH; bgfxInit.resolution.height = WNDW_HEIGHT; bgfxInit.resolution.reset = BGFX_RESET_VSYNC; bgfx::init(bgfxInit); ...

Let's render something. We'll set the view clear flags and create a simple render loop.



... bgfx::setViewClear(0, BGFX_CLEAR_COLOR | BGFX_CLEAR_DEPTH, 0x443355FF, 1.0f, 0); bgfx::setViewRect(0, 0, 0, WNDW_WIDTH, WNDW_HEIGHT); unsigned int counter = 0; while(true) { bgfx::frame(); counter++; } ...

You should see a window with a purple background. Soak in the awesome.



At this point, we're ready to take on something more interesting. We'll steal a cube mesh from one of the example files.



struct PosColorVertex { float x; float y; float z; uint32_t abgr; }; static PosColorVertex cubeVertices[] = { {-1.0f, 1.0f, 1.0f, 0xff000000 }, { 1.0f, 1.0f, 1.0f, 0xff0000ff }, {-1.0f, -1.0f, 1.0f, 0xff00ff00 }, { 1.0f, -1.0f, 1.0f, 0xff00ffff }, {-1.0f, 1.0f, -1.0f, 0xffff0000 }, { 1.0f, 1.0f, -1.0f, 0xffff00ff }, {-1.0f, -1.0f, -1.0f, 0xffffff00 }, { 1.0f, -1.0f, -1.0f, 0xffffffff }, }; static const uint16_t cubeTriList[] = { 0, 1, 2, 1, 3, 2, 4, 6, 5, 5, 6, 7, 0, 2, 4, 4, 2, 6, 1, 5, 3, 5, 7, 3, 0, 4, 1, 4, 5, 1, 2, 3, 6, 6, 3, 7, };

Now we need to describe the mesh in terms of the vertex declaration, bgfx::VertexDecl.



... bgfx::setViewRect(0, 0, 0, WNDW_WIDTH, WNDW_HEIGHT); bgfx::VertexDecl pcvDecl; pcvDecl.begin() .add(bgfx::Attrib::Position, 3, bgfx::AttribType::Float) .add(bgfx::Attrib::Color0, 4, bgfx::AttribType::Uint8, true) .end(); bgfx::VertexBufferHandle vbh = bgfx::createVertexBuffer(bgfx::makeRef(cubeVertices, sizeof(cubeVertices)), pcvDecl); bgfx::IndexBufferHandle ibh = bgfx::createIndexBuffer(bgfx::makeRef(cubeTriList, sizeof(cubeTriList))); unsigned int counter = 0; ...

We're almost there. We just need to load a bgfx shader which we'll borrow from the example files in the examples/runtime/shaders directory. To do that we need to load the shader file contents inside a bgfx::Memory object before passing that to bgfx::createShader.



bgfx::ShaderHandle loadShader(const char *FILENAME) { const char* shaderPath = "???"; switch(bgfx::getRendererType()) { case bgfx::RendererType::Noop: case bgfx::RendererType::Direct3D9: shaderPath = "shaders/dx9/"; break; case bgfx::RendererType::Direct3D11: case bgfx::RendererType::Direct3D12: shaderPath = "shaders/dx11/"; break; case bgfx::RendererType::Gnm: shaderPath = "shaders/pssl/"; break; case bgfx::RendererType::Metal: shaderPath = "shaders/metal/"; break; case bgfx::RendererType::OpenGL: shaderPath = "shaders/glsl/"; break; case bgfx::RendererType::OpenGLES: shaderPath = "shaders/essl/"; break; case bgfx::RendererType::Vulkan: shaderPath = "shaders/spirv/"; break; } size_t shaderLen = strlen(shaderPath); size_t fileLen = strlen(FILENAME); char *filePath = (char *)malloc(shaderLen + fileLen); memcpy(filePath, shaderPath, shaderLen); memcpy(&filePath[shaderLen], FILENAME, fileLen); FILE *file = fopen(FILENAME, "rb"); fseek(file, 0, SEEK_END); long fileSize = ftell(file); fseek(file, 0, SEEK_SET); const bgfx::Memory *mem = bgfx::alloc(fileSize + 1); fread(mem->data, 1, fileSize, file); mem->data[mem->size - 1] = '\0'; fclose(file); return bgfx::createShader(mem); }

Now we can create a shader program and wrap up rendering our cube. The bx library has matrix helper methods or use your own. Either way, building the projection matrix and setting the view transform should look familiar. Don't forget to set the vertex and index buffers and submit the program we just created before advancing the next frame.



... bgfx::ShaderHandle vsh = loadShader("vs_cubes.bin"); bgfx::ShaderHandle fsh = loadShader("fs_cubes.bin"); bgfx::ProgramHandle program = bgfx::createProgram(vsh, fsh, true); unsigned int counter = 0; while(true) { const bx::Vec3 at = {0.0f, 0.0f, 0.0f}; const bx::Vec3 eye = {0.0f, 0.0f, -5.0f}; float view[16]; bx::mtxLookAt(view, eye, at); float proj[16]; bx::mtxProj(proj, 60.0f, float(WNDW_WIDTH) / float(WNDW_HEIGHT), 0.1f, 100.0f, bgfx::getCaps()->homogeneousDepth); bgfx::setViewTransform(0, view, proj); bgfx::setVertexBuffer(0, vbh); bgfx::setIndexBuffer(ibh); bgfx::submit(0, program); bgfx::frame(); counter++; } ...



Behold! A cube. Let's make it move.



... bgfx::setViewTransform(0, view, proj); float mtx[16]; bx::mtxRotateXY(mtx, counter * 0.01f, counter * 0.01f); bgfx::setTransform(mtx); bgfx::submit(0, program); ...

And we're done!



You can check out the completed example here. Note that I kept error handling and callbacks out to better highlight how bgfx is used. Hopefully this will give you a basic idea of how things work and enable you to layer in more advanced techniques. Be sure to take some time to scan through the example code and API documentation. Good luck and happy rendering!

Спасибо Бранимир Караџић!