Creating a displacement map from an image

I am trying to use mapbox’s terrain api to generate a terrain mesh. I read some articles that mentioned that they use a plane buffer geometry with a displacement map. So given such an image, to get the height of the terrain at a pixel the formula is:

height = -10000 + ((R * 256 * 256 + G * 256 + B) * 0.1)

I know that the displacement map only looks at red values, and this is what I was trying to do (ImageData is apparently in RGBA):

const ctx = canvas.getContext(‘2d’);
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const converted = new Uint8ClampedArray(4 * canvas.width * canvas.height);
for (let i = 0; i < imageData.data.length; i += 4) {
const height = (imageData.data[i] * 256 * 256 + imageData.data[i + 1] * 256 + imageData.data[i + 2])
converted[i] = height;
}

I am not exactly sure what value I should be storing in the array, the height is always 100000 for the location I am testing with. On the mesh standard material, I set the displacementBias = -10000 and the displacementScale = 0.1 like in the formula above. I have never used any texture maps other than map, so this is all new. Also does this mean I can only have 256 different heights?

I tried reducing the problem to a simple displacement map, just a red canvas (2209x2209) and I expected the vertices to shift 255 units up but it doesn’t work so I must be doing something wrong. Can anyone spot anything wrong in my code?

``````export default function buildTerrainTexture(canvas) {
const ctx = canvas.getContext('2d');
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const converted = new Uint8ClampedArray(4 * canvas.width * canvas.height);
for (let i = 0; i < imageData.data.length; i += 4) {
const height = (imageData.data[i] * 256 * 256 + imageData.data[i + 1] * 256 + imageData.data[i + 2]);
converted[i] = 255;
}

const convertedImageData = new ImageData(converted, canvas.width, canvas.height);

ctx.putImageData(convertedImageData, 0, 0);

const texture = new THREE.Texture(canvas);
// texture.minFilter = THREE.NearestFilter;
// texture.magFilter = THREE.NearestFilter;
return texture;
}
``````

This is where I call the buildTerrainTexture:

``````    const terrainTexture = buildTerrainTexture(canvas);

this.map.geometry.dispose();

this.map.material.dispose();

this.map.geometry = (new THREE.PlaneBufferGeometry(this.opt.mapSize, this.opt.mapSize, 15, 15)).clone();

this.map.material = new THREE.MeshStandardMaterial({ color: 0xff0000, displacementMap: terrainTexture, metalness: 0, roughness: 1 });

this.map.material.color.convertSRGBToLinear();

this.map.material.displacementMap.needsUpdate = true;

const helper = new VertexNormalsHelper(this.map, 2, 0x00ff00, 1);