[vvill.dev] $

TopoToucher is a heightmap sandbox program that gives you the power of pushing points on a grid up and down to create interesting terrain visualizations. It is open source on Github, and the readme file overviews build instructions, controls, main features, and a quick summary of the most important functions in the program.

First Gallary Image Second Gallary Image Third Gallary Image

On Heightmaps

Heightmaps are an amazing and efficient way to store a whole 3D model as a single image. Acres of digital topography to explore, all stored on two dimensions! As I learned more about heightmaps and how they are commonly used, I had a few questions about why they are used how they are.

Why ◩? How about ⛝?

When heightmaps are rendered, the top-down wireframe view looks like a grid with a single slash across each square (in order to turn them into triangles for easier rendering by the GPU). I had a problem with that. Imagine a 3 x 3 grid of vertices. Now imagine a diagonal line of those vertices raised above the rest. With our 5-line squares, there will either be a single raised edge running along those raised vertices, or three peaks with lines coming down from all sides of each one.

What if, instead of two triangles, we stuck a vertex in the center and made four triangles, each pointing at the center? And that center point can be interpolated between the four surrounding points! This would result in higher detail heightmap meshes, and everything would be uniform, no matter which way you draw it!

Unfortunately, what you’d be sacrificing there would be performance. And a whole lot of it. It’s not just the fact that you’d be doubling the number of faces in the mesh, but you’d be sacrificing the power of triangle strips. GPUs are very good at doing one small thing many times, so one of the easiest ways to build meshes is with triangle strips. This technique is performed once on each row and includes remembering the last two vertices and adding another one to create a new triangle, like so: ◩◩◩◩◩◩. Imagine adding the vertices to a list in a row, doing one top vertex, then the bottom one, moving over and doing the top one, and so on.

Why Black and White?

“Why are heightmap images always in black and white? Why not use the full range of RGB to store higher detail height differences? Aren’t we wasting space?”

When building or taking apart a heightmap image, each pixel is made of a red, green, and blue value. In a grayscale image, those red green and blue values are exactly the same. So why not use the extra bits to store more detail instead of copying the same value 3 times?

As it turns out, pictures are smart. Most popular image formats, including PNG, JPEG, and BMP are all optimized to store grayscale images without any color data – just the shade of gray – and therefore, save a lot of space compared to a full color image. When you load the image up in a program however, it must adhere to the data structure it is being loaded into, and the program copies the shade of gray to each color channel.

Features

This part is also written in the Github Readme file so if you’ve already read that, skip to the difficulties section.

Rolloff Functions

Desmos

An image of the currently implemented falloff equations is shown above. You can go to the interactive Desmos graph by clicking on the image or clicking here. The x value represents the distance from the currently selected vertex, and the y value represents the value to be added/subtracted from that vertex. In TopoToucher, the currently selected rolloff function can be found in white in the bottom left corner of the screen.

Lighting

TopoToucher uses a small ambient light, and an orbiting sun lamp to show variation in topography. No user input is required to utilize these lights, and the sun orbits automatically.

There is the option of enabling Depth Cue, using the right click context menu. Turning on Depth Cue enables some OpenGL Fog (whose parameters are defined as constants at the start of main.cpp) which darkens the scene a bit, but will make your angles a bit more prominent. Below are two images showing a map with depth cue turned off (left, default) and turned on (right).

No Depth Cue With Depth Cue

Difficulties

As with all graphics projects, there was a lot of back and forth changing values and making things look right on screen. Beyond that, here are a few areas that caused me some stress when working on this project.

Aligning Vertex Location Equations

My biggest difficulty when building the program was making sure the vertex positions came out the same from building of the heightmap mesh, to the positioning of the cursor sphere, to moving the cursor sphere, and the modifying of the heights in the MoveVert function. MoveVert is probably the simplest example, because we are directly referencing indices in an array. In building the heightmap however, the positions must be scaled and recentered, and different height values must be collected in order to compute the vertex’s normal vector. When drawing the sphere and influence circle, more translation and scaling is needed.

I overcame this issue by being more verbose in my calculations, creating extra variables to mark significant points even if they were only used in calculating the next step, and writing down which operator (/ or %) controlled the x axis and y axis of the heights array.

Vertical Offset of Text

One bug I ran into and was unable to solve, was positioning relative text at different y positions. Under the right click context menu there is an option to enable axis labels, which displays the movement controls around the cursor. Showing the vim keybindings around the grid to signify directional movement was fine, but when I tried to draw W above S above the sphere to signify up and down, they were drawn over each other. I triple checked my math and I still don’t know what was going wrong, so I fixed it by displaying “W/S” over the sphere instead. If it works it works!

Future Plans

This project has massive room for growth. From beautifying to feature adding, and a whole lot of optimizing. Here’s my to-do list!

Shaders

Falling into the category of both beautifying and optimizing, one thing I need to do is move some implementation over to shader code. A fragment shader would do the terrain coloring much better (and even lay ground for some cooler stylized textures), and the program will be made much more performant when the heightmap displacement is performed and stored on the GPU with a vertex shader. In order to keep the traffic to a minimum, the heightmap itself would be stored in VRAM.

I am currently taking a class specifically about GLSL shaders, and I plan to make these integrations to TopoToucher be my final project for that class.

Skybox

The matte color background this program has is pretty boring. I’d like to create a skybox (or in OpenGL terms: cubemap) texture. I’m not sure if I want to try to procedurally generate one or just design one or find one online yet, but regardless, one is sorely needed.

BMP Import/Export

This is the to-do item that transitions this program from being a fun toy to an actual tool. The ability to export a map you’ve created in TopoToucher as an image, and render it as a heightmap somewhere else shouldn’t be too hard to implement. I have worked with bmp images before as textures in other graphics projects and I believe they will be the easiest to write and read from.

Click to Select Vertices

Originally, this was a core feature of the project proposal and part of the main scope. After much time spent on the alignment of the vertex location equations, failing to get my raycast collision equations working properly, and a deadline on the horizon, this feature was moved to the backlog. The current implementation of TopoToucher uses Vim keybindings to move a cursor around the grid of vertices, but this isn’t very quick and can be confusing. I plan on implementing the Möller–Trumbore intersection algorithm with the heightmap triangles and the mouse click ray vector.

Custom Color Layers

Sure you can go in the code and change values to make it look different, but I’d like it to be more user friendly than that. Maybe a config file that shows height to color values in an easy to read way. I also think it would be fun to add a bit of a particle system to make it more interesting, where it would randomly spawn patches of trees or flowers in the green areas, or rocks up the mountains.