Cross-platform, graphics API agnostic, “Bring Your Own Engine/Framework” style rendering library, licensed under permissive BSD-2 clause open source license.
bgfx is a rendering library. It acts as an abstraction layer over the platform rendering APIs along with some sauce for added efficiency. It also has bindings to most popular languages including C#, F#, D, Go, Haskell, Python, Rust and Swift. I don’t claim to have built anything substantial with bgfx, but I did manage to figure out how to get a square on screen. The documentation and examples are thorough but they do not hold your hand through the process. So let me.
Disclaimer: This tutorial is a result of initial exploration with bgfx and getting a square on screen. I do not claim to have the best makefiles, memory safe code or efficient methods. Those topics are for future tutorials.
bgfx is quite close to the metal innards of 3d rendering, being just a thin wrapper. So I would highly recommend going through this OpenGL Tutorial to get comfortable with how modern 3d rendering works. It would really help in making sense of what bgfx is doing at any moment.
bgfx only handles the rendering, which means we need to use a windowing library (the brave can use the platform code directly, be my guest). I chose SDL. Ensure your system has SDL dev libraries installed along with bgfx dependencies on Ubuntu.
Ok, for the sake of this tutorial, lets make a simple C++ project, starting with a Makefile that links up SDL2, GL, X11, DL, pthread and rt libraries.
And lets add a simple main.cpp file as well.
You should try and run make and then run the program to see if everything is setup correctly.
Getting a window on screen
As mentioned earlier, we would be using SDL to create a window and poll for events. The code for that is fairly straight forward. If you need to understand more about setting up SDL, I recommend going through LazyFoo’s SDL tutorial series.
Compiling and running the program at this point should get you something like the following image.
Getting and building BGFX
First clone the bgfx repository and its dependencies bx and bimg libraries into the project.
Then lets build bgfx.
If you following along on anything other than an Ubuntu system, you can find instructions to build bgfx here.
At the end of this process, you’ll have a bunch of library files and executables sitting in the bgfx/.build/linux64_gcc/bin folder. Let us link them in the makefile.
I’ve linked the shared version to make compile faster, but you can also link the static library libbgfxRelease.a if you want.
Now that the window is ready and bgfx is linked, lets initialize bgfx in our code.
You should see a pleasant purple being rendered in the window now.
Vertices and Indices
If you went through the OpenGL tutorial linked above, you would know that getting a triangle on screen isn’t as simple anymore. We need to define a few things before we can see any action on screen. These include:
The vertex buffer: a list of 3d vertices that define what your 3d shape looks like in normalized space.
The index buffer: a list of vertex indices that define which of the vertices in the vertex buffer form a triangle.
A vertex and a fragment shader which compute how each of the vertices are renderered and colored on the screen.
A vertex attribute declaration which translate what your vertex buffer means for the shaders.
We know each of the vertices of the square would have a 3 points that denote where the vertex is in space, and lets also have a 4th variable that represents the color of the vertex. We hold these values in a struct and use bgfx::VertexDecl to define the vertex attribute declaration.
Lets define the vertices that form our square.
And the indexes of the vertices that form two triangles to make a square.
We also need a bgfx::VertexBufferHandle variable to hold the actual vertex buffer and a bgfx::IndexBufferHandle variable to hold the index buffer.
Now we need to initialize the vertex buffer handle and the index buffer handle with the vertices and triangle we have defined above. Put this after bgfx::init().
bgfx has a shader language very close to glsl with a few caveats. You can read about it on this page. We need a vertex shader and a fragment shader to render our square on the screen. Lets start with the vertex shader in a new file and name it v_simple.sc.
bgfx_shader.sh has many useful macros that we can use in our shaders. We use the u_modelViewProj variable from the file to get a projection of our position into the screen and we pass on the color as it is.
Let’s create a fragment shader and name it f_simple.sc.
The fragment shader just returns the color as it is.
bgfx requires another file apart from just the vertex and fragment shaders called varying.def.sc which defines the input and output variables being used in these shaders. Lets create that as well.
Now we need to compile these shaders before we can use them with bgfx. The tool for that was built along with the bgfx library. You can find the tool shadercRelease in bgfx/.build/linux64_gcc/bin.
Lets run the shaderc tool to create our compiled shader files.
We can add these lines to our make file as well so that the shaders are recompiled when we build.
Next up we want to load our shaders into memory and into bgfx::ProgramHandle so that we can use them to render our square.
We’re so close. We have everything we need to start rendering a square on the screen. We need to define where the camera is and where it is looking at.
Next we define a 4x4 matrix that defines where our square is going to be and what orientation it is at. We also set the index and vertex buffer handles and then submit the drawing with the program.
And thats it! Make and run and you would see a square with a horizontal gradient of red to green.
This is the whole main.cpp program.
This program is a direct subversion of the cubes example from the examples list in the bgfx docs. I had to dig in to really make sense of how the shaders needed to be and how to set up the camera. I hope that integrating bgfx on Ubuntu would be an easier task for you now. Between the examples and the docs, it is fairly easy to figure out the corresponding steps for other platforms. You can find the full repository of this tutorial at Github.