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!
Спасибо Бранимир Караџић!
Top comments (14)
In order to use the
bgfx::setPlatformData
function, you also need to includebgfx/platform.h
header file.bgfx::VertexDecl
has been renamed tobgfx::VertexLayout
.This line:
Should be
this returns null for me.
filePath ends up being "shaders/dx11/vs_cubes.binýýýý"
hard coding this to "shaders/dx11/vs_cubes.bin" results in no change despite the file being present...
Any idea why?
Because he uses memcpy, a null terminator never gets written to the end of the string. You need to allocate with "char* filePath = (char*)calloc(1, shaderLen + fileLen + 1);", then there will be a zero at the end of the string
I should have read this. I took me a good 10 minutes to figure out why I was segfaulting
My program stops at "nULLER7" - dont mind the language its my native lang word
using Tdmgcc 9.2;
Sorry figured it out it was silly
In order to get bgfx to clear the screen, you also need to call
bgfx::touch(0)
beforebgfx::frame()
.For platform compatibility, you also need to add
bgfx/include/compat/***
to your include paths.For example, for Visual Studio 2012 and higher, add
bgfx/include/compat/msvc
to your include paths.For
bx::Vec3
you also need to includebx/math.h
.For
FILE
, andfopen
function to compile, you also need to includecstdio
.Thanks for the great tutorial! I learned a lot about using bgfx!
its causes a LNK2019 error