Space Minecraft
Ray Marching Collisions, Post-processing, Perlin Noise, Proceduarl Generation C++, GLM, QT
Video
https://www.youtube.com/watch?v=u8vedHiUQ7c
Overview
A spherical Minecraft world built in C++ with OpenGL, developed as a team of three. Unlike a traditional flat Minecraft clone, our world takes place on a spherical planet, which required rethinking standard approaches to collision detection, player physics, and terrain generation.
I was responsible for the post-processing pipeline, player collision detection on the curved surface, procedural cave generation, and the procedural skybox.
Collision Detection on a Sphere
In flat Minecraft, collision detection uses grid marching — stepping along axis-aligned grid cells. On a sphere, blocks don’t sit on an axis-aligned grid, so this approach doesn’t work.
I replaced grid marching with ray marching, stepping along the ray in small fixed increments and checking block occupancy at each step. The key adaptations for the spherical environment:
- The player’s velocity is decomposed into local axes (surface-relative forward, right, up).
- The bounding box is constructed perpendicular to the planet surface using the player’s local coordinate frame.
- The surface normal is updated every tick based on the player-to-planet-center vector.
- Horizontal mouse rotation uses the current surface normal as the rotation axis, not the global up vector.
- The camera orientation is parented to the player so the view always aligns with the local horizon.
Likewise, block addition and deletion were also implemented via collision detection, where a certain distance along the player’s local forward vector is raymarched, and if there is a collision we are able to delete the block, if it is empty, we are able to delete it.
Post-processing underwater effects
The scene is first rendered to a framebuffer object. A screen-filling quad is then drawn, and the fragment shader samples the framebuffer’s color and depth textures to apply effects.
When the player is submerged in water, Perlin noise distorts the UV coordinates to create a liquid rippling effect, and a blue tint is composited over the scene. The same structure is used for lava with a red tint.
While setting up the post-process pipeline, I encountered a bug where the only 1 fourth of the scene was being rendered as a triangle, with the scene inside being heavily distorted.
I could not figure out what was causing the issue, until I hooked the program up to RenderDoc.
I was expecting x4 vec3s to be processed in the vertex shader of the post-processing pipeline, but RenderDoc was showing x3 vec4s instead.
It turns out the GLSL vertex shader was expecting vec4, but I was buffering vec3s
Note that glVertexAttribPointer expects size 4
if ((handle = m_attribs["vs_Pos"]) != -1 && d.bindBuffer(POSITION)) {
context->glEnableVertexAttribArray(handle);
context->glVertexAttribPointer(handle, 4, GL_FLOAT, false, 0, nullptr);
}
Wrong
std::vector<glm::vec3> glPos { glm::vec3(-1,-1,1),
glm::vec3( 1,-1,1),
glm::vec3( 1, 1,1),
glm::vec3(-1, 1,1) };
glBufferData(GL_ARRAY_BUFFER, sizeof(glm::vec3) * glPos.size(), glPos.data(), GL_STATIC_DRAW);
Correct
std::vector<glm::vec4> glPos { glm::vec4(-1,-1,1,1),
glm::vec4( 1,-1,1,1),
glm::vec4( 1, 1,1,1),
glm::vec4(-1, 1,1,1) };
glBufferData(GL_ARRAY_BUFFER, sizeof(glm::vec4) * glPos.size(), glPos.data(), GL_STATIC_DRAW);
Procedural Cave System
Underground caves are generated by sampling 3D Perlin noise at each block’s world position. When the noise output is below zero, the block is set to empty. The sampling range is restricted to blocks below the terrain surface and above the planet’s core shell, preventing caves from breaching either boundary.
The planet’s core shell is set to type BEDROCK so that it could not be removed by the player, and blocks below a certain threshold were set to type LAVA instead of EMPTY.
Procedural Skybox
The skybox is rendered in the lighting pass of the deferred rendering pipeline when the depth buffer reads 1.0 (no geometry hit). The camera’s forward, right, and up vectors are used to reconstruct a view direction from the screen UV.
Stars are generated with 3D Worley noise sampled at high frequency. When the noise value falls below a threshold, a star is drawn.
Nebulae are generated with 3D Perlin FBM, masked by a separate Perlin noise passed through smoothstep to give them defined edges. The nebula is animated by slowly offsetting the noise sampling position over time. A purple-to-black color gradient is applied.
Reference: CIS 5600 MIS - Adam Mally CIS 5600 Game Engine Paradigms - Adam Mally https://www.bowerbyte.com/posts/blocky-planet/ https://dev.thi.ng/gradients/