# Interpolate colors between vertex in a plane

So I have this plane generated with two different Perlin noises. It has mainly 3 regions, divided by color. I want to smooth out the lines, to create a "gradient" so there are no abrupt color changes:

Each zone corresponds to a biome which is decided by a PerlinNoise called Humidity and another one called Temperature, so each vertex receives a biome according to the following chart with its corresponding humidity and temperature vertices' height:

So this is my code (I commented almost every step):

        //CREATES the terrain material and geometry + adds the color attribute
let TerrainMaterial = new THREE.MeshPhongMaterial({side:THREE.DoubleSide, flatShading: true,vertexColors: true});
let TerrainGeometry = new THREE.PlaneGeometry(step, step, 16,16);
TerrainGeometry.setAttribute( 'color', new THREE.BufferAttribute( new Float32Array(  TerrainGeometry.attributes.position.count * 3), 3 ) )
const colors = TerrainGeometry.attributes.color;

//The position like this is to create a grid so every tile is next to eachother
let terrain = new THREE.Mesh(TerrainGeometry,TerrainMaterial)
terrain.position.set(x, 0, z).multiplyScalar(step)

// Create some variables
const TemperatureVertices = TemperatureTerrain.attributes.position;
const HumidityVertices = HumidityTerrain.attributes.position;
let pos = TerrainGeometry.attributes.position;

const lerp = (a, b, amount) => (1 - amount) * a + amount * b;

//iteration through each vertex
for(let i = 0; i < pos.count; i++){

//gets temperature and humidity maps vertex height
let temperature = TemperatureVertices.getY(i) * 2;
let humidity = HumidityVertices.getY(i)* 2;

//this function returns the biome according with the chart in the post by giving it temperature and humidity variables
let BIOME = this.Biomes.determineBiome(temperature,humidity,new THREE.Vector2(x, z),i);

//This retrieves the fractional part of the numer, ex: frac(-2.8) = 0.8 or frac(10.09) = 0.9
var a = Math.abs(temperature) - Math.floor(Math.abs(temperature))
var b = Math.abs(humidity) - Math.floor(Math.abs(humidity))

//This variables determine the "blending zone", wich you will understand later
let AMPLIFIER = 0.1;
let AMPLIFIER1 = 0.9;

//Instantiates the temperature and humidity variables
let temperature_blending = temperature;
let temperature_weight = 1;

let humidity_blending = humidity;
let humidity_weight = 1;

//This is executed if the temperature is close to the next biome chart row/column
if(a < AMPLIFIER){
//This algorithm gives me like a percentage of how much the vertex is influenced by the other biome.
let x = Math.abs(temperature);
temperature_weight = (x - Math.floor(x)) / AMPLIFIER

//here based on the temperature, I can detect what is the closer column-
if(temperature > 0){
temperature_blending -= 0.5
}else{
temperature_blending += 0.5
}
//here I repeat the process but backwards as my function has to work with negative numbers when the closest biome column is further away than the original one.
}else if(a > AMPLIFIER1){
let x = -Math.abs(temperature);
temperature_weight = (x - Math.floor(x)) / AMPLIFIER

if(temperature < 0){
temperature_blending -= 0.5
}else{
temperature_blending += 0.5
}
}

//here I repeat all the process but for humidity
if(b < AMPLIFIER){
let x = Math.abs(humidity);
humidity_weight = (x - Math.floor(x)) / AMPLIFIER

if(humidity > 0){
humidity_blending -= 0.5
}else{
humidity_blending += 0.5
}
}else if(b > AMPLIFIER1){
let x = -Math.abs(humidity);
humidity_weight = (x - Math.floor(x)) / AMPLIFIER

if(humidity < 0){
humidity_blending -= 0.5
}else{
humidity_blending += 0.5
}
}

//Once we get here, We have the original vertex biome and the closest biome to it (if it is in the blending zone: very close to another biome implying that it has to start blending with it)
let BIOME1 = this.Biomes.determineBiome(temperature_blending,humidity_blending,new THREE.Vector2(x, z),i);

//I am very lost from here, I also think that the way to calculate the weights is incorrect.

//this calculate the average between both climatic factors
let average = (humidity_weight+temperature_weight)/2

//Here I try blending the colors between them using the average
colors.setXYZ( i, lerp(BIOME.color.r,BIOME1.color.r,average), lerp(BIOME.color.g,BIOME1.color.g,average), lerp(BIOME.color.b,BIOME1.color.b,average));
}


The result which doesn't look well:

So if you know how I can correct it, or if an algorithm already exists please tell me. Thanks a lot!

New contributor
alon is a new contributor to this site. Take care in asking for clarification, commenting, and answering. Check out our Code of Conduct.

So you basically have a function that takes a coordinate, runs it through some noise, and outputs a biome, if I understand correctly? Biome blending is trickier than it might seem at surface value, but there's an approach I've been using that works well

• Scatter a bunch of points around your world, similar to how you would in Voronoi noise.
• You can also use blue noise point distributions, e.g. Poisson if your world is finite and small.
• Evaluate the biome at each of those points.
• Figure out a circle size that will always contain points, use a falloff function to assign weights to nearby biomes, and perform a weighted average.

I wrote about it in more detail here: https://noiseposti.ng/posts/2021-03-13-Fast-Biome-Blending-Without-Squareness.html

Also I wouldn't suggest using actual Perlin for noise, at least not without something in place to fix its issues. A lot of sources talk about it, but it has strong cardinal axis bias. Noise should remind us of nature, not of the coordinate-space math under the hood. Better as a general rule to use Simplex or Simplex-type noise, or specifically 3D Perlin which has been rotated so that its cardinal planes are out of alignment. This library supports both: https://www.npmjs.com/package/fastnoise-lite/v/0.0.1

• This approach looks very good, I think it's just what I need. But I don`t understand what you mean in the las step with "certain points".
– alon
2 days ago