# How to add noise into a texture?

I'm working on a Heat Map generated from Perlin Noise. So far I've got 2 different outputs. Let's go step by step:

The result should be something like this: This is the Perlin Noise I've got, easy to get, RED means hot and BLUE means cold, it's already generated in a spherical way.

And this is the gradient TEXTURE, it's based on Y axis as you can see.

At the moment they are generated in different functions in Unity, public Texture2D PerlinHeat() and public Texture2D GradientHeat(). I'd like to multiply them but you can't just simply do something like Texture2D * Texture2D, so I´m trying to figure it out and any help would be appreciated.

I'm using LibNoise as reference but as my gradient is not a noise module I can't multiply them. By the way: How to make a noise gradient? That´s the page that approach the best to my issue, as is about the same, I just don´t know how to implement everything that is said there.

Here is the code of the Gradient Texture, is pretty messy but it allows to change dynamically colors and heat ranges over the texture with just a change in the inspector.

    public Texture2D GetGradientHeat()
{
var texture = new Texture2D(heatMap.Width, heatMap.Height);
var pixels = new Color[heatMap.Width * heatMap.Height];
for (var x = 0; x < heatMap.Width; x++)
{
for (var y = 0; y < heatMap.Height; y++)
{
if (y <= southPoleCap || y >= northPoleCap)
{
pixels[x + y * heatMap.Width] = coldest;
}
else if (y > southPoleCap && y <= southArcticCap || y < northPoleCap && y >= northArcticCap)
{
pixels[x + y * heatMap.Width] = colder;
}
else if (y > southArcticCap && y <= southArcticOuter || y < northArcticCap && y >= northArcticOuter)
{
pixels[x + y * heatMap.Width] = cold;
}
else if (y > southArcticOuter && y <= southMiddleOuter || y < northArcticOuter && y >= northMiddleOuter)
{
pixels[x + y * heatMap.Width] = template;
}
else if (y > southMiddleOuter && y <= southTropic || y < northMiddleOuter && y >= northTropic)
{
pixels[x + y * heatMap.Width] = hot;
}
else if (y > southTropic && y <= southMiddleInner || y >= northMiddleInner && y < northTropic)
{
pixels[x + y * heatMap.Width] = hoter;
}
else if (y > southMiddleInner && y < northMiddleInner)
{
pixels[x + y * heatMap.Width] = hotest;
}
}
}

texture.SetPixels(pixels);
texture.wrapMode = TextureWrapMode.Clamp;
texture.Apply();
return texture;
}

Any help with this will be appreciated as I've been trying to do it during weeks and I´m not able to find any more documentation or a pseudocode that I can make work.

EDIT 2 So answer from DM Gregory just works like a charm, awesome. The resulting output is at follows, with that you can alter that noise strength and playing with the offsets, color multipliers, or other noise modules, you get such a nice output, thanks!

• Welcome to GDSE. The term for starting with image A (your banded gradient) & using image B (probably some perlin noise or something similar) to produce a new, distorted version of A is called 'domain warping'. If that's what you're after, we already have a good Q&A addressing how to do that. – Pikalek Aug 13 at 16:02
• Yep, that´s what I´m trying to implement but I don´t really know how, and I´d prefer to avoid shaders, but I´ll give it a try, thanks for the link! – Mateo Pérez Murillo Aug 13 at 16:04
• The answer to the question I linked isn't shader specific. – Pikalek Aug 13 at 16:07
• Possible duplicate of How to distort 2d perlin noise? – Pikalek Aug 13 at 16:08
• I just added and Edit, the first link looks useful, however I think I´m gonna need some help with that :S – Mateo Pérez Murillo Aug 13 at 16:20

First, change the workhorses of your texture generation loops into its own standalone generator functions. These take in a set of parameters - like which pixel you want to generate a result for - and return the generated result according to some rule.

Color GetGradientColor(int x, int y) {
// TODO: You can probably clean this up with Mathf.Abs() or a lookup table.
if (y <= southPoleCap || y >= northPoleCap)
return coldest;
else if (y > southPoleCap && y <= southArcticCap || y < northPoleCap && y >= northArcticCap)
return colder;
else if (y > southArcticCap && y <= southArcticOuter || y < northArcticCap && y >= northArcticOuter)
return cold;
else if (y > southArcticOuter && y <= southMiddleOuter || y < northArcticOuter && y >= northMiddleOuter)
return template;
else if (y > southMiddleOuter && y <= southTropic || y < northMiddleOuter && y >= northTropic)
return hot;
else if (y > southTropic && y <= southMiddleInner || y >= northMiddleInner && y < northTropic)
return hotter;
else
return hottest;
}


Now you can either invoke the generator directly, to get the same result you had before:

public Texture2D GetGradientHeat()
{
var texture = new Texture2D(heatMap.Width, heatMap.Height);
var pixels = new Color[heatMap.Width * heatMap.Height];
for (var x = 0; x < heatMap.Width; x++)
{
for (var y = 0; y < heatMap.Height; y++)
{
pixels[x + y * heatMap.Width] = GetGradientColor(x, y);
}
}
// ...


Or you can compose the results of your generators, to make one texture that's the composition of multiple rules feeding into each other:

public Texture2D GetDistortedGradient(float noiseStrength)
{
var texture = new Texture2D(heatMap.Width, heatMap.Height);
var pixels = new Color[heatMap.Width * heatMap.Height];
for (var x = 0; x < heatMap.Width; x++)
{
for (var y = 0; y < heatMap.Height; y++)
{
float noise = GetNoise(x, y);
int shift = Mathf.RoundToInt(noise * noiseStrength);
pixels[x + y * heatMap.Width] = GetGradientColor(x, y + shift);
}
}
// ...

• Oh wow, this looks nice, I´m gonna try to get it, so far looks okay thanks DMGregory! About the lookup table and cleanup is something I will do, definitely, that´s just a fast made version to get it work. I just have a question about all this, the float noise = GetNoise(x, y), this should be the generated noise map, I suppose xD just wanna make sure – Mateo Pérez Murillo Aug 13 at 16:40
• You can either sample from the map, or make GetNoise itself a generator function that computes the noise value using its rules from the input position on demand. – DMGregory Aug 13 at 16:46
• OH DMGregory, trust me, you just saved my day, it works like a charm, so ended up using the samples I get from the map, from that point I´m gonna play around to see different stuff, I DO APPRECIATE what you just did. Love you mate (btw I just realized you are using the same function as the domain warping but applied to C#, love that too) – Mateo Pérez Murillo Aug 13 at 16:54
• How can I make yours as the true answer or give you stars or something? You really deserve them – Mateo Pérez Murillo Aug 13 at 16:55
• Looks like you figured it out — the checkmark highlights an answer as "accepted" (ie. "This worked for me") and gives the author +15 reputation (though seriously, I've got enough, so feel free to spread the rep around to other users to encourage more solutions & perspectives) – DMGregory Aug 13 at 17:09