Introduction

Ink Minecraft was my final project for CIS460 (Interactive Computer Graphics). I worked with Dzung Nguyen and Thy Tran to create a replica of the popular game Minecraft, which mimicked the style of Asian ink wash paintings.

Efficient Terrain Rendering and Chunking

In order to render the terrain faster, I implemented a “Chunking” system identical to the one used in the actual Minecraft games. Block data in Minecraft is stored in std::vectors and must be passed into OpenGL in the most efficient way possible. This is done through a function that passes only the outer faces of the terrain into the GPU. The terrain is stored as a series of “Chunks” of set size. Any faces inside of the chunks are not passed into VBOs (which is an unnecessarily expensive process). Furthermore, interior faces on adjacent chunks are also omitted from the VBOs. 

L-System Trees and Rivers

For our second milestone, I implemented procedural trees and rivers using L-Systems. For rivers, a stack of “Turtle” data structures is stored that contain information for position, orientation, and depth. There are two types of rivers: a Delta river that branches often, and a Linear river that mostly moves in a single direction. In the Delta river, the expanded sentence branches many times, whereas the linear river advances and branches only occasionally. A Bresenham’s line drawing algorithm is used to calculate and draw the connection between two points in the turtle’s walk. The depth of the river is also inversely proportional to the size of the stack of turtles, so that the more it branches, the thinner it gets.

For trees, I modified my expanded strings and characters to functions to cover rotation in all three dimensions. Instead of using Bresenham’s to connect different points in the turtle’s walk, I instead repeatedly incremented the turtle’s position by its orientation and then drew blocks in that new position.

I also implemented a distance fog effect by modifying the terrain’s fragment shader to interpolate between the sky color and the block colors, depending on the geometry’s relative z-Position to the player.

Multithreading

For multithreading, I used QT’s built-in QThreadPool class to generate terrain concurrently with player movement. My main role, in collaboration with Dzung Nguyen, was refactoring the chunking structure to prevent race conditions. I fully generated all the blocks in any given newly created chunk before passing it to a list of Chunks, which was passed into the main thread every time the game’s timer updated.

Skybox Shader 

The skybox utilizes a fragment shader that uses ray-casting in order to generate UV values for each coordinate in a screen-size quad. These UVs then sample various hard-coded color palettes for different times of day. The shader also draws a sun that changes position as a function of time. The sky changes color relative to the position of the sun by interpolating between palettes. The terrain shader also contains code such that the direction of the point light changes relative to the direction of the sun.

Post-Process Paint Shader

I created a fragment shader that took in a texture render pass of the game, and then modified it create a painterly effect. To achieve this effect, I used a modified Gaussian blur function that only averaged vertices of similar colors; this allowed me to maintain somewhat strong outlines around distinct shapes. I then took the blurred image and posterized it using a step function. I also sampled a regular Gaussian blur, and then mixed the posterized blurred image with the regular blurred image. Finally, for fragments that were above a specific saturation/lightness, I returned the original color, such that richer colors stuck out.