Terrain sculpting and terrain loading from greyscale images
Jul 2013
Open Source Project
Google Summer of Code 2013 Week 3
@ projects > gsoc 2013

Week 3 of Google Summer of Code 2013 is mainly to get a terrain system in place for the user to sculpt structures, such as dams, terraces, slopes and valleys, for the flood simulation in later weeks. This will provide an interesting way for the user to interact with the flood and also allow better understanding of how certain structures affect flood flow.

Since there is a possibility that we need to load actual terrain data into the 3D terrain to simulate flood at a particular region in Iowa, I have also created a system to load terrains from greyscale images.

Sculpting Terrain

Part of the project specification was to allow users to sculpt the terrain in real time, so that we can see how the flood water reacts due to structural changes. Thus, I created a height-based sculpting system:

There are three main brushes in the system currently: Add, Remove and Flatten. They can be used to quickly create structures such as hills, valleys and rivers. The flatten brush in particular can create terrace steps that would be useful for water to fall and splash.

The Add brush is basically adding a lump of circular heights based on the cosine function:

var modulator = Math.cos(dist / radius * Math.PI / 2.0);
terrainHeight[vertexId] += amount * modulator;

This means that grid points at the center of the circle will get the full value, grid points at the circumference will get a value of 0, and anything in between will get a smooth cosine-interpolated value. This creates a smooth circular lump.

The Remove brush uses the same equation, but subtracts heights instead of adding, of course.

The Flatten brush first calculates the average height of all grid points within the circle, and then uses the same cosine function to blend between the average height and the current height.

var newHeight = modulator * averageHeight + (1 - modulator) * thisTerrainGeomVertex.y;

This helps to provide a smooth transition from the current terrain to the flattened terrain.

These sculpted heights are stored in another set of arrays so that it is easy to remove all edits without changing the base heights.

A visual brush indicator is visible to show how large the brush diameter is. Its height indicates the brush add amount.

Notice that the brush does not follow exactly where the mouse cursor is. The mouse intersect test is done on a flat plane below the terrain, and the brush is applied to the terrain vertically above this intersection point. I have done it this way because I need to place augmented reality markers on a flat plane to sculpt the terrain in future weeks.

Back To Top Back To Top

Loading Terrain Images

In this recording, I’m showing how different greyscale images can be used to load the terrain. The greyscale images are obtained from the Iowa Geograhical Map Server (IGMS), as provided by Prof. Ibrahim Demir.

Example greyscale terrain map obtained from IGMS
Example greyscale terrain map obtained from IGMS

The terrain image is first resized to the same resolution as the terrain resolution, so that one pixel on the resized image maps to one vertex on the terrain mesh. The image resizing is done by first drawing the image from the terrain image to a temporary resized canvas, and then copying out that data to a resized Image:

//draw to scaled canvas to scale this image
imageCanvasElemContext.drawImage($origImageObj[0], 0, 0, TERRAIN_RES, TERRAIN_RES);

//set scaled data from canvas to scaled image
$scaledImageObj[0].src = $imageCanvasElem[0].toDataURL();

This needs to be done only once, during initialization.

The terrain images obtained has some high frequency noise which translates to spikes in the actual 3D terrain. I used Stack Blur to pre-filter the image a little first before loading it into the terrain:

stackBlurImage('scaledTerrainImage', 'terrainImageCanvas', options.terrainPreBlur, false);

where scaledTerrainImage is the ID of the scaled image element from the previous step, terrainImageCanvas is the ID of the canvas to draw the blurred image, options.terrainPreBlur is the amount to blur, and false just indicates that I do not want to blur the alpha channel. Note that the function requires an Image as an input, which is the reason why I copied the data from the resized canvas to a resized Image in the earlier step.

You can see the effect of this blurring in real-time in the displayed terrain image at the top left corner of the screen in the video recording.

Now that I have a resized and blurred the terrain image, it is then a simple operation of reading the pixel value and using that to change the terrain height at that location:

var data = imageCanvasElemContext.getImageData(0, 0, TERRAIN_RES, TERRAIN_RES).data;
var i, normHeight;
for (i = 0; i < numVertices; i++)
    normHeight = Math.abs(data[i*4] / 255.0 - 0.5);
    //then assign normHeight to terrain etc

Unfortunately, the greyscale images that I got from IGMS are not height maps but are hillshade maps. I am not entirely sure how to interpret these maps to get the correct heights because the greyscale values indicate shading rather than heights. What I have done at the moment is to map mid grey as the lowest height. Anything lighter and darker than mid grey will be higher. That’s what the “Mid Grey Lowest” checkbox in the demo is for. I’ll have to come back to this topic in future weeks.

The codes used here are summarized to bring the idea across in a simpler way. Please take a look at threejs_terrain.html to see the actual codes.

Back To Top Back To Top


Back To Top Back To Top

Source Codes

The source codes at the end of week 3 can be obtained from my ifc-ar-flood GitHub repository. Checkout the “GSoC_wk_03_end” tag to get the state of the source codes at the end of week 3.

The main file is threejs_terrain.html.

Please remember that you need to serve the HTML pages using a http server before you can see them as intended.

Back To Top Back To Top

Skeel Lee Skeel Lee
Facebook Google+ Twitter Tumblr
YouTube Vimeo Flickr Pinterest
Senior FX TD / R&D
Digital Domain 3.0 (Previously at Sony Pictures Imageworks, MPC, Industrial Light & Magic, Double Negative)
LinkedIn IMDb GitHub Stack Overflow
I am a Senior Technical Director with strong interests in both tech and art. My life evolves round VFX, photography, software engineering, tools programming and generally anything that looks / sounds cool.
I have done a variety of CG programming, including fluid sims, muscles, soft/rigid bodies, raytracing etc. These knowledge complement the visual works that I do as a TD in VFX.
I was interviewed by The Straits Times in May 2014 for my VFX work in X-Men: Days of Future Past.