Something I've always wanted to learn is how to implement voxels into a 3D game similarly to Minecraft. I didn't want to just copy Minecraft however, since there are many easier ways to achieve that.
What made me interested in this endeavor was a small JavaScript voxel game I found online called "Star Defenders 3D" created by "Eric Gurt" (eric-gurt.github.io/StarDefenders3D).
This is what it looked like at the time (not my game):
I loved how the voxels in this game were small and spherical in nature, allowing for great level of detail and colors. This intrigued me, so I began studying different implementations of voxel rendering.
"Star Defenders 3D" used OpenGL Point rendering in these screenshots, which basically means each voxel was a single vertex in space, and was rendered as a 2d circle on the screen. This is done easily in OpenGL by using the primitive rendering mode of GL_POINTS. On its own, this renders each vertex as a square of pixels on the screen.
Using shaders, you can render this square as a circle. However, this circle would have a flat depth, and would not intersect with geometry as a sphere would.
In 2020, I did this in C++ and this is what it looked like:
As you can see, where the points overlap its obvious that they are indeed flat circles.
The next step to make these points spherical is to use a fragment shader to alter the depth of each pixel on the circle. It took me many months of researching online to find how to do this back then, but I eventually found ways of doing it.
Here's the results of them when I implemented it in C#:
The performance of using "Impostor Spheres" as voxels has its pros and cons. The main benefit being that each voxel can be represented with one or four vertices (depending on how accurately you want the spheres to be rendered). However, the OpenGL Rendering pipeline does not like it when you manually alter the depth of fragments. This causes some inefficiencies when rendering the spheres, as they are unable to be "early z-tested" when being rendered. Which basically means their fragment shader code will always execute, even if they are pixels which are obscured completely by other impostor spheres in-front of it.
I wanted to see how efficiently I could render these voxels, so I experimented with tessellation shaders to render each face using only a single vertex.
It was relatively successful in lowering the amount of memory needed to store all the many vertices in a sea of voxels by an order of magnitude. But now I had the task of rendering a world made of voxels. To do this, I implemented "chunks", which similarly to Minecraft, are large 'chunks' of thousands of voxels which are rendered in groups. This can be used to partition the world into segments, allowing for much faster rendering and storage of the data.
There was no real lighting done, but I simply lowered the brightness of the voxels based on their height, which gave a nice look:
This meant that I would have to do quite a lot of refactoring in order to make this run at any acceptable level of performance, including switching to C++ and implementing multi-threading.
I will be posting updates on my progress in voxels using C++ OpenGL in the future.