Another Programming Blog

(mostly iOS and Ruby on Rails)

WebGL: Creating a Landscape-mesh With Three.js Using a PNG-heightmap

Ok, this isn’t very complicated, even though it looks kind of cool.

The aim is to go from this:

Flat heightmap example png

to this:

Three.js landscape

The idea is that the brighter a pixel on the heightmap is, the higher the elevation of the resulting mesh is. So the first step is to get the values for all the pixels in the PNG. This is accomplished by drawing the image onto a canvas, and then getting the pixel data from it. It looks something like this:

canvas.getContext('2d').drawImage(img, 0, 0, img.width, img.height);
var data = canvas.getContext('2d').getImageData(0,0, img.height, img.width).data;

The next step is to create a planar mesh with ThreeJS and go through all the pixel and set their z-value, their elevation, according to the color of the pixel.

for (var i = 0, l = geometry.vertices.length; i < l; i++)
{
  var terrainValue = terrain[i] / 255;
  geometry.vertices[i].z = geometry.vertices[i].z + terrainValue * 200 ;
}

Please keep in mind that in this case the width in pixels is 1 more than the number of segments in the plane. (If there’s one segment, theres 2 vertices, two segments, 3 vertices, and so forth..) If that’s not the case more work is required, because the landscape will be “skewed” if height and width dont correspond correctly.

And that’s it.

I also afterwards call

geometry.computeFaceNormals();
geometry.computeVertexNormals();

so the shading according to the light-source is correct.

You can get the code from Github.

P.S.: You can follow me on Twitter.

Comments

Webmentions