Procedural Generation Tutorial Brick Noise Texture - Part 3
Before you start you should know these things:
- Create Texture variables
- Manipulate textures using SetPixels
You should have gone through and completed all the ‘Checker Board Texture’ and 'Brick Pattern Texture' tutorials before going any further. If you find yourself not understanding some of the terminology or code of the tutorial I would also recommend going through the previous tutorial to get up to speed. I will leave a link to it below to go through at your leisure.
http://joseph-easter.blogspot.com/2017/01/procedural-generation-tutorial-brick_95.html
If you follow this tutorial and find I am moving too fast or if you don’t know the things in the list above, I would recommend getting up to speed and then come back to this tutorial when you are ready.
With this tutorial if you want to convert this into JavaScript by all means do so but it may be easier for you to follow this in C# and stick with the language of the tutorial to make it easier.
PROJECT RESOURCES: At the bottom of the post there is a download link to a zip file. This files includes all files needed to follow Part 3 and the completed project file of Part 3.
In this tutorial series, we will be adding to the brick pattern texture using code by adding noise to the texture image.
In this tutorial series, we will be adding to the brick pattern texture using code by adding noise to the texture image.
We will:
- Explain the theory
- Create a static class
- Learn how to make a hash table function
- How to create our own 1D noise using the quads 'x' position
Step 1: The theory
In this part we will cover how to make a 1D value noise pattern, dependant on the object’s location in world space and independent of it’s resolution.
Step 2: Establishing the NoiseLibrary Class
Code:
using UnityEngine;
public static class NoiseLibrary()
public static class NoiseLibrary()
{
}
In this class we need a function that, when we parse it a point in world space, will give us (or return) what looks like a random value based on a calculation and algorithm. For the time being we will make it return the game object’s position on the ‘x’ axis. You can return ‘y’ or ‘z’ if you like, I just chose ‘x’ for simplicity and its the first axis on the list.
Code:
using UnityEngine;
public static class NoiseLibrary()
{
public static float RandomValue (Vector3
point)
{
return point.x;
}
point)
{
return point.x;
}
}
Code:
void CreateNoisePattern()
{
float normalizer = 1f / resolutionW;
WorldCoordinates();
for (int w = 0; w < resolutionW; w++)
{
LeftAndRightSides(pointLL, pointLR,
pointUL, pointUR,
w, normalizer);
for (int h = 0; h < resolutionH; h++)
{
CalculatePoint(leftSide, rightSide,
h, normalizer);
noiseTexture.SetPixel(w, h,
Color.grey *
NoiseLibrary.RandomValue(
point));
}
}
noiseTexture.Apply();
WorldCoordinates();
for (int w = 0; w < resolutionW; w++)
{
LeftAndRightSides(pointLL, pointLR,
pointUL, pointUR,
w, normalizer);
for (int h = 0; h < resolutionH; h++)
{
CalculatePoint(leftSide, rightSide,
h, normalizer);
noiseTexture.SetPixel(w, h,
Color.grey *
NoiseLibrary.RandomValue(
point));
}
}
noiseTexture.Apply();
}
At the moment the result isn't particularly pretty or useful to us.
Step 3: Creating a pattern with alternating stripes
With creating a noise pattern, we will start very simple, then add the complexity later as we go on. We will start by creating a pattern of alternating black and white vertical stripes.
Code:
using UnityEngine;
public static class NoiseLibrary()
{
public static float RandomValue (Vector3
point)
{
int intX = (int)point.x;
return intX % 2;
}
point)
{
int intX = (int)point.x;
return intX % 2;
}
}
Code:
using UnityEngine;
public static class NoiseLibrary()
{
public static float RandomValue (Vector3
point, float patternAlternationSpeed)
{
point *= patternAlternationSpeed;
int intX = (int)point.x;
return intX % 2;
}
point, float patternAlternationSpeed)
{
point *= patternAlternationSpeed;
int intX = (int)point.x;
return intX % 2;
}
}
Code:
public float patternAlternationSpeed = 1f;
...
void CreateNoisePattern()
{
float normalizer = 1f / resolutionW;
WorldCoordinates();
for (int w = 0; w < resolutionW; w++)
{
LeftAndRightSides(pointLL, pointLR,
pointUL, pointUR,
w, normalizer);
for (int h = 0; h < resolutionH; h++)
{
CalculatePoint(leftSide, rightSide,
h, normalizer);
noiseTexture.SetPixel(w, h,
Color.grey *
NoiseLibrary.RandomValue(
point, patternAlternationSpeed));
}
}
noiseTexture.Apply();
WorldCoordinates();
for (int w = 0; w < resolutionW; w++)
{
LeftAndRightSides(pointLL, pointLR,
pointUL, pointUR,
w, normalizer);
for (int h = 0; h < resolutionH; h++)
{
CalculatePoint(leftSide, rightSide,
h, normalizer);
noiseTexture.SetPixel(w, h,
Color.grey *
NoiseLibrary.RandomValue(
point, patternAlternationSpeed));
}
}
noiseTexture.Apply();
}
Now, working on the assumption the quad is in the same place, we can guess there are about 2 stripes, at al ‘patternAlternationSpeed’ of five, with one white and one black. However, they are only being shown on the right hand side. This is because the right hand side is seen as the positive side (value wise), so either the values on the left are returning zero values or negative ones being clamped to zero. This is because the modulus operator is working its magic and the remainder is either zero or one.
Code:
using UnityEngine;
public static class NoiseLibrary()
{
public static float RandomValue (Vector3
point, float patternAlternationSpeed)
{
point *= patternAlternationSpeed;
int intX = (int)point.x;
return intX & 2;
}
point, float patternAlternationSpeed)
{
point *= patternAlternationSpeed;
int intX = (int)point.x;
return intX & 2;
}
}
Code:
using UnityEngine;
public static class NoiseLibrary()
{
public static float RandomValue (Vector3
point, float patternAlternationSpeed)
{
point *= patternAlternationSpeed;
int intX = Mathf.FloorToInt(point.x);
return intX & 2;
}
point, float patternAlternationSpeed)
{
point *= patternAlternationSpeed;
int intX = Mathf.FloorToInt(point.x);
return intX & 2;
}
}
We now have a regular stripe pattern that does not mirror itself.
Step 4: Lattice Noise
Code:
using UnityEngine;
public static class NoiseLibrary()
{
private static int [] hashValue =
{
0, 1, 2, 3
};
public static float RandomValue (Vector3
point, float patternAlternationSpeed)
{
point *= patternAlternationSpeed;
int intX = Mathf.FloorToInt(point.x);
return intX & 2;
}
{
0, 1, 2, 3
};
public static float RandomValue (Vector3
point, float patternAlternationSpeed)
{
point *= patternAlternationSpeed;
int intX = Mathf.FloorToInt(point.x);
return intX & 2;
}
}
Code:
using UnityEngine;
public static class NoiseLibrary()
{
private static int [] hashValue =
{
0, 1, 2, 3
};
public static float RandomValue (Vector3
point, float patternAlternationSpeed)
{
point *= patternAlternationSpeed;
int intX = Mathf.FloorToInt(point.x);
return hashValue[intX] / 3f;
}
{
0, 1, 2, 3
};
public static float RandomValue (Vector3
point, float patternAlternationSpeed)
{
point *= patternAlternationSpeed;
int intX = Mathf.FloorToInt(point.x);
return hashValue[intX] / 3f;
}
}
Code:
using UnityEngine;
public static class NoiseLibrary()
{
private static int [] hashValue =
{
0, 1, 2, 3
};
public static float RandomValue (Vector3
point, float patternAlternationSpeed)
{
point *= patternAlternationSpeed;
int intX = Mathf.FloorToInt(point.x);
intX &= 3f;
return hashValue[intX] / 3f;
}
{
0, 1, 2, 3
};
public static float RandomValue (Vector3
point, float patternAlternationSpeed)
{
point *= patternAlternationSpeed;
int intX = Mathf.FloorToInt(point.x);
intX &= 3f;
return hashValue[intX] / 3f;
}
}
Step 5: Mixing up the permutation table
Code:
using UnityEngine;
public static class NoiseLibrary()
{
private static int [] hashValue =
{
2, 1, 3, 0
};
public static float RandomValue (Vector3
point, float patternAlternationSpeed)
{
point *= patternAlternationSpeed;
int intX = Mathf.FloorToInt(point.x);
intX &= 3f;
return hashValue[intX] / 3f;
}
{
2, 1, 3, 0
};
public static float RandomValue (Vector3
point, float patternAlternationSpeed)
{
point *= patternAlternationSpeed;
int intX = Mathf.FloorToInt(point.x);
intX &= 3f;
return hashValue[intX] / 3f;
}
}
Pattern alternation speed is 64.
The pattern looks less regular but it’s still obvious. This is down to how few integers we have in the permutation table (or permutation array). We could easily increase the number of integers to get a more varied result.
Code:
using UnityEngine;
public static class NoiseLibrary()
{
private static int [] hashValue =
{
2, 1, 3, 0, 5, 6, 4, 7
};
public static float RandomValue (Vector3
point, float patternAlternationSpeed)
{
point *= patternAlternationSpeed;
int intX = Mathf.FloorToInt(point.x);
intX &= 7f;
return hashValue[intX] / 7f;
}
{
2, 1, 3, 0, 5, 6, 4, 7
};
public static float RandomValue (Vector3
point, float patternAlternationSpeed)
{
point *= patternAlternationSpeed;
int intX = Mathf.FloorToInt(point.x);
intX &= 7f;
return hashValue[intX] / 7f;
}
}
Resolution 512 by 512, pattern alternation speed of 64.
Code:
using UnityEngine;
public static class NoiseLibrary()
{
private static int [] hashValue =
{
8, 1, 3, 4, 14, 7, 5, 12, 11, 2, 10,
13, 0, 9, 6
};
public static float RandomValue (Vector3
point, float patternAlternationSpeed)
{
point *= patternAlternationSpeed;
int intX = Mathf.FloorToInt(point.x);
intX &= 15f;
return hashValue[intX] / 15f;
}
{
8, 1, 3, 4, 14, 7, 5, 12, 11, 2, 10,
13, 0, 9, 6
};
public static float RandomValue (Vector3
point, float patternAlternationSpeed)
{
point *= patternAlternationSpeed;
int intX = Mathf.FloorToInt(point.x);
intX &= 15f;
return hashValue[intX] / 15f;
}
}
Resolution 256 by 256, pattern alternation speed of 128.
This hash table will still produce a repeating pattern depending on how many time it’s repeated, but it is a lot less obvious then it was when we started. Also we only notice when we see a big enough sample when it’s repeated a whole lot.
Now if we combine our texture with this noise one we can see the result. Enable the ‘CheckerBoardPatternTexture’ script, and in ‘Noise’ comment out the ‘Start’ method, then in ‘SetNoiseTextureSize’ comment out the line where we assign the ‘noiseTexture’ as our main texture.
Resolution 256 by 256, pattern alternation speed of 64.
Great we know how to make our own noise using a static noise class and how to create a hash table with seemly random values.
- Create a static class
- Reference a function from said class
- Create a simple regular pattern
- Use lattice noise
- Create 1D noise Use a hash table/ hash array
- Control the frequency at which the noise pattern repeats itself
- What ‘Mathf.FloorToInt’ does and how to use it
- Use an objects ‘x’ coordinate to generate a value from a hash table
- Use a bitwise operator ‘&’ to check is a value is odd or even
- Make an array indexer wrap back on itself if it exceeds hash table length
- Use modulus operator to find a remainder and how it’s efficient
- Normalize hash table indexer after
- Bitwise operator using ‘/’