While rendering voxels appears easy at a glance ("they're just cubes!"), there are actually many, many, trade-offs one has to take into account, mainly depending on the intended visual style and scale of the individual voxels.
Ultimately it depends on what your goals are; try not to set them too lofty!
Since there is no way to write about all possible methods, this article will talk about things in general, linking elsewhere for more details - if possible.
Talking about rendering is impossible without talking about hardware acceleration and thus GPUs (Graphics Processing Unit); so let's do that real quick!
The primary purpose of a GPU is to calculate/compute/solve a massive number of highly similar problems1 "all at once", by splitting up large sets of data into smaller groups, which are then worked thru by a large number of tiny processing units2.
These days GPUs are much more general purpose and are used in so many applications that we won't bother listing them here.
To hopefully nobodies surprise, rendering voxels is such a massively parallel process, that not using GPUs would be very (very) silly.
Now, how do we gain access to the awesome processing power of GPUs...?
Graphics Programming APIs
Unfortunately, directly accessing GPUs usually isn't possible, since...
- Operating systems must share GPU 'resources' between multiple processes.
- There are a multitude of hardware vendors and thus GPUs, not to mention drivers!
As such, we are forced to use a Graphics Programming API... of which there are several.
However, since you are most likely to want to run your program across many platforms and hardware combinations, we can safely ignore a whole bunch of them and choose from one of two APIs:
- To get started as fast as possible, with no regard as to how modern GPUs and their drivers work, use OpenGL.
- To be future-proof and get as much performance as possible out of your GPU, use Vulkan.
- If you're okay with proprietary stuff and are targeting Microsoft Windows/Xbox, you can use Direct3D.
Graphics Programming Libraries
If you find the previously mentioned APIs too burdensome or low-level, there are various rendering abstraction libraries available, using the lower APIs as backends:
Only libraries directly exposing 3D graphics are listed.
|OGRE||OpenGL / Direct3D|
Some of these libraries have bindings for other languages, so check out their documentation before rejecting any!
Windowing Abstractions & Libraries
Creating a surface to actually draw into is, due to the many platforms that exist, terribly difficult, so it's best to leave it to a windowing library...
General Rendering Methods
Converting voxels into meshes, then using a hardware-accelerated rasterizer to render a whole lot of triangles.
Converting voxels into tightly-fitting screen-aligned quadliterals, rasterizing them, then performing a Ray-AABB intersection test in the fragment shader to get cubes.
Send rays out of a camera into a volume of voxels, marching along them until voxels are hit, calculating a color based on the hit voxels and additional rays sent out from there.
This method makes it possible to achieve a high degree of photorealism and/or complex lighting effects, that are otherwise extremely hard to produce with other methods.
General Lighting Methods
TODO: Create article just for lighting?
General Culling Methods
Depending on the method you choose to render voxels, you may have to cull your geometry, so as to not overload your GPU with drawcalls & geometry.
- GigaVoxels: Ray-Guided Streaming for Efficient and Detailed Voxel Rendering
Commonly referred to as embarrassingly parallel.
Modern GPU's have literally hundreds, if not thousands, of 'units' running in parallel; the exact mapping of 'units' to 'threads' differs per GPU vendor.